def logout_client(): """ Client-initiated logout """ client = Client.get(key=request.args['client_id']) if client is None: # No such client. Possible CSRF. Don't logout and don't send them back flash(logout_errormsg, 'danger') return redirect(url_for('index')) if client.trusted: # This is a trusted client. Does the referring domain match? clienthost = urlparse.urlsplit(client.redirect_uri).hostname if request.referrer: if clienthost != urlparse.urlsplit(request.referrer).hostname: # Doesn't. Don't logout and don't send back flash(logout_errormsg, 'danger') return redirect(url_for('index')) # else: no referrer? Either stripped out by browser or a proxy, or this is a direct link. # We can't do anything about that, so assume it's a legit case. # # If there is a next destination, is it in the same domain? if 'next' in request.args: if clienthost != urlparse.urlsplit(request.args['next']).hostname: # Doesn't. Assume CSRF and redirect to index without logout flash(logout_errormsg, 'danger') return redirect(url_for('index')) # All good. Log them out and send them back logout_internal() return redirect(get_next_url(external=True)) else: # We know this client, but it's not trusted. Send back without logout. return redirect(get_next_url(external=True))
def _client_login_inner(): if request.authorization is None: return Response(u"Client credentials required.", 401, {"WWW-Authenticate": 'Basic realm="Client credentials"'}) client = Client.get(key=request.authorization.username) if client is None or not client.secret_is(request.authorization.password): return Response(u"Invalid client credentials.", 401, {"WWW-Authenticate": 'Basic realm="Client credentials"'}) g.client = client
def logout_client(): """ Client-initiated logout """ client = Client.get(key=request.args['client_id']) if client is None: # No such client. Possible CSRF. Don't logout and don't send them back flash(logout_errormsg, 'danger') return redirect(url_for('index')) if client.trusted: # This is a trusted client. Does the referring domain match? clienthost = urlparse.urlsplit(client.redirect_uri).hostname if request.referrer: if clienthost != urlparse.urlsplit(request.referrer).hostname: # Doesn't. Don't logout and don't send back flash(logout_errormsg, 'danger') return redirect(url_for('index')) # else: no referrer? Either stripped out by browser or a proxy, or this is a direct link. # We can't do anything about that, so assume it's a legit case. # # If there is a next destination, is it in the same domain? if 'next' in request.args: if clienthost != urlparse.urlsplit(request.args['next']).hostname: # Doesn't. Assume CSRF and redirect to index without logout flash(logout_errormsg, 'danger') return redirect(url_for('index')) # All good. Log them out and send them back logout_internal() return redirect(get_next_url(external=True)) else: # We know this client, but it's not trusted. Send back without logout. return redirect(get_next_url(external=True))
def _client_login_inner(): if request.authorization is None or not request.authorization.username: return Response(u"Client credentials required.", 401, {'WWW-Authenticate': 'Basic realm="Client credentials"'}) client = Client.get(key=request.authorization.username) if client is None or not client.secret_is(request.authorization.password): return Response(u"Invalid client credentials.", 401, {'WWW-Authenticate': 'Basic realm="Client credentials"'}) g.client = client
def login_beacon_iframe(client_id, login_url): client = Client.get(key=client_id) if client is None: abort(404) if not client.host_matches(login_url): abort(400) return render_template('login_beacon.html', client=client, login_url=login_url), 200, { 'Expires': 'Fri, 01 Jan 1990 00:00:00 GMT', 'Cache-Control': 'private, max-age=86400' }
def login_beacon_json(client_id): client = Client.get(key=client_id) if client is None: abort(404) if g.user: token = client.authtoken_for(g.user) else: token = None response = jsonify({ 'hastoken': True if token else False }) response.headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT' response.headers['Cache-Control'] = 'private, max-age=300' return response
def logout_client(): """ Client-initiated logout """ client = Client.get(key=request.args['client_id']) if client is None or not request.referrer or not client.host_matches(request.referrer): # No referrer or such client, or request didn't come from the client website. # Possible CSRF. Don't logout and don't send them back flash(current_app.config.get('LOGOUT_UNAUTHORIZED_MESSAGE') or logout_errormsg, 'danger') return redirect(url_for('index')) # If there is a next destination, is it in the same domain as the client? if 'next' in request.args: if not client.host_matches(request.args['next']): # Host doesn't match. Assume CSRF and redirect to index without logout flash(current_app.config.get('LOGOUT_UNAUTHORIZED_MESSAGE') or logout_errormsg, 'danger') return redirect(url_for('index')) # All good. Log them out and send them back logout_internal() db.session.commit() return redirect(get_next_url(external=True))
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 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