Beispiel #1
0
class AuthBackend(object):
    def __init__(self):
        super(AuthBackend, self).__init__()
        self.osis = OsisDB().getConnection(p.api.appname)
        self.initChecked = False
        self.adminGroupGuid = None
        self.publicGroupGuid = None
        self.anonymousUserGuid = None
        self.userGroupGuid = None
        self._cache = cache.getApi() if cache else None
        self._cacheHash = cache.calculateHash(
            {"__name__": "authorization_authbackend"}) if cache else None

    def _initCheck(self):
        if self.initChecked:
            return

        self.initChecked = True

        #make sure we have an admins group
        adminGroup = AuthBackend._getGroup(self, ADMIN_GROUP)
        if not bool(adminGroup):
            self.adminGroupGuid = AuthBackend.createUsergroup(
                self, {"name": ADMIN_GROUP})
        else:
            self.adminGroupGuid = adminGroup[0]

        #make sure we have a public group
        publicGroup = AuthBackend._getGroup(self, PUBLIC_GROUP)
        if not bool(publicGroup):
            self.publicGroupGuid = AuthBackend.createUsergroup(
                self, {"name": PUBLIC_GROUP})
        else:
            self.publicGroupGuid = publicGroup[0]

        #make sure we have a users group
        userGroup = AuthBackend._getGroup(self, USER_GROUP)
        if not bool(userGroup):
            self.userGroupGuid = AuthBackend.createUsergroup(
                self, {"name": USER_GROUP})
        else:
            self.userGroupGuid = userGroup[0]

        #make sure we have an anonymous user
        self.anonymousUserGuid = AuthBackend._getUsers(self, ANON_USER)
        if not bool(self.anonymousUserGuid):
            #We are Anonymous. We are Legion. We do not forgive. We do not forget. Expect us.
            self.anonymousUserGuid = AuthBackend.createUser(
                self, {
                    "login": ANON_USER,
                    "name": "Anonymous User"
                })
        else:
            self.anonymousUserGuid = self.anonymousUserGuid[0]

    def _getGroup(self, name):
        searchfilter = self.osis.getFilterObject()
        searchfilter.add('group', "name", name, True)
        return self.osis.objectsFind("ui", "group", searchfilter)

    def _groupExists(self, name):
        return bool(self._getGroup(name))

    def _getUsers(self, login):
        searchfilter = self.osis.getFilterObject()
        searchfilter.add('user', "login", login, True)
        return self.osis.objectsFind("ui", "user", searchfilter)

    def _userExists(self, login):
        return bool(self._getUsers(login))

    def _getRules(self, group, functionname, context):
        searchfilter = self.osis.getFilterObject()
        searchfilter.add('authoriserule', "groupguids", ";" + group + ";",
                         False)
        searchfilter.add('authoriserule', "function", functionname, True)
        searchfilter.add('authoriserule', "context", json_print_dict(context),
                         True)
        return self.osis.objectsFind("ui", "authoriserule", searchfilter)

    def _ruleExists(self, group, functionname, context):
        return bool(self._getRules(group, functionname, context))

    def _getCachedIsAuhtorised(self, groups, functionname, context):
        if not self._cache:
            return None

        #get main authorization dict
        results = self._cache.get(self._cacheHash) or {}

        #calculate hash for isAuthorised
        isAuthorisedHash = cache.calculateHash({
            "groups": groups,
            "functionname": functionname,
            "context": context
        })
        if isAuthorisedHash in results:  #get the cached result
            return results[isAuthorisedHash]

        return None

    def _setCachedIsAuthorized(self, groups, functionname, context, value):
        if not self._cache:
            return

        #get main authorization dict
        results = self._cache.get(self._cacheHash) or {}

        #calculate hash for isAuthorised
        isAuthorisedHash = cache.calculateHash({
            "groups": groups,
            "functionname": functionname,
            "context": context
        })
        results[isAuthorisedHash] = value

        #save main authorization dict
        self._cache.set(self._cacheHash, results, cache.maxduration)

    def _clearCache(self):
        if not self._cache:
            return

        #clear main authorization dict
        self._cache.flushByKeys([self._cacheHash])

    @ensure_groups
    def verifyUserIdentity(self, login, password):  #pylint: disable=W0613
        q.errorconditionhandler.raiseError("Not implemented")

    @ensure_groups
    def createUsergroup(self, usergroupinfo):
        if self._groupExists(usergroupinfo["name"]):
            q.errorconditionhandler.raiseError("Group %s already exists." %
                                               usergroupinfo["name"])
        else:
            group = p.api.model.ui.group.new()
            group.name = usergroupinfo["name"]
            p.api.model.ui.group.save(group)
            return group.guid

    @ensure_groups
    def deleteUsergroup(self, usergroupid):
        if usergroupid == self.adminGroupGuid:
            q.errorconditionhandler.raiseError("Unable to delete %s." %
                                               ADMIN_GROUP)
        if usergroupid == self.publicGroupGuid:
            q.errorconditionhandler.raiseError("Unable to delete %s." %
                                               PUBLIC_GROUP)

        #make sure users don't reference the group anymore
        searchfilter = self.osis.getFilterObject()
        searchfilter.add('user', "groupguids", ";" + usergroupid + ";", False)
        userguids = self.osis.objectsFind("ui", "user", searchfilter)
        for userguid in userguids:
            user = p.api.model.ui.user.get(userguid)
            user.groups.remove(usergroupid)
            p.api.model.ui.user.save(user)

        #make sure rules don't reference the group anymore
        searchfilter = self.osis.getFilterObject()
        searchfilter.add('authoriserule', "groupguids",
                         ";" + usergroupid + ";", False)
        ruleguids = self.osis.objectsFind("ui", "authoriserule", searchfilter)
        for ruleguid in ruleguids:
            rule = p.api.model.ui.authoriserule.get(ruleguid)
            rule.groupguids.remove(usergroupid)
            if not len(rule.groupguids):
                p.api.model.ui.authoriserule.delete(ruleguid)
            else:
                p.api.model.ui.authoriserule.save(rule)

        p.api.model.ui.group.delete(usergroupid)

        #clear authorization cache
        self._clearCache()

        return True

    @ensure_groups
    def createUser(self, userinfo):
        if self._userExists(userinfo["login"]):
            q.errorconditionhandler.raiseError("User %s already exists." %
                                               userinfo["login"])
        else:
            user = p.api.model.ui.user.new()
            user.login = userinfo["login"]
            if "name" in userinfo:
                user.name = userinfo["name"]
            user.groups.append(self.publicGroupGuid)
            # Keep anonymous out of users group, they can't be trusted...
            if userinfo["login"] != ANON_USER:
                user.groups.append(self.userGroupGuid)
            p.api.model.ui.user.save(user)
            return user.guid

    @ensure_groups
    def deleteUser(self, userid):
        if userid == self.anonymousUserGuid:
            q.errorconditionhandler.raiseError("Unable to delete %s." %
                                               ANON_USER)
        login = p.api.model.ui.user.get(userid).login
        p.api.model.ui.user.delete(userid)
        return login

    @ensure_groups
    def updateUser(self, userid, userinfo):
        user = p.api.model.ui.user.get(userid)
        if "name" in userinfo:
            user.name = userinfo["name"]
        p.api.model.ui.user.save(user)
        return user.login

    @ensure_groups
    def addUserToGroup(self, userid, usergroupid):
        user = p.api.model.ui.user.get(userid)
        if usergroupid in user.groups:
            q.errorconditionhandler.raiseError(
                "User is already in that group.")

        user.groups.append(usergroupid)
        p.api.model.ui.user.save(user)
        return True

    @ensure_groups
    def deleteUserFromGroup(self, userid, usergroupid):
        user = p.api.model.ui.user.get(userid)
        if not usergroupid in user.groups:
            q.errorconditionhandler.raiseError("User is not in that group.")

        user.groups.remove(usergroupid)
        p.api.model.ui.user.save(user)
        return True

    @ensure_groups
    def authorise(self, groups, functionname, context):
        if not isinstance(groups, list):
            groups = [groups]
        for group in groups:
            if self._ruleExists(group, functionname, context):
                q.errorconditionhandler.raiseError("Rule already exists.")

        rule = p.api.model.ui.authoriserule.new()
        rule.groupguids = groups
        rule.function = functionname
        rule.context = context
        p.api.model.ui.authoriserule.save(rule)

        #clear authorization cache
        self._clearCache()

        return rule.guid

    @ensure_groups
    def unAuthorise(self, groups, functionname, context):
        if not isinstance(groups, list):
            groups = [groups]

        if self.adminGroupGuid in groups:
            q.errorconditionhandler.raiseError(
                "Removing authorization from the \"%s\" group is not allowed."
                % ADMIN_GROUP)

        found = False
        for group in groups:
            rules = self._getRules(group, functionname, context)
            for rule in rules:
                p.api.model.ui.authoriserule.delete(rule)
                found = True

        if found:
            #clear authorization cache
            self._clearCache()

        return found

    @ensure_groups
    def listAuthorisation(self, groups=None, functionname=None, context=None):  #pylint: disable=W0613
        q.errorconditionhandler.raiseError("Not implemented")

    @ensure_groups
    def isAuthorised(self, groups, functionname, context):
        q.logger.log(
            "Checking if group %s is authorized for function '%s' with context '%s' "
            % (str(groups), functionname, str(context)), 3)

        if not isinstance(groups, list):
            groups = [groups]

        cachedResult = self._getCachedIsAuhtorised(groups, functionname,
                                                   context)
        if cachedResult is not None:
            #return cached result
            return cachedResult

        def doSearch(groupguid, functionname, context):
            searchfilter = self.osis.getFilterObject()
            searchfilter.add('authoriserule', "groupguids",
                             ";" + groupguid + ";", False)
            searchfilter.add('authoriserule', "function", functionname, True)
            rules = self.osis.objectsFindAsView("ui", "authoriserule",
                                                searchfilter, 'authoriserule')
            # No rules found
            if not rules:
                q.logger.log("No rules found for group %s" % (str(groups)), 3)
                return False

            for rule in rules:
                ruleContext = json.loads(rule['context'])
                # Remove the '_rulegroup' and '_forceinheritance' items from the saved context.
                # This is only used to map stored rules to rulegroups in the UI
                # And yes, I know it's dirty
                if authorization:
                    if authorization.RacktivityAuthorization.RULE_GROUP_PARAM in ruleContext and authorization.RacktivityAuthorization.RULE_GROUP_PARAM not in context:
                        ruleContext.pop(authorization.RacktivityAuthorization.
                                        RULE_GROUP_PARAM)
                    if authorization.RacktivityAuthorization.FORCE_INHERITANCE_PARAM in ruleContext:
                        ruleContext.pop(authorization.RacktivityAuthorization.
                                        FORCE_INHERITANCE_PARAM)

                ruleMatch = True
                contextKeys = set(context.keys())
                ruleContextKeys = set(ruleContext.keys())

                if not ruleContextKeys.issubset(contextKeys):
                    q.logger.log(
                        "Context keys do not match for rule %s" % (str(rule)),
                        3)
                    ruleMatch = False
                else:
                    for key, value in context.iteritems():
                        keyMatches = True
                        if key in ruleContext and not (value == ""):
                            valueToMatch = ruleContext[key]
                            if isinstance(value, dict):
                                valueToMatch = json.loads(valueToMatch)
                                if set(valueToMatch.keys()).issubset(
                                        set(value.keys())):
                                    for valueKey in value.keys():
                                        if not valueKey in valueToMatch:
                                            continue
                                        if not (value[valueKey]
                                                == valueToMatch[valueKey]):
                                            keyMatches = False
                                else:
                                    keyMatches = False
                            elif isinstance(value, list):
                                if valueToMatch not in set(value):
                                    keyMatches = False
                            elif not (value == valueToMatch):
                                keyMatches = False
                        if not keyMatches:
                            q.logger.log(
                                "'%s' not in '%s' or '%s' != '%s'" %
                                (key, str(ruleContext), value, valueToMatch),
                                3)
                            ruleMatch = False
                            break
                    if ruleMatch:
                        return True
            return False

        result = False
        for groupguid in groups:
            if not groupguid:
                continue

            if doSearch(groupguid, functionname, context):
                result = True
                break

        #store result in cache
        self._setCachedIsAuthorized(groups, functionname, context, result)
        return result
