diff options
Diffstat (limited to 'pyload/Api.py')
-rw-r--r-- | pyload/Api.py | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/pyload/Api.py b/pyload/Api.py new file mode 100644 index 000000000..32a077c08 --- /dev/null +++ b/pyload/Api.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +############################################################################### +# Copyright(c) 2008-2013 pyLoad Team +# http://www.pyload.org +# +# This file is part of pyLoad. +# pyLoad is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Subjected to the terms and conditions in LICENSE +# +# @author: RaNaN +############################################################################### + +import re +from types import MethodType + +from remote.apitypes import * + +# contains function names mapped to their permissions +# unlisted functions are for admins only +perm_map = {} + +# decorator only called on init, never initialized, so has no effect on runtime +def RequirePerm(bits): + class _Dec(object): + def __new__(cls, func, *args, **kwargs): + perm_map[func.__name__] = bits + return func + + return _Dec + +urlmatcher = re.compile(r"((https?|ftps?|xdcc|sftp):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-=\\\.&]*)", re.IGNORECASE) + +stateMap = { + DownloadState.All: frozenset(getattr(DownloadStatus, x) for x in dir(DownloadStatus) if not x.startswith("_")), + DownloadState.Finished: frozenset((DownloadStatus.Finished, DownloadStatus.Skipped)), + DownloadState.Unfinished: None, # set below + DownloadState.Failed: frozenset((DownloadStatus.Failed, DownloadStatus.TempOffline, DownloadStatus.Aborted)), + DownloadState.Unmanaged: None, #TODO +} + +stateMap[DownloadState.Unfinished] = frozenset(stateMap[DownloadState.All].difference(stateMap[DownloadState.Finished])) + +def state_string(state): + return ",".join(str(x) for x in stateMap[state]) + +from datatypes.User import User + +class Api(Iface): + """ + **pyLoads API** + + This is accessible either internal via core.api, websocket backend or json api. + + see Thrift specification file remote/thriftbackend/pyload.thrift\ + for information about data structures and what methods are usable with rpc. + + Most methods requires specific permissions, please look at the source code if you need to know.\ + These can be configured via web interface. + Admin user have all permissions, and are the only ones who can access the methods with no specific permission. + """ + + EXTERNAL = Iface # let the json api know which methods are external + EXTEND = False # only extendable when set too true + + def __init__(self, core): + self.core = core + self.user_apis = {} + + @property + def user(self): + return None #TODO return default user? + + @property + def primaryUID(self): + return self.user.primary if self.user else None + + @classmethod + def initComponents(cls): + # Allow extending the api + # This prevents unintentionally registering of the components, + # but will only work once when they are imported + cls.EXTEND = True + # Import all Api modules, they register themselves. + import pyload.api + # they will vanish from the namespace afterwards + + + @classmethod + def extend(cls, api): + """Takes all params from api and extends cls with it. + api class can be removed afterwards + + :param api: Class with methods to extend + """ + if cls.EXTEND: + for name, func in api.__dict__.iteritems(): + if name.startswith("_"): continue + setattr(cls, name, MethodType(func, None, cls)) + + return cls.EXTEND + + def withUserContext(self, uid): + """ Returns a proxy version of the api, to call method in user context + + :param uid: user or userData instance or uid + :return: :class:`UserApi` + """ + if isinstance(uid, User): + uid = uid.uid + + if uid not in self.user_apis: + user = self.core.db.getUserData(uid=uid) + if not user: #TODO: anonymous user? + return None + + self.user_apis[uid] = UserApi(self.core, User.fromUserData(self, user)) + + return self.user_apis[uid] + + + ############################# + # Auth+User Information + ############################# + + # TODO + + @RequirePerm(Permission.All) + def login(self, username, password, remoteip=None): + """Login into pyLoad, this **must** be called when using rpc before any methods can be used. + + :param username: + :param password: + :param remoteip: Omit this argument, its only used internal + :return: bool indicating login was successful + """ + return True if self.checkAuth(username, password, remoteip) else False + + 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 + """ + self.core.log.info(_("User '%s' tries to log in") % username) + + return self.core.db.checkAuth(username, password) + + def isAuthorized(self, func, user): + """checks if the user is authorized for specific method + + :param func: function name + :param user: `User` + :return: boolean + """ + if user.isAdmin(): + return True + elif func in perm_map and user.hasPermission(perm_map[func]): + return True + else: + return False + + # TODO + @RequirePerm(Permission.All) + def getUserData(self, username, password): + """similar to `checkAuth` but returns UserData thrift type """ + user = self.checkAuth(username, password) + if not user: + raise UserDoesNotExists(username) + + return user.toUserData() + + def getAllUserData(self): + """returns all known user and info""" + return self.core.db.getAllUserData() + + def changePassword(self, username, oldpw, newpw): + """ changes password for specific user """ + return self.core.db.changePassword(username, oldpw, newpw) + + def setUserPermission(self, user, permission, role): + self.core.db.setPermission(user, permission) + self.core.db.setRole(user, role) + + +class UserApi(Api): + """ Proxy object for api that provides all methods in user context """ + + def __init__(self, core, user): + # No need to init super class + self.core = core + self._user = user + + def withUserContext(self, uid): + raise Exception("Not allowed") + + @property + def user(self): + return self._user
\ No newline at end of file |