Esempio n. 1
0
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)
Esempio n. 2
0
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)
Esempio n. 3
0
    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")
Esempio n. 4
0
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
Esempio n. 5
0
    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"))
Esempio n. 6
0
def resource_new(client):
    form = ResourceForm()
    form.client = client
    form.edit_id = None
    if form.validate_on_submit():
        resource = Resource(client=client)
        form.populate_obj(resource)
        db.session.add(resource)
        db.session.commit()
        flash(_("Your new resource has been saved"), 'success')
        return render_redirect(url_for('.client_info', key=client.key), code=303)
    return render_form(form=form, title=_("Define a resource"), formid='resource_new',
        submit=_("Define resource"), ajax=True)
Esempio n. 7
0
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
Esempio n. 8
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
Esempio n. 9
0
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)
Esempio n. 10
0
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)
Esempio n. 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
Esempio n. 12
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