summaryrefslogtreecommitdiffstats
path: root/module/Api.py
diff options
context:
space:
mode:
authorGravatar RaNaN <Mast3rRaNaN@hotmail.de> 2011-09-27 16:24:03 +0200
committerGravatar RaNaN <Mast3rRaNaN@hotmail.de> 2011-09-27 16:24:03 +0200
commit5f8a4d25ea9034cadc8ae19a2ffab788f62cc56c (patch)
tree9d947797aafb3e9d97dbf10313c5f48f6f3d6198 /module/Api.py
parentMerge (diff)
downloadpyload-5f8a4d25ea9034cadc8ae19a2ffab788f62cc56c.tar.xz
reworked authorization, now works on api level
Diffstat (limited to 'module/Api.py')
-rw-r--r--module/Api.py136
1 files changed, 124 insertions, 12 deletions
diff --git a/module/Api.py b/module/Api.py
index 37336e568..f4a252ac9 100644
--- a/module/Api.py
+++ b/module/Api.py
@@ -26,14 +26,47 @@ from remote.thriftbackend.thriftgen.pyload.ttypes import *
from remote.thriftbackend.thriftgen.pyload.Pyload import Iface
from PyFile import PyFile
-from database.UserDatabase import ROLE
from utils import freeSpace, compare_time
from common.packagetools import parseNames
from network.RequestFactory import getURL
+# contains function names mapped to their permissions
+# unlisted functions are for admins only
+permMap = {}
+
+# decorator only called on init, never initialized, so has no effect on runtime
+def permission(bits):
+ class _Dec(object):
+ def __new__(cls, func, *args, **kwargs):
+ permMap[func.__name__] = bits
+ return func
+
+ return _Dec
+
+
urlmatcher = re.compile(r"((https?|ftps?|xdcc|sftp):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+-=\\\.&]*)", re.IGNORECASE)
+class PERMS:
+ ALL = 0 # requires no permission, but login
+ ADD = 1 # can add packages
+ DELETE = 2 # can delete packages
+ STATUS = 4 # see and change server status
+ LIST = 16 # see queue and collector
+ MODIFY = 32 # moddify some attribute of downloads
+ DOWNLOAD = 64 # can download from webinterface
+ SETTINGS = 128 # can access settings
+ ACCOUNTS = 256 # can access accounts
+
+class ROLE:
+ ADMIN = 0 #admin has all permissions implicit
+ USER = 1
+
+def has_permission(userperms, perms):
+ # bytewise or perms before if needed
+ return perms == (userperms & perms)
+
+
class Api(Iface):
"""
**pyLoads API**
@@ -75,6 +108,7 @@ class Api(Iface):
section.outline = sub["outline"]
return sections
+ @permission(PERMS.SETTINGS)
def getConfigValue(self, category, option, section="core"):
"""Retrieve config value.
@@ -90,6 +124,7 @@ class Api(Iface):
return str(value) if not isinstance(value, basestring) else value
+ @permission(PERMS.SETTINGS)
def setConfigValue(self, category, option, value, section="core"):
"""Set new config value.
@@ -109,6 +144,7 @@ class Api(Iface):
elif section == "plugin":
self.core.config.setPlugin(category, option, value)
+ @permission(PERMS.SETTINGS)
def getConfig(self):
"""Retrieves complete config of core.
@@ -123,6 +159,7 @@ class Api(Iface):
"""
return self.core.config.config
+ @permission(PERMS.SETTINGS)
def getPluginConfig(self):
"""Retrieves complete config for all plugins.
@@ -138,14 +175,17 @@ class Api(Iface):
return self.core.config.plugin
+ @permission(PERMS.STATUS)
def pauseServer(self):
"""Pause server: Tt wont start any new downloads, but nothing gets aborted."""
self.core.threadManager.pause = True
+ @permission(PERMS.STATUS)
def unpauseServer(self):
"""Unpause server: New Downloads will be started."""
self.core.threadManager.pause = False
+ @permission(PERMS.STATUS)
def togglePause(self):
"""Toggle pause state.
@@ -154,6 +194,7 @@ class Api(Iface):
self.core.threadManager.pause ^= True
return self.core.threadManager.pause
+ @permission(PERMS.STATUS)
def toggleReconnect(self):
"""Toggle reconnect activation.
@@ -162,6 +203,7 @@ class Api(Iface):
self.core.config["reconnect"]["activated"] ^= True
return self.core.config["reconnect"]["activated"]
+ @permission(PERMS.LIST)
def statusServer(self):
"""Some general information about the current status of pyLoad.
@@ -177,10 +219,12 @@ class Api(Iface):
return serverStatus
+ @permission(PERMS.STATUS)
def freeSpace(self):
"""Available free space at download directory in bytes"""
return freeSpace(self.core.config["general"]["download_folder"])
+ @permission(PERMS.ALL)
def getServerVersion(self):
"""pyLoad Core version """
return self.core.version
@@ -194,6 +238,7 @@ class Api(Iface):
pass
#self.core.do_restart = True
+ @permission(PERMS.STATUS)
def getLog(self, offset=0):
"""Returns most recent log entries.
@@ -211,6 +256,7 @@ class Api(Iface):
except:
return ['No log available']
+ @permission(PERMS.STATUS)
def isTimeDownload(self):
"""Checks if pyload will start new downloads according to time in config.
@@ -220,6 +266,7 @@ class Api(Iface):
end = self.core.config['downloadTime']['end'].split(":")
return compare_time(start, end)
+ @permission(PERMS.STATUS)
def isTimeReconnect(self):
"""Checks if pyload will try to make a reconnect
@@ -229,6 +276,7 @@ class Api(Iface):
end = self.core.config['reconnect']['endTime'].split(":")
return compare_time(start, end) and self.core.config["reconnect"]["activated"]
+ @permission(PERMS.LIST)
def statusDownloads(self):
""" Status off all currently running downloads.
@@ -248,6 +296,7 @@ class Api(Iface):
return data
+ @permission(PERMS.ADD)
def addPackage(self, name, links, dest=Destination.Queue):
"""Adds a package, with links to desired destination.
@@ -273,6 +322,7 @@ class Api(Iface):
return pid
+ @permission(PERMS.ADD)
def parseURLs(self, html=None, url=None):
"""Parses html content or any arbitaty text for links and returns result of `checkURLs`
@@ -291,6 +341,7 @@ class Api(Iface):
return self.checkURLs(urls)
+ @permission(PERMS.ADD)
def checkURLs(self, urls):
""" Gets urls and returns pluginname mapped to list of matches urls.
@@ -308,6 +359,7 @@ class Api(Iface):
return plugins
+ @permission(PERMS.ADD)
def checkOnlineStatus(self, urls):
""" initiates online status check
@@ -329,6 +381,7 @@ class Api(Iface):
return OnlineCheck(rid, result)
+ @permission(PERMS.ADD)
def checkOnlineStatusContainer(self, urls, container, data):
""" checks online status of urls and a submited container file
@@ -343,6 +396,7 @@ class Api(Iface):
return self.checkOnlineStatus(urls + [th.name])
+ @permission(PERMS.ADD)
def pollResults(self, rid):
""" Polls the result available for ResultID
@@ -358,6 +412,7 @@ class Api(Iface):
return OnlineCheck(rid, result)
+ @permission(PERMS.ADD)
def generatePackages(self, links):
""" Parses links, generates packages names from urls
@@ -367,6 +422,7 @@ class Api(Iface):
result = parseNames((x, x) for x in links)
return result
+ @permission(PERMS.ADD)
def generateAndAddPackages(self, links, dest=Destination.Queue):
"""Generates and add packages
@@ -377,6 +433,7 @@ class Api(Iface):
return [self.addPackage(name, urls, dest) for name, urls
in self.generatePackages(links).iteritems()]
+ @permission(PERMS.ADD)
def checkAndAddPackages(self, links, dest=Destination.Queue):
"""Checks online status, retrieves names, and will add packages.\
Because of this packages are not added immediatly, only for internal use.
@@ -389,6 +446,7 @@ class Api(Iface):
self.core.threadManager.createResultThread(data, True)
+ @permission(PERMS.LIST)
def getPackageData(self, pid):
"""Returns complete information about package, and included files.
@@ -406,6 +464,7 @@ class Api(Iface):
return pdata
+ @permission(PERMS.LIST)
def getPackageInfo(self, pid):
"""Returns information about package, without detailed information about containing files
@@ -423,6 +482,7 @@ class Api(Iface):
return pdata
+ @permission(PERMS.LIST)
def getFileData(self, fid):
"""Get complete information about a specific file.
@@ -436,6 +496,7 @@ class Api(Iface):
fdata = self._convertPyFile(info.values()[0])
return fdata
+ @permission(PERMS.DELETE)
def deleteFiles(self, fids):
"""Deletes several file entries from pyload.
@@ -446,6 +507,7 @@ class Api(Iface):
self.core.files.save()
+ @permission(PERMS.DELETE)
def deletePackages(self, pids):
"""Deletes packages and containing links.
@@ -456,6 +518,7 @@ class Api(Iface):
self.core.files.save()
+ @permission(PERMS.LIST)
def getQueue(self):
"""Returns info about queue and packages, **not** about files, see `getQueueData` \
or `getPackageData` instead.
@@ -468,6 +531,7 @@ class Api(Iface):
pack["linkstotal"])
for pack in self.core.files.getInfoData(Destination.Queue).itervalues()]
+ @permission(PERMS.LIST)
def getQueueData(self):
"""Return complete data about everything in queue, this is very expensive use it sparely.\
See `getQueue` for alternative.
@@ -480,6 +544,7 @@ class Api(Iface):
links=[self._convertPyFile(x) for x in pack["links"].itervalues()])
for pack in self.core.files.getCompleteData(Destination.Queue).itervalues()]
+ @permission(PERMS.LIST)
def getCollector(self):
"""same as `getQueue` for collector.
@@ -491,6 +556,7 @@ class Api(Iface):
pack["linkstotal"])
for pack in self.core.files.getInfoData(Destination.Collector).itervalues()]
+ @permission(PERMS.LIST)
def getCollectorData(self):
"""same as `getQueueData` for collector.
@@ -503,6 +569,7 @@ class Api(Iface):
for pack in self.core.files.getCompleteData(Destination.Collector).itervalues()]
+ @permission(PERMS.ADD)
def addFiles(self, pid, links):
"""Adds files to specific package.
@@ -514,6 +581,7 @@ class Api(Iface):
self.core.log.info(_("Added %(count)d links to package #%(package)d ") % {"count": len(links), "package": pid})
self.core.files.save()
+ @permission(PERMS.MODIFY)
def pushToQueue(self, pid):
"""Moves package from Collector to Queue.
@@ -521,6 +589,7 @@ class Api(Iface):
"""
self.core.files.setPackageLocation(pid, Destination.Queue)
+ @permission(PERMS.MODIFY)
def pullFromQueue(self, pid):
"""Moves package from Queue to Collector.
@@ -528,6 +597,7 @@ class Api(Iface):
"""
self.core.files.setPackageLocation(pid, Destination.Collector)
+ @permission(PERMS.MODIFY)
def restartPackage(self, pid):
"""Restarts a package, resets every containing files.
@@ -535,6 +605,7 @@ class Api(Iface):
"""
self.core.files.restartPackage(int(pid))
+ @permission(PERMS.MODIFY)
def restartFile(self, fid):
"""Resets file status, so it will be downloaded again.
@@ -542,6 +613,7 @@ class Api(Iface):
"""
self.core.files.restartFile(int(fid))
+ @permission(PERMS.MODIFY)
def recheckPackage(self, pid):
"""Proofes online status of all files in a package, also a default action when package is added.
@@ -550,6 +622,7 @@ class Api(Iface):
"""
self.core.files.reCheckPackage(int(pid))
+ @permission(PERMS.MODIFY)
def stopAllDownloads(self):
"""Aborts all running downloads."""
@@ -557,6 +630,7 @@ class Api(Iface):
for pyfile in pyfiles:
pyfile.abortDownload()
+ @permission(PERMS.MODIFY)
def stopDownloads(self, fids):
"""Aborts specific downloads.
@@ -569,6 +643,7 @@ class Api(Iface):
if pyfile.id in fids:
pyfile.abortDownload()
+ @permission(PERMS.MODIFY)
def setPackageName(self, pid, name):
"""Renames a package.
@@ -579,6 +654,7 @@ class Api(Iface):
pack.name = name
pack.sync()
+ @permission(PERMS.MODIFY)
def movePackage(self, destination, pid):
"""Set a new package location.
@@ -588,6 +664,7 @@ class Api(Iface):
if destination not in (0, 1): return
self.core.files.setPackageLocation(pid, destination)
+ @permission(PERMS.MODIFY)
def moveFiles(self, fids, pid):
"""Move multiple files to another package
@@ -599,6 +676,7 @@ class Api(Iface):
pass
+ @permission(PERMS.ADD)
def uploadContainer(self, filename, data):
"""Uploads and adds a container file to pyLoad.
@@ -611,6 +689,7 @@ class Api(Iface):
self.addPackage(th.name, [th.name], Destination.Queue)
+ @permission(PERMS.MODIFY)
def orderPackage(self, pid, position):
"""Gives a package a new position.
@@ -619,6 +698,7 @@ class Api(Iface):
"""
self.core.files.reorderPackage(pid, position)
+ @permission(PERMS.MODIFY)
def orderFile(self, fid, position):
"""Gives a new position to a file within its package.
@@ -627,6 +707,7 @@ class Api(Iface):
"""
self.core.files.reorderFile(fid, position)
+ @permission(PERMS.MODIFY)
def setPackageData(self, pid, data):
"""Allows to modify several package attributes.
@@ -643,6 +724,7 @@ class Api(Iface):
p.sync()
self.core.files.save()
+ @permission(PERMS.DELETE)
def deleteFinished(self):
"""Deletes all finished files and completly finished packages.
@@ -651,10 +733,12 @@ class Api(Iface):
deleted = self.core.files.deleteFinishedLinks()
return deleted
+ @permission(PERMS.MODIFY)
def restartFailed(self):
"""Restarts all failed failes."""
self.core.files.restartFailed()
+ @permission(PERMS.LIST)
def getPackageOrder(self, destination):
"""Returns information about package order.
@@ -672,6 +756,7 @@ class Api(Iface):
order[pack["order"]] = pack["id"]
return order
+ @permission(PERMS.LIST)
def getFileOrder(self, pid):
"""Information about file order within package.
@@ -687,6 +772,7 @@ class Api(Iface):
return order
+ @permission(PERMS.STATUS)
def isCaptchaWaiting(self):
"""Indicates wether a captcha task is available
@@ -696,6 +782,7 @@ class Api(Iface):
task = self.core.captchaManager.getTask()
return not task is None
+ @permission(PERMS.STATUS)
def getCaptchaTask(self, exclusive=False):
"""Returns a captcha task
@@ -712,6 +799,7 @@ class Api(Iface):
else:
return CaptchaTask(-1)
+ @permission(PERMS.STATUS)
def getCaptchaTaskStatus(self, tid):
"""Get information about captcha task
@@ -722,6 +810,7 @@ class Api(Iface):
t = self.core.captchaManager.getTaskByID(tid)
return t.getStatus() if t else ""
+ @permission(PERMS.STATUS)
def setCaptchaResult(self, tid, result):
"""Set result for a captcha task
@@ -735,6 +824,7 @@ class Api(Iface):
self.core.captchaManager.removeTask(task)
+ @permission(PERMS.STATUS)
def getEvents(self, uuid):
"""Lists occured events, may be affected to changes in future.
@@ -764,6 +854,7 @@ class Api(Iface):
newEvents.append(event)
return newEvents
+ @permission(PERMS.ACCOUNTS)
def getAccounts(self, refresh):
"""Get information about all entered accounts.
@@ -778,6 +869,7 @@ class Api(Iface):
for acc in group])
return accounts
+ @permission(PERMS.ALL)
def getAccountTypes(self):
"""All available account types.
@@ -785,10 +877,12 @@ class Api(Iface):
"""
return self.core.accountManager.accounts.keys()
+ @permission(PERMS.ACCOUNTS)
def updateAccount(self, plugin, account, password=None, options={}):
"""Changes pw/options for specific account."""
self.core.accountManager.updateAccount(plugin, account, password, options)
+ @permission(PERMS.ACCOUNTS)
def removeAccount(self, plugin, account):
"""Remove account from pyload.
@@ -797,6 +891,7 @@ class Api(Iface):
"""
self.core.accountManager.removeAccount(plugin, account)
+ @permission(PERMS.ALL)
def login(self, username, password, remoteip=None):
"""Login into pyLoad, this **must** be called when using rpc before any methods can be used.
@@ -805,26 +900,38 @@ class Api(Iface):
:param remoteip: Omit this argument, its only used internal
:return: bool indicating login was successful
"""
- if self.core.config["remote"]["nolocalauth"] and remoteip == "127.0.0.1":
- return True
- if self.core.startedInGui and remoteip == "127.0.0.1":
- return True
+ return True if self.checkAuth(username, password, remoteip) else False
- user = self.core.db.checkAuth(username, password)
- if user and user["role"] == ROLE.ADMIN:
- return True
-
- return False
-
- def checkAuth(self, username, password):
+ def checkAuth(self, username, password, remoteip=None):
"""Check authentication and returns details
:param username:
:param password:
+ :param remoteip:
:return: dict with info, empty when login is incorrect
"""
+ if self.core.config["remote"]["nolocalauth"] and remoteip == "127.0.0.1":
+ return "local"
+ if self.core.startedInGui and remoteip == "127.0.0.1":
+ return "local"
+
return self.core.db.checkAuth(username, password)
+ def isAuthorized(self, func, userdata):
+ """checks if the user is authorized for specific method
+
+ :param func: function name
+ :param userdata: dictionary of user data
+ :return: boolean
+ """
+ if userdata["role"] == ROLE.ADMIN or userdata == "local":
+ return True
+ elif func in permMap and has_permission(userdata["permission"], permMap[func]):
+ return True
+ else:
+ return False
+
+
def getUserData(self, username, password):
"""see `checkAuth`"""
return self.checkAuth(username, password)
@@ -834,6 +941,7 @@ class Api(Iface):
"""returns all known user and info"""
return self.core.db.getAllUserData()
+ @permission(PERMS.STATUS)
def getServices(self):
""" A dict of available services, these can be defined by hook plugins.
@@ -845,6 +953,7 @@ class Api(Iface):
return data
+ @permission(PERMS.STATUS)
def hasService(self, plugin, func):
"""Checks wether a service is available.
@@ -855,6 +964,7 @@ class Api(Iface):
cont = self.core.hookManager.methods
return plugin in cont and func in cont[plugin]
+ @permission(PERMS.STATUS)
def call(self, info):
"""Calls a service (a method in hook plugin).
@@ -877,6 +987,7 @@ class Api(Iface):
except Exception, e:
raise ServiceException(e.message)
+ @permission(PERMS.STATUS)
def getAllInfo(self):
"""Returns all information stored by hook plugins. Values are always strings
@@ -884,6 +995,7 @@ class Api(Iface):
"""
return self.core.hookManager.getAllInfo()
+ @permission(PERMS.STATUS)
def getInfoByPlugin(self, plugin):
"""Returns information stored by a specific plugin.