Browse Source

Add UI for multiple apikeys to config/General/Web Interface.

Add jquery-qrcode 0.17.0
pull/1200/head
JackDandy 6 years ago
parent
commit
ac5acedbe1
  1. 20
      CHANGES.md
  2. 1
      HACKS.txt
  3. 2
      gui/slick/css/dark.css
  4. 2
      gui/slick/css/light.css
  5. 13
      gui/slick/css/style.css
  6. 22
      gui/slick/interfaces/default/config_general.tmpl
  7. 96
      gui/slick/js/config.js
  8. 2
      sickbeard/__init__.py
  9. 67
      sickbeard/webserve.py

20
CHANGES.md

@ -1,5 +1,25 @@
### 0.21.0 (2019-xx-xx xx:xx:xx UTC)
* Add ability to use multiple SG apikeys
* Add UI for multiple apikeys to config/General/Web Interface
* Add jquery-qrcode 0.17.0
* Change add apikey name to ERROR log messages
* Change add logging of errors from api
* Change add remote ip to error message
* Change add print command name for api in debug log
* Change add warning message to log if old Sick-Beard api call is used
* Change add an api call mapping helper for name changed functions (for printed warnings)
* Change ui typo in apiBuilder
* Fix display of fanart in apibuilder
* Add help command to apiBuilder and fix help call
* Fix add shows via api
* Change fix sg.searchqueue output
* Add missing sg.show.delete parameter "full"
* Add missing sg.setdefaults and sg.shutdown methods
* Change increase api version because missing sg.* methods are added
* Change add some extra checks for Sick-Beard call add (existing) show
* Change patch imdbpie to add cachedir folder and set imdbpie cachedir in SG
* Fix force search return values
* Update attr 19.2.0.dev0 (154b4e5) to 19.2.0.dev0 (daf2bc8)
* Update Beautiful Soup 4.7.1 (r497) to 4.8.0 (r526)
* Update bencode to 2.1.0 (e8290df)

1
HACKS.txt

@ -9,6 +9,7 @@ Libs with customisations...
/lib/hachoir_metadata/riff.py
/lib/hachoir_parser/guess.py
/lib/hachoir_parser/misc/torrent.py
/lib/imdbpie
/lib/lockfile/mkdirlockfile.py
/lib/rtorrent
/lib/scandir/scandir.py

2
gui/slick/css/dark.css

@ -651,10 +651,12 @@ config*.tmpl
border-bottom:1px dotted #666
}
.qr-btn .glyphicon-qrcode,
.component-group-desc p{
color:#ddd
}
.dotted-surround,
.test-notification{
border:1px dotted #ccc
}

2
gui/slick/css/light.css

@ -641,10 +641,12 @@ config*.tmpl
border-bottom:1px dotted #666
}
.qr-btn .glyphicon-qrcode,
.component-group-desc p{
color:#666
}
.dotted-surround,
.test-notification{
border:1px dotted #ccc
}

13
gui/slick/css/style.css