Beispiel #2
0
def main(q, i, p, params, tags):  #pylint: disable=W0613
    global authService  #pylint: disable=W0603
    if authService is None:
        authService = AuthService()

    request = params["request"]
    if not request.username:
        params["result"] = False
    else:
        q.logger.log(
            "Checking authorization for user %s with params %s" %
            (request.username, str(params)), 3)

        config = getConfig(q, p)
        if request.username == "anonymous" and isAdminOrIdeSpace(params):
            #An unauthenticated user cannot access the ADMIN or IDE space
            q.logger.log(
                "An unauthenticated user cannot access the ADMIN or IDE space",
                3)
            params["result"] = False
        elif request.username == "anonymous" and isLocalRequest(
                request) and int(config["auth"]["insecure"]):
            q.logger.log("Allowing authorization for local request", 3)
            params["result"] = True
        else:
            #default unauthorized
            params["result"] = False

            conn = OsisDB().getConnection(p.api.appname)
            searchfilter = conn.getFilterObject()
            searchfilter.add("user", "login", request.username, True)
            users = conn.objectsFindAsView("ui", "user", searchfilter, "user")

            if users and len(users) == 1:
                user = users[0]
                groups = filter(None, user["groupguids"].split(";"))  #pylint: disable=W0141

                # we only parse the name in kwargs and the default values
                arguments = getAllArguments(params)
                context = {}
                authInfo = params["criteria"]
                if "authorizeParams" in authInfo:
                    for key, value in authInfo["authorizeParams"].iteritems():
                        if isinstance(value, list):
                            contextValues = list()
                            for val in value:
                                contextValue = getParamValue(arguments, val)
                                if contextValue is not None:
                                    contextValues.append(contextValue)
                            if contextValues:
                                context[key] = contextValues
                            else:
                                # param not found, bailing out
                                params["result"] = False
                                request._request.setResponseCode(412)  #pylint: disable=W0212
                                return
                        else:
                            contextValue = getParamValue(arguments, value)
                            if contextValue is not None:
                                context[key] = contextValue
                            else:
                                # param not found, bailing out
                                params["result"] = False
                                request._request.setResponseCode(412)  #pylint: disable=W0212
                                return

                funcName = None
                if "authorizeRule" in authInfo:
                    funcName = authInfo["authorizeRule"]

                if funcName:
                    params["result"] = authService.isAuthorised(
                        groups, funcName, context)

                    if not params["result"] and 'localAuthorize' in params:
                        newContext = params['localAuthorize'].redefine(
                            funcName, context)
                        if newContext:
                            q.logger.log(
                                "Rechecking authorization for user %s with new context %s"
                                % (request.username, str(newContext)), 3)
                            params["result"] = authService.isAuthorised(
                                groups, funcName, newContext)

    #set the http response to 405 when we failed
    if params["result"] == False:
        request._request.setResponseCode(405)  #pylint: disable=W0212