Multi-User Restricted Access with Python Eve

随声附和 提交于 2019-12-12 23:05:08

问题


Currently, Eve v0.4 supports User-Restricted Resource Access via the 'auth_field', but it seems to be designed to automatically handle the single-owner case.

How would you enable multi-user restricted access, where a user is allowed to view the resource if their id was included in an array of permitted ids? Potentially with multiple lists for separate read and write permissions.


回答1:


I wrote a small hack for EVE which is adding this functionality. Maybe it's a little bit tricky, but it works.

You need to update your fdec function auth.py for EVE:

def fdec(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if args:
            # resource or item endpoint
            resource_name = args[0]
            resource = app.config['DOMAIN'][args[0]]
            if endpoint_class == 'resource':
                public = resource['public_methods']
                roles = resource['allowed_roles']
                if request.method in ['GET', 'HEAD', 'OPTIONS']:
                    roles += resource['allowed_read_roles']
                else:
                    roles += resource['allowed_write_roles']
            elif endpoint_class == 'item':
                public = resource['public_item_methods']
                roles = resource['allowed_item_roles']
                if roles and isinstance(roles, str):
                    items = app.data.driver.db[args[0]]
                    item = items.find_one(kwargs)
                    roles = item[roles]
                if request.method in ['GET', 'HEAD', 'OPTIONS']:
                    roles += resource['allowed_item_read_roles']
                else:
                    roles += resource['allowed_item_write_roles']
            if callable(resource['authentication']):
                auth = resource['authentication']()
            else:
                auth = resource['authentication']
        else:
            # home
            resource_name = resource = None
            public = app.config['PUBLIC_METHODS'] + ['OPTIONS']
            roles = app.config['ALLOWED_ROLES']
            if request.method in ['GET', 'OPTIONS']:
                roles += app.config['ALLOWED_READ_ROLES']
            else:
                roles += app.config['ALLOWED_WRITE_ROLES']
            auth = app.auth
        if auth and request.method not in public:
            if not auth.authorized(roles, resource_name, request.method):
                return auth.authenticate()
        return f(*args, **kwargs)
    return decorated
return fdec

As you can see I added a condition: if isinstance(roles, str) The idea is that when you put a string to allowed_roles instead of list it means that you're pointing to a field in this item, which contains a list of users. So for example I have next scheme and definition of groups:

definition = {
    'url': 'groups',
    'item_title': 'group',
    # only admins and apps are allowed to consume this endpoint
    'cache_control': '',
    'cache_expires': 0,
    'id_field': 'url',
    'schema': _schema,
    'allowed_item_roles': 'users',
    'additional_lookup': {
        'url': 'regex("[\w]+")',     # to be unique
        'field': 'url',
    },
}

_schema = {
    'name': required_string,  # group name
    'url': unique_string,       # group url - unique id
    'users': {                  # list of users, who registered for this group
        'type': 'list',
        'scheme': embedded_object('accounts')
    },
    'items': {                  # list of items in the group
        'type': 'list',
        'scheme': embedded_object('items'),
    },
    'owner': embedded_object('accounts'),
    'secret': {'type': 'string'}

}

So as you can see I have a list of users, which is an embedded object of my accounts. The next step is to update roles authentication. Now it should looks like:

def check_auth(self, token, allowed_roles, resource, method):
    accounts = app.data.driver.db['accounts']
    lookup = {'token': token}
    if allowed_roles:
        # only retrieve a user if his roles match ``allowed_roles``
        lookup['username'] = {'$in': allowed_roles}
    account = accounts.find_one(lookup)
    if account and 'username' in account:
        self.set_request_auth_value(account['username'])
    if account:
        self.request_auth_value = account['username']
    return account is not None

So it checks if username is in allowed roles or not. The last step is to update flaskapp.py in EVE to support strings in allowed_roles

def validate_roles(self, directive, candidate, resource):
    """ Validates that user role directives are syntactically and formally
    adeguate.

    :param directive: either 'allowed_[read_|write_]roles' or
                      'allow_item_[read_|write_]roles'.
    :param candidate: the candidate setting to be validated.
    :param resource: name of the resource to which the candidate settings
                     refer to.

    .. versionadded:: 0.0.4
    """
    roles = candidate[directive]
    if not (isinstance(roles, list) or isinstance(roles, str)):
        raise ConfigException("'%s' must be list"
                              "[%s]." % (directive, resource))

Anyway it's still a workaround and I'm not sure that it will work fast and reliable when you will have thousands of users per item, but for a small amount of users it works.

Hope it will help




回答2:


User Restricted Resource Access is essentially a mechanism for transparently storing the id of the user who created the document along with the document itself. When the user comes back to the endpoint he only sees/edits his own documents. How would you assign multiple "owners" to the document when it is stored?

Have you looked into Role Based Access Control? It does what you are asking for, although at the endpoint (not document) level.



来源:https://stackoverflow.com/questions/24760710/multi-user-restricted-access-with-python-eve

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!