Esempio n. 1
0
        class FooResource(Resource):
            @Route.POST()
            def bar(self, value):
                pass

            bar.request_schema = FieldSet({"value": fields.Boolean(nullable=True)})

            @bar.GET()
            def bar(self):
                pass

            bar.response_schema = FieldSet({"value": fields.Boolean(nullable=True)})

            class Meta:
                name = 'foo'
Esempio n. 2
0
        class FooResource(Resource):
            @Route.POST()
            def foo(self):
                return True

            foo.response_schema = fields.Boolean()

            class Meta:
                name = 'foo'
Esempio n. 3
0
        False).keys())

SPECIFIC_VALUES_SCHEMA = fields.List(
    fields.String(description='An individual dimension value.'),
    nullable=True,
    default=EMPTY_LIST,
    description=
    'A list of dimension values that the policy holder will be allowed to or prevented '
    'from querying on. To allow the user to query against all values, you would omit '
    'this field and instead set `allowAllValues` to true.',
)

ALL_VALUES_SCHEMA = fields.Boolean(
    description=
    'Set to true if the policy holder should be allowed to query across ALL values '
    'for the specified dimension.',
    default=False,
    nullable=True,
)

DIMENSION_VALUES_SCHEMA = fields.Object({
    'excludeValues': SPECIFIC_VALUES_SCHEMA,
    'includeValues': SPECIFIC_VALUES_SCHEMA,
    'allValues': ALL_VALUES_SCHEMA,
})

POLICY_FILTERS_SCHEMA = fields.Object(
    properties=DIMENSION_VALUES_SCHEMA,
    pattern_properties={
        fields.String(title='dimensionType',
                      description='The dimension to filter on'):
Esempio n. 4
0
    def change_password(
        self, user, new_password: fields.String()) -> fields.Boolean():
        user.password_hash = new_password
        db.session.commit()

        return True
Esempio n. 5
0
    def activate(self, user) -> fields.Boolean():
        user.is_active = True

        db.session.commit()

        return True
Esempio n. 6
0
        fields.String(
            io='r',
            description=
            'The resource that the role is tied to. If null, this role is applicable '
            'sitewide on all resources of the specified type.',
            nullable=True,
        ),
        'resourceType':
        fields.String(
            io='r',
            description='The type of resource the role is associated with.'),
        'applyToUnregistered':
        fields.Boolean(
            io='r',
            description=
            'Indicates whether or not this role only applies to registered users. If '
            'set to `false` it only applies to registered users. If set to `true`, it '
            'applies to unregistered/anonymous users as well as long as public access '
            'is enabled.',
        ),
    }),
    converter=None,
    formatter=lambda user_role: default_role_as_dictionary(user_role, True),
)

USERNAME_SCHEMA = fields.Email(
    description='The e-mail address/username that the user uses to sign-in.',
    pattern=EMAIL_PATTERN,
)

_USER_FIELDS = {
    'username':
Esempio n. 7
0
    def activate(self, domain) -> fields.Boolean():
        domain.is_active = True

        db.session.commit()

        return True
Esempio n. 8
0
 class Schema:
     description = fields.String()
     is_open = fields.Boolean(default=False)
Esempio n. 9
0
    ROLE_PERMISSIONS_FIELDS)
ROLE_LIST_SCHEMA = fields.List(
    ROLE_NAME_SCHEMA,
    title='roles',
    description='A listing of roles held by a user or security group on an '
    'indvidual resource or all resources of a specific type.',
)

DEFAULT_ROLE_SCHEMA = fields.Object({
    'roleName':
    ROLE_NAME_SCHEMA,
    'applyToUnregistered':
    fields.Boolean(
        nullable=False,
        description=
        'Indicates whether or not this role only applies to registered users. If '
        'set to `false` it only applies to registered users. If set to `false`, it '
        'applies to unregistered/anonymous users as well as long as public access '
        'is enabled.',
    ),
})

USER_ROLES_MAPPING = fields.Object(
    properties=ROLE_LIST_SCHEMA,
    pattern_properties={
        fields.String(title='username', description='The user\'s username'):
        ROLE_LIST_SCHEMA
    },
    default=None,
    nullable=True,
    description=
    'A mapping of usernames to a list of roles that a user should have for a given '
Esempio n. 10
0
    'web.server.api.permission_api_models.BackendResource', attribute='resource_id'
)

CREATED_SCHEMA = fields.DateTimeString(
    description='When the dashboard was created.', attribute='created', io='r'
)

LAST_MODIFIED_SCHEMA = fields.DateTimeString(
    description='The last time the dashboard was modified.',
    attribute='last_modified_real',
    io='r',
)

IS_OFFICIAL_SCHEMA = fields.Boolean(
    attribute='is_official',
    description='Indicates whether or not an administrator has flagged the '
    'dashboard as "official" or not.',
    io='r',
)