@ -3029,6 +3029,7 @@ select .selected:before{
margin:0 !important
}
.dotted-surround,
.test-notification{
padding:5px;
margin-bottom:10px;
@ -3151,6 +3152,18 @@ select .selected:before{
background-position:-104px 0
}
#api-keys > div{display:inline-block}
#api-keys span{float:left}
#api-keys span, #generate-result{line-height:22px}
#api-keys .api-key{width:235px}
#api-keys .app-name{width:135px}
.qr-btn{margin-right:6px}
.qr-btn .glyphicon-qrcode{cursor:pointer;font-size:15px}
.apikey-qr-dlg .qr-title{padding:0 25px 5px 25px;text-align:right}
.apikey-qr-dlg .qr-title em{color:#999;font-weight:bolder}
.apikey-qr-dlg .qr-title span{color:#333}
.apikey-qr-dlg .qr-body{padding:25px 25px 0}
/* =======================================================================
config_postProcessing.tmpl
========================================================================== */

22
gui/slick/interfaces/default/config_general.tmpl

@ -496,7 +496,7 @@
<span class="component-title">API enable</span>
<span class="component-desc">
<input type="checkbox" name="use_api" class="enabler" id="use_api"#echo ('', $checked)[$sg_var('USE_API')]#>
<p>permit the use of the SickGear (and Legacy SickBeard) API</p>
<p>permit the use of the SickGear (and legacy SickBeard) API</p>
</span>
</label>
</div>
@ -505,10 +505,22 @@
<label for="api_key">
<span class="component-title">API key</span>
<span class="component-desc">
<p>The legacy SickBeard API is limited to shows from thetvdb.com.<br>Use the SickGear API endpoint for full access</p>
<input type="text" name="api_key" id="api_key" value="$sg_str('API_KEYS')" class="form-control input-sm input300" readonly="readonly">
<input class="btn btn-inline" type="button" id="generate_new_apikey" value="Generate">
<div class="clear-left"><p>used to give 3rd party programs limited access to SickGear</p></div>
<input id="app-name" type="text" placeholder="enter app name" class="form-control input-sm input150">
<input id="generate-api-key" value="Generate" type="button" class="btn btn-inline">
<span id="generate-result">&nbsp;</span>
<p class="clear-left">apps using SickGear API calls gain full access, legacy SickBeard endpoints are limited to thetvdb.com shows</p>
<div id="api-keys" class="clear-left">
<div class="new-key highlight-text" style="display:none"><span class="qr-btn"><a rel="" name="qr" title="API key QR code"><span class="glyphicon glyphicon-qrcode"></span></a></span><span class="api-key"></span><span class="app-name"></span><input value="Revoke" type="button" class="revoke btn btn-inline"></div>
#set $tip_addkeys = ''
#for $appname, $uid in $api_keys
#if not ($appname and $uid)
#continue
#end if
<div class="grey-text"><span class="qr-btn"><a rel="qr" name="qr" title="API key QR code"><span class="glyphicon glyphicon-qrcode"></span></a></span><span class="api-key">$uid</span><span class="app-name">$appname</span><input value="Revoke" type="button" class="revoke btn btn-inline"></div>
#set $tip_addkeys = ' style="display:none"'
#end for
<div id="tip-addkeys"$tip_addkeys>Keys used by 3rd party programs to access SickGear will list here when generated</div>
</div>
</span>
</label>
</div>

96
gui/slick/js/config.js

File diff suppressed because one or more lines are too long

2
sickbeard/__init__.py

@ -818,7 +818,7 @@ def init_stage_1(console_logging):
if not API_KEYS:
tmp_api_key = check_setting_str(CFG, 'General', 'api_key', None)
if None is not tmp_api_key:
API_KEYS = [['app-name', tmp_api_key]]
API_KEYS = [['app name (old key)', tmp_api_key]]
DEBUG = bool(check_setting_int(CFG, 'General', 'debug', 0))

67
sickbeard/webserve.py

@ -5854,6 +5854,8 @@ class ConfigGeneral(Config):
t.indexers = dict([(i, sickbeard.indexerApi().indexers[i]) for i in sickbeard.indexerApi().indexers
if sickbeard.indexerApi(i).config['active']])
t.request_host = escape.xhtml_escape(self.request.host_name)
api_keys = '|||'.join([':::'.join(a) for a in sickbeard.API_KEYS])
t.api_keys = api_keys and sickbeard.API_KEYS or []
return t.respond()
def saveRootDirs(self, rootDirString=None):
@ -5912,44 +5914,60 @@ class ConfigGeneral(Config):
m.update(r)
# Return a hex digest of the md5, eg 49f68a5c8493ec2c0bf489821c21fc3b
logger.log(u'New API generated')
app_name = kwargs.get('app_name')
app_name = '' if not app_name else ' for [%s]' % app_name
logger.log(u'New apikey generated%s' % app_name)
return m.hexdigest()
@staticmethod
def api_ui_func(name, key, delete=False):
# type: (AnyStr, AnyStr, bool) -> AnyStr
if not name:
return 'No Name given'
if 32 != len(key):
return 'key not valid'
if delete:
if key in [k[1] for k in sickbeard.API_KEYS]:
sickbeard.API_KEYS = [ak for ak in sickbeard.API_KEYS if key != ak[1]]
logger.log('APIKEY: [%s] removed' % name, logger.DEBUG)
return 'KEY removed'
else:
return 'KEY doesn\'t exists'
def create_apikey(self, app_name):
result = dict()
if not app_name:
result['result'] = 'Failed: no name given'
elif app_name in [k[0] for k in sickbeard.API_KEYS if k[0]]:
result['result'] = 'Failed: name is not unique'
else:
if key in [k[1] for k in sickbeard.API_KEYS]:
return 'APIKEY already exists'
elif name in [k[0] for k in sickbeard.API_KEYS]:
return 'Key name is not unique'
api_key = self.generateKey(app_name=app_name)
if api_key in [k[1] for k in sickbeard.API_KEYS if k[0]]:
result['result'] = 'Failed: apikey already exists, try again'
else:
sickbeard.API_KEYS.append([name, key])
logger.log('APIKEY: [%s] added' % name, logger.DEBUG)
return 'APIKEY added'
sickbeard.API_KEYS.append([app_name, api_key])
logger.log('Created apikey for [%s]' % app_name, logger.DEBUG)
result.update(dict(result='Success: apikey added', added=api_key))
sickbeard.USE_API = 1
sickbeard.save_config()
ui.notifications.message('Configuration Saved', ek.ek(os.path.join, sickbeard.CONFIG_FILE))
return json.dumps(result)
@staticmethod
def revoke_apikey(app_name, api_key):
result = dict()
if not app_name:
result['result'] = 'Failed: no name given'
elif not api_key or 32 != len(re.sub('(?i)[^0-9a-f]', '', api_key)):
result['result'] = 'Failed: key not valid'
elif api_key not in [k[1] for k in sickbeard.API_KEYS if k[0]]:
result['result'] = 'Failed: key doesn\'t exist'
else:
sickbeard.API_KEYS = [ak for ak in sickbeard.API_KEYS if ak[0] and api_key != ak[1]]
logger.log('Revoked [%s] apikey [%s]' % (app_name, api_key), logger.DEBUG)
result.update(dict(result='Success: apikey removed', removed=True))
sickbeard.save_config()
ui.notifications.message('Configuration Saved', ek.ek(os.path.join, sickbeard.CONFIG_FILE))
return json.dumps(result)
def saveGeneral(self, log_dir=None, web_port=None, web_log=None, encryption_version=None, web_ipv6=None, web_ipv64=None,
update_shows_on_start=None, show_update_hour=None,
trash_remove_show=None, trash_rotate_logs=None, update_frequency=None, launch_browser=None, web_username=None,
use_api=None, api_keys=None, indexer_default=None, timezone_display=None, cpu_preset=None, file_logging_preset=None,
use_api=None, indexer_default=None, timezone_display=None, cpu_preset=None, file_logging_preset=None,
web_password=None, version_notify=None, enable_https=None, https_cert=None, https_key=None,
handle_reverse_proxy=None, send_security_headers=None, allowed_hosts=None,
home_search_focus=None, display_freespace=None, sort_article=None, auto_update=None, notify_on_update=None,
proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=None,
fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None,
indexer_timeout=None, rootDir=None, show_dirs_with_dots=None, theme_name=None, default_home=None,
use_imdb_info=None, fanart_limit=None, show_tags=None, showlist_tagview=None):
use_imdb_info=None, fanart_limit=None, show_tags=None, showlist_tagview=None, **kwargs):
results = []
@ -6025,7 +6043,6 @@ class ConfigGeneral(Config):
sickbeard.CALENDAR_UNPROTECTED = config.checkbox_to_value(calendar_unprotected)
sickbeard.USE_API = config.checkbox_to_value(use_api)
sickbeard.API_KEYS = [a.split(':::') for a in api_keys.split('|')]
sickbeard.WEB_PORT = config.to_int(web_port)
# sickbeard.WEB_LOG is set in config.change_log_dir()

Loading…
Cancel
Save