def test_string(): chain = validator.Chain() chain.add(validator.String(min_len=3)) chain.add(validator.String(max_len=6, trim=True)) assert chain('xxx') == 'xxx' assert chain('xxxx ') == 'xxxx' with pytest.raises(ValueError): chain('x') with pytest.raises(ValueError): chain('xxxxxxxxxx')
class AccountQuarantineListResource(Resource): """Quarantines for a single account.""" account_quarantines_filter = api.parser() account_quarantines_filter.add_argument( 'context', type=validator.String(), location=['form', 'json'], help='Consider locked status based on context.') @api.marshal_with(AccountQuarantineList) @api.doc(parser=account_quarantines_filter) @auth.require() def get(self, name): """Get account quarantines.""" args = self.account_quarantines_filter.parse_args() spreads = None if args.context: try: spreads = [int(db.const.Spread(args.context))] except Errors.NotFoundError: abort(404, message='Unknown context {!r}'.format( args.context)) ac = find_account(name) qh = QuarantineHandler.check_entity_quarantines( db=db.connection, entity_id=ac.entity_id, spreads=spreads) locked = qh.is_locked() # TODO: Replace with list of hrefs to quarantines resource? quarantines = [] for q in ac.get_entity_quarantine(only_active=True): quarantines.append(_format_quarantine(q)) return { 'locked': locked, 'quarantines': quarantines }
class ContextListResource(Resource): """Resource for contexts.""" context_search_filter = api.parser() context_search_filter.add_argument('entity_types', type=validator.String(), action='append', help='Filter by entity type(s)') @api.marshal_list_with(Context) @api.doc(parser=context_search_filter) @auth.require() def get(self): """List contexts""" args = self.context_search_filter.parse_args() filters = { key: value for (key, value) in args.items() if value is not None } entity_types = None if 'entity_types' in filters: etypes = [] for etype in filters['entity_types']: try: entity_type = db.const.EntityType(etype) int(entity_type) etypes.append(entity_type) except Errors.NotFoundError: abort(404, message='Unknown entity type for ' 'entity_types={}'.format(etype)) entity_types = etypes or None es = EntitySpread(db.connection) contexts = es.list_spreads(entity_types=entity_types) return contexts
class AccountPasswordResource(Resource): """Resource for account password change.""" new_password_parser = api.parser() new_password_parser.add_argument( 'password', type=validator.String(), required=False, location=['form', 'json'], help='Password, leave empty to generate one', ) @db.autocommit @auth.require() @api.expect(new_password_parser) @api.response(200, 'Password changed', PasswordChanged) @api.response(400, 'Invalid password') def post(self, name): """Change the password for this account.""" ac = find_account(name) data = self.new_password_parser.parse_args() password = data.get('password', None) if password is None: password = ac.make_passwd(ac.account_name) assert isinstance(password, text_type) try: check_password(password, account=ac, structured=False) except PasswordNotGoodEnough as e: abort(400, 'Bad password: {}'.format(e)) ac.set_password(password) # Remove "weak password" quarantine for q in (db.const.quarantine_autopassord, db.const.quarantine_svakt_passord): ac.delete_entity_quarantine(q) ac.write_db() return {'password': password}
class GroupResource(Resource): """ Resource for a single group. """ @staticmethod def group_info(group): return { 'name': utils._db_decode(group.group_name), 'id': group.entity_id, 'created_at': group.created_at, 'expire_date': group.expire_date, 'visibility': group.visibility, 'description': utils._db_decode(group.description), 'contexts': [row['spread'] for row in group.get_spread()], } @staticmethod def visibility_type(vis): lut = { 'all': db.const.GroupVisibility('A'), 'internal': db.const.GroupVisibility('I'), 'none': db.const.GroupVisibility('N') } return lut[vis.lower()] # Either this or import undecorated other places @staticmethod def _get(name, idtype='name'): """ Undecorated get(). """ group = find_group(name, idtype) return GroupResource.group_info(group) # GET /<group> # @auth.require() @api.response(200, 'Group found', Group) @api.response(404, 'Group not found') @api.marshal_with(Group) def get(self, name): """ Get group information. """ return self._get(name) # PUT /<group> # new_group_parser = reqparse.RequestParser() new_group_parser.add_argument( 'visibility', choices=GroupVisibility._rev_map.keys(), required=True, location='form', case_sensitive=False, nullable=False, help='{error_msg}', ) new_group_parser.add_argument( 'description', type=validator.String(min_len=0, max_len=512), location='form', nullable=True, help='{error_msg}', ) @db.autocommit @auth.require() @api.expect(new_group_parser) @api.response(200, 'Group updated', Group) @api.response(201, 'Group created', Group) @api.response(400, 'Illegal group name') @api.marshal_with(Group) def put(self, name): """ Create or update group. """ args = self.new_group_parser.parse_args() args['visibility'] = GroupVisibility.unserialize(args['visibility']) name = name.encode(db.encoding) result_code = 200 try: # find and update all attributes group = utils.get_group(name, 'name', 'Group') changes = False if group.visibility != args['visibility']: group.visibility = args['visibility'] changes = True if group.description != args['description']: group.description = args['description'].encode(db.encoding) changes = True if changes: group.write_db() except utils.EntityLookupError: # create group group = Factory.get('Group')(db.connection) bad_name = group.illegal_name(name) if bad_name: abort(400, message="Illegal group name: {!s}".format(bad_name)) group.new(auth.account.entity_id, args['visibility'], name, args['description']) result_code = 201 return self.group_info(group), result_code # TODO: Do we want PATCH? # # # PATCH /<group> # # # update_group_parser = new_group_parser.copy() # # @db.autocommit # @auth.require() # @api.expect(update_group_parser) # @api.response(200, 'group updated', Group) # @api.response(404, 'group not found') # @api.marshal_with(Group) # def patch(self, name): # """ Alter group attributes. """ # args = self.update_group_parser.parse_args() # group = find_group(name) # # changes = False # if group.description != args['description']: # group.description = args['description'] # changes = True # if group.visibility != args['visibility']: # group.visibility = args['visibility'] # changes = True # if changes: # group.write_db() # # return self.group_info(group) # DELETE /<group> # @db.autocommit @auth.require() @api.response(204, 'group deleted') @api.response(404, 'group not found') def delete(self, name): """ Delete group. """ # TODO: Find out if any user has group as dfg? # If so, 409 CONFLICT? name = name.encode(db.encoding) group = find_group(name) group.delete()
class GroupListResource(Resource): """Resource for list of groups.""" # GET / # group_search_filter = api.parser() group_search_filter.add_argument( 'name', type=validator.String(), help='Filter by name. Accepts * and ? as wildcards.') group_search_filter.add_argument( 'description', type=validator.String(), help='Filter by description. Accepts * and ? as wildcards.') group_search_filter.add_argument('context', type=validator.String(), dest='spread', help='Filter by context.') group_search_filter.add_argument( 'member_id', type=int, action='append', help='Filter by memberships. Only groups that have member_id as a ' 'member will be returned. If member_id is a sequence, the group ' 'is returned if any of the IDs are a member of it.') group_search_filter.add_argument( 'indirect_members', type=bool, help='If true, alter the behavior of the member_id filter to also ' 'include groups where member_id is an indirect member.') group_search_filter.add_argument('filter_expired', type=bool, help='If false, include expired groups.') group_search_filter.add_argument( 'expired_only', type=bool, help='If true, only include expired groups.') group_search_filter.add_argument('creator_id', type=int, help='Filter by creator entity ID.') @auth.require() @api.marshal_with(GroupListItem, as_list=True, envelope='groups') @api.doc(parser=group_search_filter) def get(self): """List groups.""" args = self.group_search_filter.parse_args() filters = { key: value for (key, value) in args.items() if value is not None } if 'spread' in filters: try: group_spread = db.const.Spread(filters['spread']) filters['spread'] = int(group_spread) except Errors.NotFoundError: abort(404, message='Unknown context={}'.format(filters['spread'])) gr = Factory.get('Group')(db.connection) groups = list() for row in gr.search(**filters): group = dict(row) group.update({ 'id': group['name'], 'name': group['name'], }) groups.append(group) return groups
class GroupMemberListResource(Resource): """Resource for list of members of groups.""" # GET /<group>/members/ # group_member_filter = api.parser() group_member_filter.add_argument('type', type=validator.String(), dest='member_type', help='Filter by entity type.') group_member_filter.add_argument( 'context', type=validator.String(), dest='member_spread', help='Filter by context. Accepts * and ? as wildcards.') group_member_filter.add_argument( 'filter_expired', type=bool, dest='member_filter_expired', help='If false, include members that are expired.') @auth.require() @api.marshal_with(GroupMember, as_list=True, envelope='members') @api.doc(parser=group_member_filter) @api.doc(params={'name': 'group name'}) def get(self, name): """List members of a group.""" args = self.group_member_filter.parse_args() filters = { key: value for (key, value) in args.items() if value is not None } if 'member_type' in filters: try: member_type = db.const.EntityType(filters['member_type']) filters['member_type'] = int(member_type) except Errors.NotFoundError: abort(404, message='Unknown entity type for type={}'.format( filters['member_type'])) if 'member_spread' in filters: try: member_spread = db.const.Spread(filters['member_spread']) filters['member_spread'] = int(member_spread) except Errors.NotFoundError: abort(404, message='Unknown context for context={}'.format( filters['member_spread'])) gr = find_group(name) filters['group_id'] = gr.entity_id filters['include_member_entity_name'] = True members = list() for row in gr.search_members(**filters): member = dict(row) member.update({ 'id': row['member_id'], 'name': row['member_name'], 'href': utils.href_from_entity_type(entity_type=row['member_type'], entity_id=row['member_id'], entity_name=row['member_name']), }) members.append(member) return members # PUT /<group>/members # group_members_parser = api.parser() group_members_parser.add_argument( 'members', type=validator.Integer(min_val=0), location=['form', 'json'], nullable=False, required=False, default=[], action='append', help='{error_msg}', ) @db.autocommit @auth.require() @api.expect(group_members_parser) @api.response(200, 'members added') @api.response(404, 'group or member not found') def put(self, name): """ Ensure that the supplied member list are the only members of the group. """ args = self.group_members_parser.parse_args() group = find_group(name) members = set(row['member_id'] for row in group.search_members( group_id=group.entity_id, member_filter_expired=False)) to_remove = members - set(args['members']) to_add = set(args['members']) - members for entity_id in to_remove: member = find_entity(entity_id) group.remove_member(member.entity_id) for entity_id in to_add: member = find_entity(entity_id) group.add_member(member.entity_id)
class ExternalIdResource(Resource): """Resource for external ID searches.""" # GET / # extid_search_filter = api.parser() extid_search_filter.add_argument( 'source_system', type=validator.String(), action='append', help='Filter by one or more source systems.') extid_search_filter.add_argument('id_type', type=validator.String(), action='append', help='Filter by one or more ID types.') extid_search_filter.add_argument('external_id', type=validator.String(), required=True, help='Filter by external ID.') @auth.require() @api.doc(parser=extid_search_filter) @api.marshal_with(ExternalIdItem, as_list=True, envelope='external_ids') def get(self): """Get external IDs""" args = self.extid_search_filter.parse_args() filters = { key: value for (key, value) in args.items() if value is not None } filters['entity_type'] = db.const.entity_person eei = EntityExternalId(db.connection) if 'source_system' in filters: source_systems = [] if not isinstance(filters['source_system'], list): filters['source_system'] = [filters['source_system']] for entry in filters['source_system']: try: code = int(db.const.AuthoritativeSystem(entry)) source_systems.append(code) except Errors.NotFoundError: abort(404, message='Unknown source_system={}'.format(entry)) filters['source_system'] = source_systems if 'id_type' in filters: id_types = [] if not isinstance(filters['id_type'], list): filters['id_type'] = [filters['id_type']] for entry in filters['id_type']: try: if entry not in models.ExternalIdType.valid_types(): raise Errors.NotFoundError code = int(models.ExternalIdType.unserialize(entry)) id_types.append(code) except Errors.NotFoundError: abort(404, message='Unknown id_type={}'.format(entry)) filters['id_type'] = id_types strip_wildcards = {ord(c): '' for c in '*?%_'} if 'external_id' in filters: filters['external_id'] = filters['external_id'].translate( strip_wildcards) results = list() for row in eei.search_external_ids(**filters): entry = dict(row) # id for the href builder, won't be shown in output entry['id'] = entry['entity_id'] results.append(entry) return results
def test_string_validator_normalizes_unicode(): assert validator.String()(OHM_SIGN) == LETTER_OMEGA
class AccountListResource(Resource): """Resource for list of accounts.""" account_search_filter = api.parser() account_search_filter.add_argument( 'name', type=validator.String(), help='Filter by account name. Accepts * and ? as wildcards.') account_search_filter.add_argument( 'context', type=validator.String(), dest='spread', help='Filter by context. Accepts * and ? as wildcards.') account_search_filter.add_argument( 'owner_id', type=int, help='Filter by owner entity ID.') account_search_filter.add_argument( 'owner_type', type=validator.String(), help='Filter by owner entity type.') account_search_filter.add_argument( 'expire_start', type=validator.String(), help='Filter by expiration start date.') account_search_filter.add_argument( 'expire_stop', type=validator.String(), help='Filter by expiration end date.') @api.marshal_with(AccountList) @api.doc(parser=account_search_filter) @auth.require() def get(self): """List accounts.""" args = self.account_search_filter.parse_args() filters = {key: value for (key, value) in args.items() if value is not None} if 'owner_type' in filters: try: owner_type = db.const.EntityType(filters['owner_type']) filters['owner_type'] = int(owner_type) except Errors.NotFoundError: abort(404, message='Unknown entity type for owner_type={}'.format( filters['owner_type'])) ac = Factory.get('Account')(db.connection) accounts = list() for row in ac.search(**filters): account = dict(row) account.update({ 'id': account['name'], 'owner': { 'id': account['owner_id'], 'type': account['owner_type'], } }) accounts.append(account) return {'accounts': accounts}
ctype='AccountHomeStatus', description='Home status'), 'disk_id': fields.base.Integer( description='Disk entity ID'), }) AccountHomeList = api.model('AccountHomeList', { 'homes': fields.base.List( fields.base.Nested(AccountHome), description='Home directories'), }) password_parser = api.parser() password_parser.add_argument( 'password', type=validator.String(), required=True, location=['form', 'json'], help='Password', ) PasswordChanged = api.model('PasswordChanged', { 'password': fields.base.String( description='New password') }) PasswordVerification = api.model('PasswordVerification', { 'verified': fields.base.Boolean( description='Did the password match?') })