Example #1
0
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))
Example #2
0
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))
Example #3
0
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
Example #4
0
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
Example #5
0
def client_new():
    form = RegisterClientForm()
    form.client_owner.choices = available_client_owners()
    if request.method == 'GET':
        form.client_owner.data = g.user.userid

    if form.validate_on_submit():
        client = Client()
        form.populate_obj(client)
        client.user = form.user
        client.org = form.org
        client.trusted = False
        db.session.add(client)
        db.session.commit()
        return render_redirect(url_for('.client_info', key=client.key), code=303)

    return render_form(form=form, title="Register a new client application",
        formid="client_new", submit="Register application", ajax=True)
Example #6
0
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'
        }
Example #7
0
def client_new():
    form = RegisterClientForm(model=Client)
    form.edit_user = current_auth.user
    form.client_owner.choices = available_client_owners()
    if request.method == 'GET':
        form.client_owner.data = current_auth.user.buid

    if form.validate_on_submit():
        client = Client()
        form.populate_obj(client)
        client.user = form.user
        client.org = form.org
        client.trusted = False
        db.session.add(client)
        db.session.commit()
        return render_redirect(url_for('.client_info', key=client.key), code=303)

    return render_form(form=form, title=_("Register a new client application"),
        formid='client_new', submit=_("Register application"), ajax=True)
Example #8
0
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
Example #9
0
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))
Example #10
0
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
Example #11
0
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
# Add fixtures for test app
# user for CRUD workflow: creating client app
gustav = User(username=u"gustav",
              fullname=u"Gustav 'world' Dachshund",
              password='******')

# org for associating with client
# client for CRUD workflow of defining perms *in* client
# spare user for CRUD workflow of assigning permissions
oakley = User(username=u"oakley", fullname=u"Oakley 'huh' Dachshund")
dachsunited = Organization(name=u"dachsunited", title=u"Dachs United")
dachsunited.owners.users.append(gustav)
dachsunited.members.users.append(oakley)
dachshundworld = Client(title=u"Dachshund World",
                        org=dachsunited,
                        confidential=True,
                        website=u"http://gustavsdachshundworld.com")
partyanimal = Permission(name=u"partyanimal",
                         title=u"Party Animal",
                         org=dachsunited)

db.session.add(gustav)
db.session.add(oakley)
db.session.add(dachsunited)
db.session.add(dachshundworld)
db.session.add(partyanimal)
db.session.commit()

app.run('0.0.0.0')
Example #13
0
    def make_fixtures(self):
        """
        Create users, attach them to organizations. Create test client app, add test
        resource, action and message.
        """
        crusoe = User(username=u"crusoe",
                      fullname=u"Crusoe Celebrity Dachshund")
        oakley = User(username=u"oakley")
        piglet = User(username=u"piglet")
        nameless = User(fullname="Nameless")

        db.session.add_all([crusoe, oakley, piglet, nameless])
        self.crusoe = crusoe
        self.oakley = oakley
        self.piglet = piglet
        self.nameless = nameless

        crusoe_email = UserEmail(email=u"*****@*****.**",
                                 primary=True,
                                 user=crusoe)
        crusoe_phone = UserPhone(phone=u"+8080808080",
                                 primary=True,
                                 user=crusoe)
        oakley_email = UserEmail(email=u"*****@*****.**", user=oakley)
        db.session.add_all([crusoe_email, crusoe_phone, oakley_email])
        self.crusoe_email = crusoe_email
        self.crusoe_phone = crusoe_phone

        batdog = Organization(name=u'batdog', title=u'Batdog')
        batdog.owners.users.append(crusoe)
        batdog.members.users.append(oakley)
        db.session.add(batdog)
        self.batdog = batdog

        specialdachs = Organization(name=u"specialdachs",
                                    title=u"Special Dachshunds")
        specialdachs.owners.users.append(oakley)
        specialdachs.members.users.append(piglet)
        db.session.add(specialdachs)
        self.specialdachs = specialdachs

        client = Client(title=u"Batdog Adventures",
                        org=batdog,
                        confidential=True,
                        namespace=u'fun.batdogadventures.com',
                        website=u"http://batdogadventures.com")
        db.session.add(client)
        self.client = client

        dachshunds = Team(title=u"Dachshunds", org=batdog)
        db.session.add(dachshunds)
        self.dachshunds = dachshunds

        team_client_permission = TeamClientPermissions(
            team=dachshunds, client=client, access_permissions=u"admin")
        self.team_client_permission = team_client_permission
        db.session.add(team_client_permission)

        client_team_access = ClientTeamAccess(
            org=batdog, client=client, access_level=CLIENT_TEAM_ACCESS.ALL)
        db.session.add(client_team_access)

        bdfl = Permission(name=u"bdfl", title=u"BDFL", user=crusoe)
        db.session.add(bdfl)
        self.bdfl = bdfl

        user_client_permissions = UserClientPermissions(user=crusoe,
                                                        client=client)
        db.session.add(user_client_permissions)
        self.user_client_permissions = user_client_permissions

        resource = Resource(name=u"test_resource",
                            title=u"Test Resource",
                            client=client)
        db.session.add(resource)
        self.resource = resource

        resource_action = ResourceAction(name=u'Fun',
                                         resource=resource,
                                         title=u'fun')
        db.session.add(resource_action)
        self.resource_action = resource_action

        action = ResourceAction(name=u"read", title=u"Read", resource=resource)
        db.session.add(action)
        self.action = action

        message = SMSMessage(phone_number=crusoe_phone.phone,
                             transaction_id=u"Ruff" * 5,
                             message=u"Wuff Wuff")
        db.session.add(message)
        db.session.commit()
        self.message = message