IS_FAVORITE_SCHEMA = fields.Boolean(
    description='Indicates whether or not the dashboard has been favorited by the current user.',
    attribute='is_favorite',
    io='r',
)

LAST_ACCESSED_BY_USER_SCHEMA = fields.DateTimeString(
    description='The last time the dashboard was accessed (if ever) by the current user.',
    nullable=True,
    attribute='last_accessed_by_user',
    io='r',
)
Esempio n. 11
0
class StandardResponse(dict):
    '''A response type that all Zenysis APIs should use
    '''

    def __init__(self, description, code, success, **additional_fields):
        super(StandardResponse, self).__init__()
        self['message'] = description
        self['code'] = code
        self['success'] = success
        for key, value in list(additional_fields.items()):
            self[key] = value


STANDARD_RESPONSE_FIELDS = {
    'success': fields.Boolean(
        description='Indicates whether the requested operation was successful or not.',
        nullable=True,
    ),
    'message': fields.String(min_length=1, description='The response from the server.'),
    'code': fields.Integer(description='The HTTP response code from the server.'),
}

STANDARD_RESPONSE_SCHEMA = FieldSet(STANDARD_RESPONSE_FIELDS)


def augment_standard_schema(additional_fields):
    '''Augments the schema of the `StandardResponse` class with additional fields that are to be
    included in the response to an API request.

    Example
    ----------
    ```
Esempio n. 12
0
 def is_recent(self, book) -> fields.Boolean():
     return datetime.date.today().year <= book.year_published + 10
Esempio n. 13
0
 def deactivate(self, permission) -> fields.Boolean():
     return False
Esempio n. 14
0
 def test_boolean_non_nullable(self):
     field = fields.Boolean()
     assert field.format(None) is False
Esempio n. 15
0
 def test_boolean_nullable(self):
     field = fields.Boolean(nullable=True)
     assert field.format(None) is None
Esempio n. 16
0
class DashboardResource(PrincipalResource):
    '''The potion class for performing CRUD operations on the `Dashboard` class.
    '''

    resource = Relation('resource', io='r')
    author = Relation('user', io='r')

    class Meta(object):
        manager = principals(DashboardManager)
        model = Dashboard
        natural_key = 'slug'
        excluded_fields = ('id',)
        id_attribute = 'resource_id'

        permissions = {'read': 'view_resource'}

        filters = {
            'slug': True,
            'title': True,
            'created': True,
            'author': {'eq': UserFilter, None: UserFilter},
            'isOfficial': True,
        }

    class Schema(object):
        title = TITLE_SCHEMA

        slug = NULLABLE_SLUG_SCHEMA

        description = DESCRIPTION_SCHEMA

        specification = SPECIFICATION_SCHEMA

        authorUsername = AUTHOR_USERNAME_SCHEMA

        author = AUTHOR_URI_SCHEMA

        resource = RESOURCE_URI_SCHEMA

        created = CREATED_SCHEMA

        isOfficial = IS_OFFICIAL_SCHEMA

        # NOTE(stephen): These fields are now pulled **from metadata** not from
        # the Dashboard model directly. They are only represented here to make
        # flask potion happy EVEN THOUGH THEY WILL NEVER EVER BE SENT BY THE
        # CLIENT, FLASK POTION STILL COMPLAINS.
        lastModified = UNUSED_LAST_MODIFIED_SCHEMA
        totalViews = UNUSED_TOTAL_VIEWS_SCHEMA

    # HACK(stephen): Attaching dashboard metadata to the base dashboard model
    # response is like fitting a square peg into a round hole. To add that
    # information in ways that Flask-Potion would naturally work causes us to
    # issue a huge number of queries per dashboard (previously 14 queries per
    # dashboard with a naive implementation). This query encapsulates all the
    # information needed for the dashboard response into a single query.
    # TODO(stephen): If we have to write workarounds like this, it probably
    # means we shouldn't be jamming too much information into a single API.
    def _attach_metadata_to_query(self, query):
        # NOTE(stephen): I don't think a transaction is necessary for this
        # read only query, but it is an easy way to access the session.
        with Transaction() as transaction:
            session = transaction._session
            subquery = DashboardUserMetadata.summary_by_dashboard_for_user(
                session, current_user.id
            ).subquery()
            return (
                query
                # Join in the summarized metadata for each dashboard.
                .outerjoin(subquery, Dashboard.id == subquery.c.dashboard_id)
                # Attach user info so we can extract the author username.
                .outerjoin(User, Dashboard.author_id == User.id)
                # Make sure all the metadata columns are included.
                .add_columns(subquery)
                # Also include all dashboard columns since otherwise a new query
                # will be issued EACH TIME we access a dashboard in the query
                # result.
                .add_columns(Dashboard.__table__)
                # Manually set up author_username since hybrid properties weren't
                # transferring.
                .add_columns(User.username.label('author_username'))
            )

    # HACK(stephen): To ensure endpoints that return a single dashboard also
    # include the appropriate metadata, we must join in the dashboard metadata
    # to our single dashboard query.
    def _get_single_dashboard_with_metadata(self, resource_id):
        # NOTE(stephen): The resource_id being filtered on here is **not**
        # Dashboard.id. This is because we use the `resource_id` column for
        # lookups and reference.
        query = self.manager._query().filter(self.manager.id_column == resource_id)
        return self._attach_metadata_to_query(query).one()

    # pylint: disable=R0201
    # pylint: disable=E1101
    # Flask Potion does not allow class methods.

    # Override the default "get all dashboards" route to augment the response
    # with dashboard specific metadata.
    @Route.GET(
        '',
        rel='instances',
        title='Something',
        description='Something',
        schema=Instances(),
        response_schema=fields.Array(fields.Object(DASHBOARD_SIMPLE_FIELDS)),
    )
    def get_instances(self, page, per_page, where, sort):
        base_query = self.manager.instances(where, sort)

        # NOTE(stephen): I'm not sure why this wouldn't exist, but I think it
        # only happens when there are no dashboards in the DB.
        if not base_query:
            return []

        return self._attach_metadata_to_query(base_query).paginate(page, per_page).items

    @Route.POST(
        '/upgrade',
        title='Upgrade Dashboard Specification',
        description='Upgrades the provided dashboard specification to the '
        'latest schema version supported by the server.',
        schema=fields.Any(),
        rel='upgrade',
    )
    def upgrade_dashboard(self, dashboard_specification):
        # The validation is being done in the conversion defined by SPECIFICATION_SCHEMA. If there
        # are any errors, they will be thrown as an exception to the client. No action needs to be
        # taken here.
        return format_and_upgrade_specification(dashboard_specification)

    @ItemRoute.POST(
        '/visualization',
        title='Add Simple Query Visualization',
        description='Adds an item from Simple Query Tool to the dashboard.',
        schema=ADD_QUERY_TO_DASHBOARD_SCHEMA,
        response_schema=DETAILED_DASHBOARD_SCHEMA,
        rel='addVisualization',
    )
    def add_visualization(self, dashboard, request):
        resource_id = dashboard.resource.id
        with AuthorizedOperation('edit_resource', 'dashboard', resource_id):
            _add_visualization(dashboard, request, False)
            track_dashboard_access(dashboard.id, True)
            return self._get_single_dashboard_with_metadata(resource_id)

    @ItemRoute.POST(
        '/visualization/advanced',
        title='Add Advanced Query Visualization',
        description='Adds an item from Advanced Query Tool to the dashboard.',
        schema=ADD_QUERY_TO_DASHBOARD_SCHEMA,
        response_schema=DETAILED_DASHBOARD_SCHEMA,
        rel='addAdvancedVisualization',
    )
    def add_advanced_visualization(self, dashboard, request):
        resource_id = dashboard.resource.id
        with AuthorizedOperation('edit_resource', 'dashboard', resource_id):
            _add_visualization(dashboard, request, True)
            track_dashboard_access(dashboard.id, True)
            return self._get_single_dashboard_with_metadata(resource_id)

    @ItemRoute.POST(
        '/transfer',
        title='Transfer Ownership',
        description='Transfers the ownership of this dashboard from the current'
        'owner to the one specified. ',
        schema=USER_URI_SCHEMA,
    )
    def transfer_ownership(self, dashboard, new_author):
        with AuthorizedOperation(
            'update_users', 'dashboard', dashboard.resource_id
        ), AuthorizedOperation('view_resource', 'user', dashboard.author.id):
            new_author = lookup_author(author_id=new_author)
            api_transfer_dashboard_ownership(dashboard, new_author)
            return None, NO_CONTENT

    @ItemRoute.POST(
        '/transfer/username',
        title='Transfer Ownership',
        description='Transfers the ownership of this dashboard from the current'
        'owner to the one specified. ',
        schema=USERNAME_SCHEMA,
    )
    def transfer_ownership_by_username(self, dashboard, new_author):
        with AuthorizedOperation(
            'update_users', 'dashboard', dashboard.resource_id
        ), AuthorizedOperation('view_resource', 'user', dashboard.author.id):
            new_author = lookup_author(author_username=new_author)
            api_transfer_dashboard_ownership(dashboard, new_author)
            return None, NO_CONTENT

    @Route.POST(
        '/transfer',
        title='Transfer Ownership',
        description='Transfers the ownership of ALL dashboards from one user '
        'to another.',
        schema=fields.Object(
            {'sourceAuthor': USER_URI_SCHEMA, 'targetAuthor': USER_URI_SCHEMA}
        ),
    )
    @authorization_required('update_users', 'dashboard')
    def transfer_bulk_ownership(self, request):
        source_author = lookup_author(author_id=request['sourceAuthor'])
        target_author = lookup_author(author_id=request['targetAuthor'])
        api_bulk_transfer_dashboard_ownership(source_author, target_author)
        return None, NO_CONTENT

    @Route.POST(
        '/transfer/username',
        title='Transfer Ownership',
        description='Transfers the ownership of ALL dashboards from one user '
        'to another.',
        schema=fields.Object(
            {'sourceAuthor': USERNAME_SCHEMA, 'targetAuthor': USERNAME_SCHEMA}
        ),
    )
    @authorization_required('update_users', 'dashboard')
    def transfer_bulk_ownership_by_username(self, request):
        source_author = lookup_author(author_username=request['sourceAuthor'])
        target_author = lookup_author(author_username=request['targetAuthor'])
        api_bulk_transfer_dashboard_ownership(source_author, target_author)
        return None, NO_CONTENT

    @ItemRoute.POST(
        '/official',
        title='Update Dashboard \'official\' flag',
        description='Marks a Dashboard as official or not.',
        schema=fields.Boolean(
            description='The updated value of the "isOfficial" flag for the '
            'dashboard.'
        ),
    )
    @authorization_required('publish_resource', 'dashboard')
    def set_official(self, dashboard, is_official):
        self.manager.update(dashboard, {'is_official': is_official})
        return None, NO_CONTENT

    @ItemRoute.POST(
        '/favorite',
        title='Update Dashboard \'favorite\' flag',
        description='Marks a Dashboard as a user favorite or not.',
        schema=fields.Boolean(
            description='The updated value of the "isFavorite" flag for the '
            'dashboard.'
        ),
    )
    def set_favorite(self, dashboard, is_favorite):
        with AuthorizedOperation(
            'view_resource', 'dashboard', dashboard.id
        ), Transaction() as transaction:
            metadata = get_or_create_metadata(transaction, dashboard.id)
            metadata.is_favorite = is_favorite
            transaction.add_or_update(metadata)

        return None, NO_CONTENT

    # NOTE(vedant): Why are we overriding the default potion route for GET <{}:id> and
    # PATCH <{}:id>?
    #
    # We want to only display the dashboard specification when an individual dashboard is
    # requested. To do this, we have to tailor the response schema to include the specification
    # field which would otherwise NOT be included in the default Schema specified in the `Schema`
    # subclass of `DashboardResource`.
    #
    # The rationale for only display the complete dashboard specification is that specifications
    # have a tendency to be very large and we don't want to send a lot of useless data over the
    # wire unless it is specifically asked for by the client. It is also very unlikely that a
    # client will ever be loading more than one dashboard specification at a given time.
    @Route.GET(
        lambda r: '/<{}:id>'.format(r.meta.id_converter),
        rel='self',
        attribute='instance',
        response_schema=DETAILED_DASHBOARD_SCHEMA,
    )
    def read(self, id):
        with Transaction() as transaction:
            dashboard = super(DashboardResource, self).read(id)
            dashboard.total_views += 1
            track_dashboard_access(dashboard.id)
            dashboard = transaction.add_or_update(dashboard, flush=True)
        return self._get_single_dashboard_with_metadata(id)

    @Route.PATCH(
        lambda r: '/<{}:id>'.format(r.meta.id_converter),
        rel='update',
        schema=fields.Inline('self', patchable=True),
        response_schema=DETAILED_DASHBOARD_SCHEMA,
    )
    def update(self, properties, id):
        dashboard = super(DashboardResource, self).update(properties, id)
        track_dashboard_access(dashboard.id, edited=True)
        return self._get_single_dashboard_with_metadata(id)

    @ItemRoute.GET(
        '/history',
        title='Dashboard Update History',
        schema=Instances(),
        description='Gets dashboard history data',
        rel='getDashboardHistory',
        response_schema=DASHBOARD_CHANGES_SCHEMA,
    )
    # pylint: disable=W0613
    # Method signature requires where and sort params
    def get_history(self, dashboard, page, per_page, where, sort):
        records = []
        with Transaction() as transaction:
            records = (
                transaction.find_all_by_fields(
                    HistoryRecord,
                    {'object_id': dashboard.resource_id, 'object_type': self.meta.name},
                )
                .paginate(page, per_page)
                .items
            )
        return records
Esempio n. 17
0
 class Schema:
     id = fields.String(min_length=3, max_length=20)
     network = fields.ToOne('networks')
     title = fields.String(min_length=3)
     login_ask_name = fields.Boolean(default=False)
     login_require_name = fields.Boolean(default=False)
Esempio n. 18
0
    def activate(self, project) -> fields.Boolean():
        project.is_active = True

        db.session.commit()

        return True