def token_verify(): token = request.form.get('access_token') client_resource = request.form.get( 'resource') # Can only be a single resource if not client_resource: # No resource specified by caller return resource_error('no_resource') if not token: # No token specified by caller return resource_error('no_token') authtoken = AuthToken.query.filter_by(token=token).first() if not authtoken: # No such auth token return api_result('error', error='no_token') # TODO: Add support for wildcard scopes in here if g.client.namespace + ':' + client_resource not in authtoken.scope: # Token does not grant access to this resource return api_result('error', error='access_denied') if '/' in client_resource: parts = client_resource.split('/') if len(parts) != 2: return api_result('error', error='invalid_scope') resource_name, action_name = parts else: resource_name = client_resource action_name = None resource = Resource.get(resource_name, client=g.client) if not resource: # Resource does not exist or does not belong to this client return api_result('error', error='access_denied') if action_name: action = ResourceAction.query.filter_by(name=action_name, resource=resource).first() if not action: return api_result('error', error='access_denied') # All validations passed. Token is valid for this client and scope. Return with information on the token # TODO: Don't return validity. Set the HTTP cache headers instead. params = { 'validity': 120 } # Period (in seconds) for which this assertion may be cached. if authtoken.user: params['userinfo'] = get_userinfo(authtoken.user, g.client, scope=authtoken.scope) params['clientinfo'] = { 'title': authtoken.client.title, 'userid': authtoken.client.user.userid, 'buid': authtoken.client.user.userid, 'owner_title': authtoken.client.owner.pickername, 'website': authtoken.client.website, 'key': authtoken.client.key, 'trusted': authtoken.client.trusted, } return api_result('ok', **params)
def token_verify(): token = request.form.get('access_token') client_resource = request.form.get('resource') # Can only be a single resource if not client_resource: # No resource specified by caller return resource_error('no_resource') if not token: # No token specified by caller return resource_error('no_token') if not current_auth.client.namespace: # This client has not defined any resources return api_result('error', error='client_no_resources') authtoken = AuthToken.get(token=token) if not authtoken: # No such auth token return api_result('error', error='no_token') if (current_auth.client.namespace + ':' + client_resource not in authtoken.effective_scope) and ( current_auth.client.namespace + ':*' not in authtoken.effective_scope): # Token does not grant access to this resource return api_result('error', error='access_denied') if '/' in client_resource: parts = client_resource.split('/') if len(parts) != 2: return api_result('error', error='invalid_scope') resource_name, action_name = parts else: resource_name = client_resource action_name = None if resource_name != '*': resource = Resource.get(resource_name, client=current_auth.client) if not resource: # Resource does not exist or does not belong to this client return api_result('error', error='access_denied') if action_name and action_name != '*': action = ResourceAction.query.filter_by(name=action_name, resource=resource).first() if not action: return api_result('error', error='access_denied') # All validations passed. Token is valid for this client and scope. Return with information on the token # TODO: Don't return validity. Set the HTTP cache headers instead. params = {'validity': 120} # Period (in seconds) for which this assertion may be cached. if authtoken.user: params['userinfo'] = get_userinfo(authtoken.user, current_auth.client, scope=authtoken.effective_scope) params['clientinfo'] = { 'title': authtoken.client.title, 'userid': authtoken.client.owner.buid, 'buid': authtoken.client.owner.buid, 'uuid': authtoken.client.owner.uuid, 'owner_title': authtoken.client.owner.pickername, 'website': authtoken.client.website, 'key': authtoken.client.key, 'trusted': authtoken.client.trusted, } return api_result('ok', **params)
def validate_name(self, field): if not valid_username(field.data): raise wtforms.ValidationError("Name contains invalid characters.") if field.data in resource_registry: raise wtforms.ValidationError("This name is reserved for internal use") existing = Resource.get(name=field.data) if existing and existing.id != self.edit_id: raise wtforms.ValidationError("A resource with that name already exists")
def verifyscope(scope, client): """ Verify if requested scope is valid for this client. Scope must be a list. """ resources = {} # resource_object: [action_object, ...] for item in scope: if item not in resource_registry: # Validation is only required for non-internal resources # Validation 1: namespace:resource/action is properly formatted if ':' not in item: raise ScopeException( u"No namespace specified for external resource ‘{scope}’ in scope" .format(scope=item)) itemparts = item.split(':') if len(itemparts) != 2: raise ScopeException( u"Too many ‘:’ characters in ‘{scope}’ in scope".format( scope=item)) namespace, item = itemparts if '/' in item: parts = item.split('/') if len(parts) != 2: raise ScopeException( u"Too many / characters in ‘{scope}’ in scope".format( scope=item)) resource_name, action_name = parts else: resource_name = item action_name = None resource = Resource.get(name=resource_name, namespace=namespace) # Validation 2: Resource exists and client has access to it if not resource: raise ScopeException( u"Unknown resource ‘{resource}’ under namespace ‘{namespace}’ in scope" .format(resource=resource_name, namespace=namespace)) if resource.restricted and resource.client.owner != client.owner: raise ScopeException( u"This application does not have access to resource ‘{resource}’ in scope" .format(resource=resource_name)) # Validation 3: Action is valid if action_name: action = resource.get_action(action_name) if not action: raise ScopeException( u"Unknown action ‘{action}’ on resource ‘{resource}’ under namespace ‘{namespace}’" .format(action=action_name, resource=resource_name, namespace=namespace)) resources.setdefault(resource, []).append(action) else: resources.setdefault(resource, []) return resources
def validate_name(self, field): field.data = field.data.lower() if not valid_username(field.data): raise forms.ValidationError(_("Name contains invalid characters")) if field.data in resource_registry: raise forms.ValidationError(_("This name is reserved for internal use")) existing = Resource.get(name=field.data, client=self.client) if existing and existing.id != self.edit_id: raise forms.ValidationError(_("A resource with that name already exists"))
def verifyscope(scope, client): """ Verify if requested scope is valid for this client. Scope must be a list. """ resources = {} # resource_object: [action_object, ...] for item in scope: if item not in resource_registry: # Validation is only required for non-internal resources # Validation 1: namespace:resource/action is properly formatted if ':' not in item: raise ScopeException(u"No namespace specified for external resource ‘{scope}’ in scope".format(scope=item)) itemparts = item.split(':') if len(itemparts) != 2: raise ScopeException(u"Too many ‘:’ characters in ‘{scope}’ in scope".format(scope=item)) namespace, item = itemparts if '/' in item: parts = item.split('/') if len(parts) != 2: raise ScopeException(u"Too many / characters in ‘{scope}’ in scope".format(scope=item)) resource_name, action_name = parts else: resource_name = item action_name = None resource = Resource.get(name=resource_name, namespace=namespace) # Validation 2: Resource exists and client has access to it if not resource: raise ScopeException(u"Unknown resource ‘{resource}’ under namespace ‘{namespace}’ in scope".format(resource=resource_name, namespace=namespace)) if resource.restricted and resource.client.owner != client.owner: raise ScopeException( u"This application does not have access to resource ‘{resource}’ in scope".format(resource=resource_name)) # Validation 3: Action is valid if action_name: action = resource.get_action(action_name) if not action: raise ScopeException(u"Unknown action ‘{action}’ on resource ‘{resource}’ under namespace ‘{namespace}’".format( action=action_name, resource=resource_name, namespace=namespace)) resources.setdefault(resource, []).append(action) else: resources.setdefault(resource, []) return resources
def verifyscope(scope, client): """ Verify if requested scope is valid for this client. Scope must be a list. """ internal_resources = [] # Names of internal resources external_resources = {} # resource_object: [action_object, ...] full_client_access = [] # Clients linked to namespace:* scope for item in scope: if item == '*': # The '*' resource (full access) is only available to trusted clients if not client.trusted: raise ScopeException(_(u"Full access is only available to trusted clients")) elif item in resource_registry: if resource_registry[item]['trusted'] and not client.trusted: raise ScopeException(_(u"The resource {scope} is only available to trusted clients").format(scope=item)) internal_resources.append(item) else: # Validation 0: Is this an internal wildcard resource? if item.endswith('/*'): found_internal = False wildcard_base = item[:-2] for key in resource_registry: if key == wildcard_base or key.startswith(wildcard_base + '/'): if resource_registry[key]['trusted'] and not client.trusted: # Skip over trusted resources if the client is not trusted continue internal_resources.append(key) found_internal = True if found_internal: continue # Continue to next item in scope, skipping the following # Further validation is only required for non-internal resources # Validation 1: namespace:resource/action is properly formatted if ':' not in item: raise ScopeException(_(u"No namespace specified for external resource ‘{scope}’ in scope").format(scope=item)) itemparts = item.split(':') if len(itemparts) != 2: raise ScopeException(_(u"Too many ‘:’ characters in ‘{scope}’ in scope").format(scope=item)) namespace, subitem = itemparts if '/' in subitem: parts = subitem.split('/') if len(parts) != 2: raise ScopeException(_(u"Too many ‘/’ characters in ‘{scope}’ in scope").format(scope=item)) resource_name, action_name = parts else: resource_name = subitem action_name = None if resource_name == '*' and not action_name: resource_client = Client.get(namespace=namespace) if resource_client: if resource_client.owner == client.owner: full_client_access.append(resource_client) else: raise ScopeException( _(u"This application does not have access to all resources of app ‘{client}’").format( client=resource_client.title)) else: raise ScopeException(_(u"Unknown resource namespace ‘{namespace}’ in scope").format( namespace=namespace)) else: resource = Resource.get(name=resource_name, namespace=namespace) # Validation 2: Resource exists and client has access to it if not resource: raise ScopeException(_(u"Unknown resource ‘{resource}’ under namespace ‘{namespace}’ in scope").format(resource=resource_name, namespace=namespace)) if resource.restricted and resource.client.owner != client.owner: raise ScopeException( _(u"This application does not have access to resource ‘{resource}’ in scope").format(resource=resource_name)) # Validation 3: Action is valid if action_name: action = resource.get_action(action_name) if not action: raise ScopeException(_(u"Unknown action ‘{action}’ on resource ‘{resource}’ under namespace ‘{namespace}’").format( action=action_name, resource=resource_name, namespace=namespace)) external_resources.setdefault(resource, []).append(action) else: external_resources.setdefault(resource, []) internal_resources.sort() return internal_resources, external_resources, full_client_access
def sync_resources(): resources = request.get_json().get('resources', []) actions_list = {} results = {} for name in resources: if '/' in name: parts = name.split('/') if len(parts) != 2: results[name] = {'status': 'error', 'error': _(u"Invalid resource name {name}").format(name=name)} continue resource_name, action_name = parts else: resource_name = name action_name = None description = resources[name].get('description') siteresource = getbool(resources[name].get('siteresource')) restricted = getbool(resources[name].get('restricted')) actions_list.setdefault(resource_name, []) resource = Resource.get(name=resource_name, client=g.client) if resource: results[resource.name] = {'status': 'exists', 'actions': {}} if not action_name and resource.description != description: resource.description = description results[resource.name]['status'] = 'updated' if not action_name and resource.siteresource != siteresource: resource.siteresource = siteresource results[resource.name]['status'] = 'updated' if not action_name and resource.restricted != restricted: resource.restricted = restricted results[resource.name]['status'] = 'updated' else: resource = Resource(client=g.client, name=resource_name, title=resources.get(resource_name, {}).get('title') or resource_name.title(), description=resources.get(resource_name, {}).get('description') or u'') db.session.add(resource) results[resource.name] = {'status': 'added', 'actions': {}} if action_name: if action_name not in actions_list[resource_name]: actions_list[resource_name].append(action_name) action = resource.get_action(name=action_name) if action: if description != action.description: action.description = description results[resource.name]['actions'][action.name] = {'status': 'updated'} else: results[resource.name]['actions'][action.name] = {'status': 'exists'} else: # FIXME: What is "title" here? This assignment doesn't seem right action = ResourceAction(resource=resource, name=action_name, title=resources[name].get('title') or action_name.title() + " " + resource.title, description=description) db.session.add(action) results[resource.name]['actions'][action.name] = {'status': 'added'} # Deleting resources & actions not defined in client application. for resource_name in actions_list: resource = Resource.get(name=resource_name, client=g.client) actions = ResourceAction.query.filter( ~ResourceAction.name.in_(actions_list[resource_name]), ResourceAction.resource == resource) for action in actions.all(): results[resource_name]['actions'][action.name] = {'status': 'deleted'} actions.delete(synchronize_session='fetch') del_resources = Resource.query.filter( ~Resource.name.in_(actions_list.keys()), Resource.client == g.client) for resource in del_resources.all(): ResourceAction.query.filter_by(resource=resource).delete(synchronize_session='fetch') results[resource.name] = {'status': 'deleted'} del_resources.delete(synchronize_session='fetch') db.session.commit() return api_result('ok', results=results)
def sync_resources(): resources = request.get_json().get('resources', []) actions_list = {} results = {} for name in resources: if '/' in name: parts = name.split('/') if len(parts) != 2: results[name] = { 'status': 'error', 'error': _(u"Invalid resource name {name}").format(name=name) } continue resource_name, action_name = parts else: resource_name = name action_name = None description = resources[name].get('description') siteresource = getbool(resources[name].get('siteresource')) restricted = getbool(resources[name].get('restricted')) actions_list.setdefault(resource_name, []) resource = Resource.get(name=resource_name, client=g.client) if resource: results[resource.name] = {'status': 'exists', 'actions': {}} if not action_name and resource.description != description: resource.description = description results[resource.name]['status'] = 'updated' if not action_name and resource.siteresource != siteresource: resource.siteresource = siteresource results[resource.name]['status'] = 'updated' if not action_name and resource.restricted != restricted: resource.restricted = restricted results[resource.name]['status'] = 'updated' else: resource = Resource( client=g.client, name=resource_name, title=resources.get(resource_name, {}).get('title') or resource_name.title(), description=resources.get(resource_name, {}).get('description') or u'') db.session.add(resource) results[resource.name] = {'status': 'added', 'actions': {}} if action_name: if action_name not in actions_list[resource_name]: actions_list[resource_name].append(action_name) action = resource.get_action(name=action_name) if action: if description != action.description: action.description = description results[resource.name]['actions'][action.name] = { 'status': 'updated' } else: results[resource.name]['actions'][action.name] = { 'status': 'exists' } else: # FIXME: What is "title" here? This assignment doesn't seem right action = ResourceAction( resource=resource, name=action_name, title=resources[name].get('title') or action_name.title() + " " + resource.title, description=description) db.session.add(action) results[resource.name]['actions'][action.name] = { 'status': 'added' } # Deleting resources & actions not defined in client application. for resource_name in actions_list: resource = Resource.get(name=resource_name, client=g.client) actions = ResourceAction.query.filter( ~ResourceAction.name.in_(actions_list[resource_name]), ResourceAction.resource == resource) for action in actions.all(): results[resource_name]['actions'][action.name] = { 'status': 'deleted' } actions.delete(synchronize_session='fetch') del_resources = Resource.query.filter( ~Resource.name.in_(actions_list.keys()), Resource.client == g.client) for resource in del_resources.all(): ResourceAction.query.filter_by(resource=resource).delete( synchronize_session='fetch') results[resource.name] = {'status': 'deleted'} del_resources.delete(synchronize_session='fetch') db.session.commit() return api_result('ok', results=results)