class MailOutput(Schema): html_body = fields.Str() plain_body = fields.Str() subject = fields.Str() # # This is because Email is not typed on marshmallow to = fields.Email() # type: ignore cc = fields.List(fields.Email()) # type: ignore bcc = fields.List(fields.Email()) # type: ignore
def getProfileData(): # as defined in Marshmallow.schema.from_dict attributes: Dict[str, Union[fields.Field, type]] = {} attributes["uuid"] = fields.UUID(required=True) attributes["email"] = fields.Email(required=True) attributes["name"] = fields.Str(required=True) attributes["surname"] = fields.Str(required=True) attributes["isAdmin"] = fields.Boolean(required=True) attributes["isStaff"] = fields.Boolean(required=True) attributes["isCoordinator"] = fields.Boolean(required=True) attributes["privacy_accepted"] = fields.Boolean(required=True) attributes["is_active"] = fields.Boolean(required=True) attributes["expiration"] = fields.DateTime(allow_none=True, format=ISO8601UTC) attributes["roles"] = fields.Dict(required=True) attributes["last_password_change"] = fields.DateTime( required=True, format=ISO8601UTC ) attributes["first_login"] = fields.DateTime(required=True, format=ISO8601UTC) attributes["last_login"] = fields.DateTime(required=True, format=ISO8601UTC) attributes["group"] = fields.Nested(Group) attributes["two_factor_enabled"] = fields.Boolean(required=True) if custom_fields := mem.customizer.get_custom_output_fields(None): attributes.update(custom_fields)
def profile_output() -> Schema: attributes: MarshmallowSchema = {} attributes["uuid"] = fields.UUID(required=True) # This is because Email is not typed on marshmallow attributes["email"] = fields.Email(required=True) # type: ignore attributes["name"] = fields.Str(required=True) attributes["surname"] = fields.Str(required=True) attributes["isAdmin"] = fields.Boolean(required=True) attributes["isStaff"] = fields.Boolean(required=True) attributes["isCoordinator"] = fields.Boolean(required=True) attributes["privacy_accepted"] = fields.Boolean(required=True) attributes["is_active"] = fields.Boolean(required=True) attributes["expiration"] = fields.DateTime(allow_none=True, format=ISO8601UTC) attributes["roles"] = fields.Dict(required=True) attributes["last_password_change"] = fields.DateTime(required=True, format=ISO8601UTC) attributes["first_login"] = fields.DateTime(required=True, format=ISO8601UTC) attributes["last_login"] = fields.DateTime(required=True, format=ISO8601UTC) if Connector.authentication_service == "neo4j": attributes["belongs_to"] = fields.Neo4jRelationshipToSingle( Group, data_key="group") else: attributes["belongs_to"] = fields.Nested(Group, data_key="group") attributes["two_factor_enabled"] = fields.Boolean(required=True) if custom_fields := mem.customizer.get_custom_output_fields(None): attributes.update(custom_fields)
def user_registration_input(request: FlaskRequest) -> Type[Schema]: attributes: MarshmallowSchema = {} attributes["name"] = fields.Str(required=True) attributes["surname"] = fields.Str(required=True) # This is because Email is not typed on marshmallow attributes["email"] = fields.Email( # type: ignore required=True, metadata={"label": "Username (email address)"}, validate=validate.Length(max=100), ) attributes["password"] = fields.Str( required=True, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), metadata={"password": True}, ) attributes["password_confirm"] = fields.Str( required=True, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), metadata={ "label": "Password confirmation", "password": True }, ) if custom_fields := mem.customizer.get_custom_input_fields( request=None, scope=mem.customizer.REGISTRATION): attributes.update(custom_fields)
def admin_user_output(many: bool = True) -> Schema: attributes: MarshmallowSchema = {} attributes["uuid"] = fields.UUID() # This is because Email is not typed on marshmallow attributes["email"] = fields.Email() # type: ignore attributes["name"] = fields.Str() attributes["surname"] = fields.Str() attributes["first_login"] = fields.DateTime(allow_none=True, format=ISO8601UTC) attributes["last_login"] = fields.DateTime(allow_none=True, format=ISO8601UTC) attributes["last_password_change"] = fields.DateTime(allow_none=True, format=ISO8601UTC) attributes["is_active"] = fields.Boolean() attributes["privacy_accepted"] = fields.Boolean() attributes["roles"] = fields.List(fields.Nested(Role)) attributes["expiration"] = fields.DateTime(allow_none=True, format=ISO8601UTC) if Connector.authentication_service == "neo4j": attributes["belongs_to"] = fields.Neo4jRelationshipToSingle( Group, data_key="group") else: attributes["belongs_to"] = fields.Nested(Group, data_key="group") if custom_fields := mem.customizer.get_custom_output_fields(None): attributes.update(custom_fields)
class LoginsSchema(Schema): # This is because Email is not typed on marshmallow username = fields.Email() # type: ignore date = fields.DateTime(format=ISO8601UTC) IP = fields.Str() location = fields.Str() failed = fields.Boolean() flushed = fields.Boolean()
def build_schema(self, model: Type[Any]) -> None: # Get the full list of parent classes from model to object classes = inspect.getmro(model) starting_point = False # Iterate in reversed order to start from object for c in reversed(classes): # Skip all parentes up to StructuredNode and StructuredRel (included) if not starting_point: # Found the starting point, next class will be descended up to model if c == StructuredNode or c == StructuredRel: starting_point = True # skip all parent up to StructuredNode and StructuredRel INCLUDED continue # Iterate all class attributes to find neomodel properties for attribute in c.__dict__: prop = getattr(c, attribute) if not isinstance(prop, properties.Property): continue # self.fields can be None when the special value * is given in input if self.fields and attribute not in self.fields: continue # log.info("Including property {}.{}", model.__name__, attribute) if isinstance(prop, properties.StringProperty): if prop.choices is None: self.declared_fields[attribute] = fields.Str() else: self.declared_fields[attribute] = fields.Neo4jChoice( prop.choices) elif isinstance(prop, properties.BooleanProperty): self.declared_fields[attribute] = fields.Boolean() elif isinstance(prop, properties.IntegerProperty): self.declared_fields[attribute] = fields.Integer() elif isinstance(prop, properties.FloatProperty): self.declared_fields[attribute] = fields.Float() elif isinstance(prop, properties.EmailProperty): # This is because Nested is not typed on marshmallow self.declared_fields[attribute] = fields.Email( ) # type: ignore elif isinstance(prop, properties.DateTimeProperty): self.declared_fields[attribute] = fields.AwareDateTime() elif isinstance(prop, properties.DateProperty): self.declared_fields[attribute] = fields.Date() elif isinstance(prop, properties.UniqueIdProperty): self.declared_fields[attribute] = fields.Str() else: # pragma: no cover log.error( "Unsupport neomodel property: {}, fallback to StringProperty", prop.__class__.__name__, ) self.declared_fields[attribute] = fields.Str()
def group_users_output() -> Schema: attributes: MarshmallowSchema = {} # This is because Email is not typed on marshmallow attributes["email"] = fields.Email() # type: ignore attributes["name"] = fields.Str() attributes["surname"] = fields.Str() attributes["roles"] = fields.List(fields.Nested(Role)) if custom_fields := mem.customizer.get_custom_output_fields(None): attributes.update(custom_fields)
class MailInput(Schema): subject = fields.Str(required=True, metadata={"description": "Subject of your email"}) body = fields.Str( required=True, validate=validate.Length(max=9999), metadata={ "description": "Body of your email. You can use html code here." }, ) # This is because Email is not typed on marshmallow to = fields.Email( # type: ignore required=True, metadata={"label": "Destination email address"}) cc = fields.DelimitedList( # This is because Email is not typed on marshmallow fields.Email(), # type: ignore metadata={ "label": "CC - Carbon Copy", "description": "CC email addresses (comma-delimited list)", }, ) bcc = fields.DelimitedList( # This is because Email is not typed on marshmallow fields.Email(), # type: ignore metadata={ "label": "BCC - Blind Carbon Copy", "description": "BCC email addresses (comma-delimited list)", }, ) dry_run = fields.Boolean( required=True, metadata={ "label": "Dry run execution", "description": "Only simulate the email, do not send it", }, )
class Credentials(Schema): username = fields.Email(required=True) password = fields.Str( required=True, password=True, # Otherwise default testing password, like test, will fail # validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH) ) new_password = fields.Str( required=False, password=True, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), ) password_confirm = fields.Str( required=False, password=True, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), ) totp_code = TOTP(required=False)
class Credentials(Schema): # This is because Email is not typed on marshmallow username = fields.Email( # type: ignore required=True, validate=validate.Length(max=100)) password = fields.Str( required=True, metadata={"password": True}, # Otherwise default testing password, like test, will fail # validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH) ) new_password = fields.Str( required=False, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), metadata={"password": True}, ) password_confirm = fields.Str( required=False, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), metadata={"password": True}, ) totp_code = fields.TOTP(required=False)
def getInputSchema(request): # as defined in Marshmallow.schema.from_dict attributes: Dict[str, Union[fields.Field, type]] = {} attributes["name"] = fields.Str(required=True) attributes["surname"] = fields.Str(required=True) attributes["email"] = fields.Email(required=True, label="Username (email address)") attributes["password"] = fields.Str( required=True, password=True, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), ) attributes["password_confirm"] = fields.Str( required=True, password=True, label="Password confirmation", validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), ) if custom_fields := mem.customizer.get_custom_input_fields( request=None, scope=mem.customizer.REGISTRATION): attributes.update(custom_fields)
def get_output_schema(): # as defined in Marshmallow.schema.from_dict attributes: Dict[str, Union[fields.Field, type]] = {} attributes["uuid"] = fields.UUID() attributes["email"] = fields.Email() attributes["name"] = fields.Str() attributes["surname"] = fields.Str() attributes["first_login"] = fields.DateTime(allow_none=True, format=ISO8601UTC) attributes["last_login"] = fields.DateTime(allow_none=True, format=ISO8601UTC) attributes["last_password_change"] = fields.DateTime(allow_none=True, format=ISO8601UTC) attributes["is_active"] = fields.Boolean() attributes["privacy_accepted"] = fields.Boolean() attributes["roles"] = fields.List(fields.Nested(Roles)) attributes["expiration"] = fields.DateTime(allow_none=True, format=ISO8601UTC) attributes["belongs_to"] = fields.Nested(Group, data_key="group") if custom_fields := mem.customizer.get_custom_output_fields(None): attributes.update(custom_fields)
class ProfileActivation(EndpointResource): depends_on = ["MAIN_LOGIN_ENABLE", "ALLOW_REGISTRATION"] baseuri = "/auth" labels = ["base", "profiles"] @decorators.endpoint( path="/profile/activate/<token>", summary="Activate your account by providing the activation token", responses={200: "Account successfully activated"}, ) def put(self, token: str) -> Response: token = token.replace("%2B", ".") token = token.replace("+", ".") try: unpacked_token = self.auth.verify_token( token, raiseErrors=True, token_type=self.auth.ACTIVATE_ACCOUNT) # If token is expired except ExpiredSignatureError: raise BadRequest( "Invalid activation token: this request is expired", ) # if token is not yet active except ImmatureSignatureError: raise BadRequest("Invalid activation token") # if token does not exist (or other generic errors) except BaseException: raise BadRequest("Invalid activation token") user = unpacked_token[3] self.auth.verify_blocked_username(user.email) # Recovering token object from jti jti = unpacked_token[2] token_obj = self.auth.get_tokens(token_jti=jti) # Cannot be tested, this is an extra test to prevent any unauthorized access... # but invalid tokens are already refused above, with auth.verify_token if len(token_obj) == 0: # pragma: no cover raise BadRequest( "Invalid activation token: this request is no longer valid") # If user logged is already active, invalidate the token if user.is_active: self.auth.invalidate_token(token) raise BadRequest( "Invalid activation token: this request is no longer valid") # The activation token is valid, do something user.is_active = True self.auth.save_user(user) # Bye bye token (activation tokens are valid only once) self.auth.invalidate_token(token) self.log_event(self.events.activation, user=user, target=user) return self.response("Account activated") @decorators.use_kwargs({"username": fields.Email(required=True)}) @decorators.endpoint( path="/profile/activate", summary="Ask a new activation link", responses={200: "A new activation link has been sent"}, ) def post(self, username: str) -> Response: self.auth.verify_blocked_username(username) user = self.auth.get_user(username=username) # if user is None this endpoint does nothing but the response # remain the same to prevent any user guessing if user is not None: smtp_client = smtp.get_instance() send_activation_link(smtp_client, self.auth, user) msg = ("We are sending an email to your email address where " "you will find the link to activate your account") return self.response(msg)
class ProfileActivation(EndpointResource): depends_on = ["MAIN_LOGIN_ENABLE", "ALLOW_REGISTRATION", "AUTH_ENABLE"] labels = ["profile"] @decorators.endpoint( path="/auth/profile/activate/<token>", summary="Activate your account by providing the activation token", responses={ 200: "Account successfully activated", 400: "Invalid token", 403: "Account temporarily blocked due to the number of failed logins", }, ) def put(self, token: str) -> Response: token = token.replace("%2B", ".") token = token.replace("+", ".") try: # valid, token, jti, user _, _, jti, user = self.auth.verify_token( token, raiseErrors=True, token_type=self.auth.ACTIVATE_ACCOUNT) # If token is expired except ExpiredSignatureError: raise BadRequest( "Invalid activation token: this request is expired", ) # if token is not active yet except ImmatureSignatureError: raise BadRequest("Invalid activation token") # if token does not exist (or other generic errors) except Exception: raise BadRequest("Invalid activation token") if user is None: # pragma: no cover raise BadRequest("Invalid activation token") self.auth.verify_blocked_username(user.email) # Recovering token object from jti token_obj = self.auth.get_tokens(token_jti=jti) # Cannot be tested, this is an extra test to prevent any unauthorized access... # but invalid tokens are already refused above, with auth.verify_token if len(token_obj) == 0: # pragma: no cover raise BadRequest( "Invalid activation token: this request is no longer valid") # If user logged is already active, invalidate the token if user.is_active: self.auth.invalidate_token(token) raise BadRequest( "Invalid activation token: this request is no longer valid") # The activation token is valid, do something user.is_active = True self.auth.save_user(user) # Bye bye token (activation tokens are valid only once) self.auth.invalidate_token(token) self.log_event(self.events.activation, user=user, target=user) return self.response("Account activated") @decorators.use_kwargs( # This is because Email is not typed on marshmallow {"username": fields.Email(required=True)} # type: ignore ) @decorators.endpoint( path="/auth/profile/activate", summary="Ask a new activation link", responses={ 200: "A new activation link has been sent", 403: "Account temporarily blocked due to the number of failed logins", }, ) def post(self, username: str) -> Response: self.auth.verify_blocked_username(username) user = self.auth.get_user(username=username) # if user is None this endpoint does nothing but the response # remain the same to prevent any user guessing if user is not None: auth = Connector.get_authentication_instance() activation_token, payload = auth.create_temporary_token( user, auth.ACTIVATE_ACCOUNT) server_url = get_frontend_url() rt = activation_token.replace(".", "+") url = f"{server_url}/public/register/{rt}" sent = send_activation_link(user, url) if not sent: # pragma: no cover raise ServiceUnavailable("Error sending email, please retry") auth.save_token(user, activation_token, payload, token_type=auth.ACTIVATE_ACCOUNT) msg = ("We are sending an email to your email address where " "you will find the link to activate your account") return self.response(msg)
class User(Schema): uuid = fields.UUID() email = fields.Email() name = fields.String() surname = fields.String()
class RecoverPassword(EndpointResource): depends_on = [ "MAIN_LOGIN_ENABLE", "ALLOW_PASSWORD_RESET", "AUTH_ENABLE" ] labels = ["authentication"] @decorators.use_kwargs( # This is because Email is not typed on marshmallow {"reset_email": fields.Email(required=True)} # type: ignore ) @decorators.endpoint( path="/auth/reset", summary="Request password reset via email", description="Request password reset via email", responses={ 200: "Reset email is valid", 400: "Invalid reset email", 403: "Account not found or already active", }, ) def post(self, reset_email: str) -> Response: reset_email = reset_email.lower() self.auth.verify_blocked_username(reset_email) user = self.auth.get_user(username=reset_email) if user is None: raise Forbidden( f"Sorry, {reset_email} is not recognized as a valid username", ) self.auth.verify_user_status(user) reset_token, payload = self.auth.create_temporary_token( user, self.auth.PWD_RESET) server_url = get_frontend_url() rt = reset_token.replace(".", "+") uri = Env.get("RESET_PASSWORD_URI", "/public/reset") complete_uri = f"{server_url}{uri}/{rt}" sent = send_password_reset_link(user, complete_uri, reset_email) if not sent: # pragma: no cover raise ServiceUnavailable("Error sending email, please retry") ################## # Completing the reset task self.auth.save_token(user, reset_token, payload, token_type=self.auth.PWD_RESET) msg = "We'll send instructions to the email provided if it's associated " msg += "with an account. Please check your spam/junk folder." self.log_event(self.events.reset_password_request, user=user) return self.response(msg) @decorators.use_kwargs({ "new_password": fields.Str( required=False, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), metadata={"password": True}, ), "password_confirm": fields.Str( required=False, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), metadata={"password": True}, ), }) @decorators.endpoint( path="/auth/reset/<token>", summary="Change password as conseguence of a reset request", description="Change password as conseguence of a reset request", responses={ 200: "Reset token is valid, password changed", 400: "Invalid reset token", }, ) def put( self, token: str, new_password: Optional[str] = None, password_confirm: Optional[str] = None, ) -> Response: token = token.replace("%2B", ".") token = token.replace("+", ".") try: # valid, token, jti, user _, _, jti, user = self.auth.verify_token( token, raiseErrors=True, token_type=self.auth.PWD_RESET) # If token is expired except jwt.exceptions.ExpiredSignatureError: raise BadRequest( "Invalid reset token: this request is expired") # if token is not active yet except jwt.exceptions.ImmatureSignatureError as e: log.info(e) raise BadRequest("Invalid reset token") # if token does not exist (or other generic errors) except Exception as e: log.info(e) raise BadRequest("Invalid reset token") if user is None: # pragma: no cover raise BadRequest("Invalid activation token") # Recovering token object from jti tokens_obj = self.auth.get_tokens(token_jti=jti) # Can't happen because the token is refused from verify_token function if len(tokens_obj) == 0: # pragma: no cover raise BadRequest( "Invalid reset token: this request is no longer valid") token_obj = tokens_obj.pop(0) emitted = token_obj["emitted"] last_change = None # If user logged in after the token emission invalidate the token if user.last_login is not None: last_change = user.last_login # If user changed the pwd after the token emission invalidate the token # Can't happen because the change password also invalidated the token elif user.last_password_change is not None: # pragma: no cover last_change = user.last_password_change if last_change is not None: # Can't happen because the change password also invalidated the token if last_change > emitted: # pragma: no cover self.auth.invalidate_token(token) raise BadRequest( "Invalid reset token: this request is no longer valid", ) # The reset token is valid, do something # No password to be changed, just a token verification if new_password is None and password_confirm is None: return self.empty_response() # Something is missing if new_password is None or password_confirm is None: raise BadRequest("Invalid password") if new_password != password_confirm: raise BadRequest( "New password does not match with confirmation") self.auth.change_password(user, user.password, new_password, password_confirm) # I really don't know why this save is required... since it is already # in change_password ... But if I remove it the new pwd is not saved... self.auth.save_user(user) # Bye bye token (reset tokens are valid only once) self.auth.invalidate_token(token) return self.response("Password changed")
class User(Schema): email = fields.Email() name = fields.Str() surname = fields.Str()
class UserWithUUID(Schema): uuid = fields.UUID() # This is because Email is not typed on marshmallow email = fields.Email() # type: ignore name = fields.String() surname = fields.String()
def admin_user_input(request: FlaskRequest, is_post: bool) -> Type[Schema]: is_admin = HTTPTokenAuth.is_session_user_admin(request, auth) attributes: MarshmallowSchema = {} if is_post: # This is because Email is not typed on marshmallow attributes["email"] = fields.Email( # type: ignore required=is_post, validate=validate.Length(max=100)) attributes["name"] = fields.Str( required=is_post, validate=validate.Length(min=1), metadata={"label": "First Name"}, ) attributes["surname"] = fields.Str( required=is_post, validate=validate.Length(min=1), metadata={"label": "Last Name"}, ) attributes["password"] = fields.Str( required=is_post, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), metadata={"password": True}, ) if Connector.check_availability("smtp"): attributes["email_notification"] = fields.Bool( metadata={"label": "Notify password by email"}) attributes["is_active"] = fields.Bool( dump_default=True, required=False, metadata={"label": "Activate user"}, ) roles = {r.name: r.description for r in auth.get_roles()} if not is_admin and RoleEnum.ADMIN.value in roles: roles.pop(RoleEnum.ADMIN.value) attributes["roles"] = fields.List( fields.Str(validate=validate.OneOf( choices=[r for r in roles.keys()], labels=[r for r in roles.values()], )), dump_default=[auth.default_role], required=False, unique=True, metadata={ "label": "Roles", "description": "", "extra_descriptions": auth.role_descriptions, }, ) group_keys = [] group_labels = [] for g in auth.get_groups(): group_keys.append(g.uuid) group_labels.append(f"{g.shortname} - {g.fullname}") if len(group_keys) == 1: default_group = group_keys[0] else: default_group = None attributes["group"] = fields.Str( required=is_post, dump_default=default_group, validate=validate.OneOf(choices=group_keys, labels=group_labels), metadata={ "label": "Group", "description": "The group to which the user belongs", }, ) attributes["expiration"] = fields.DateTime( required=False, allow_none=True, metadata={ "label": "Account expiration", "description": "This user will be blocked after this date", }, ) if custom_fields := mem.customizer.get_custom_input_fields( request=request, scope=mem.customizer.ADMIN): attributes.update(custom_fields)
def test_responses(self, faker: Faker) -> None: class MySchema(Schema): name = fields.Str() f = "myfield" assert ( ResponseMaker.get_schema_type(f, fields.Str(metadata={"password": True})) == "password" ) assert ResponseMaker.get_schema_type(f, fields.Bool()) == "boolean" assert ResponseMaker.get_schema_type(f, fields.Boolean()) == "boolean" assert ResponseMaker.get_schema_type(f, fields.Date()) == "date" assert ResponseMaker.get_schema_type(f, fields.DateTime()) == "datetime" assert ResponseMaker.get_schema_type(f, fields.AwareDateTime()) == "datetime" assert ResponseMaker.get_schema_type(f, fields.NaiveDateTime()) == "datetime" assert ResponseMaker.get_schema_type(f, fields.Decimal()) == "number" # This is because Email is not typed on marshmallow assert ResponseMaker.get_schema_type(f, fields.Email()) == "email" # type: ignore assert ResponseMaker.get_schema_type(f, fields.Float()) == "number" assert ResponseMaker.get_schema_type(f, fields.Int()) == "int" assert ResponseMaker.get_schema_type(f, fields.Integer()) == "int" assert ResponseMaker.get_schema_type(f, fields.Number()) == "number" assert ResponseMaker.get_schema_type(f, fields.Str()) == "string" assert ResponseMaker.get_schema_type(f, fields.String()) == "string" assert ResponseMaker.get_schema_type(f, fields.Dict()) == "dictionary" assert ResponseMaker.get_schema_type(f, fields.List(fields.Str())) == "string[]" assert ResponseMaker.get_schema_type(f, fields.Nested(MySchema())) == "nested" # Unsupported types, fallback to string assert ResponseMaker.get_schema_type(f, fields.URL()) == "string" assert ResponseMaker.get_schema_type(f, fields.Url()) == "string" assert ResponseMaker.get_schema_type(f, fields.UUID()) == "string" # assert ResponseMaker.get_schema_type(f, fields.Constant("x")) == "string" assert ResponseMaker.get_schema_type(f, fields.Field()) == "string" # assert ResponseMaker.get_schema_type(f, fields.Function()) == "string" # assert ResponseMaker.get_schema_type(f, fields.Mapping()) == "string" # assert ResponseMaker.get_schema_type(f, fields.Method()) == "string" # assert ResponseMaker.get_schema_type(f, fields.Raw()) == "string" # assert ResponseMaker.get_schema_type(f, fields.TimeDelta()) == "string" assert not ResponseMaker.is_binary(None) assert not ResponseMaker.is_binary("") assert not ResponseMaker.is_binary("application/json") assert ResponseMaker.is_binary("application/octet-stream") assert ResponseMaker.is_binary("application/x-bzip") assert ResponseMaker.is_binary("application/x-bzip2") assert ResponseMaker.is_binary("application/pdf") assert ResponseMaker.is_binary("application/msword") assert ResponseMaker.is_binary("application/rtf") assert ResponseMaker.is_binary("application/x-tar") assert ResponseMaker.is_binary("application/gzip") assert ResponseMaker.is_binary("application/zip") assert ResponseMaker.is_binary("application/x-7z-compressed") assert not ResponseMaker.is_binary("text/plain") assert not ResponseMaker.is_binary("text/css") assert not ResponseMaker.is_binary("text/csv") assert not ResponseMaker.is_binary("text/html") assert not ResponseMaker.is_binary("text/javascript") assert not ResponseMaker.is_binary("text/xml") assert ResponseMaker.is_binary("image/gif") assert ResponseMaker.is_binary("image/jpeg") assert ResponseMaker.is_binary("image/png") assert ResponseMaker.is_binary("image/svg+xml") assert ResponseMaker.is_binary("image/tiff") assert ResponseMaker.is_binary("image/webp") assert ResponseMaker.is_binary("image/bmp") assert ResponseMaker.is_binary("image/aac") assert ResponseMaker.is_binary("audio/midi") assert ResponseMaker.is_binary("audio/mpeg") assert ResponseMaker.is_binary("audio/wav") assert ResponseMaker.is_binary("audio/anyother") assert ResponseMaker.is_binary("video/mpeg") assert ResponseMaker.is_binary("video/ogg") assert ResponseMaker.is_binary("video/webm") assert ResponseMaker.is_binary("video/anyother") assert ResponseMaker.is_binary("video/anyother") assert not ResponseMaker.is_binary(faker.pystr()) response = EndpointResource.response("", code=200) assert response[1] == 200 # type: ignore response = EndpointResource.response(None, code=200) assert response[1] == 204 # type: ignore response = EndpointResource.response(None, code=200, head_method=True) assert response[1] == 200 # type: ignore
class User(Schema): # This is because Email is not typed on marshmallow email = fields.Email() # type: ignore name = fields.Str() surname = fields.Str()
def getInputSchema(request, is_post): # as defined in Marshmallow.schema.from_dict attributes: Dict[str, Union[fields.Field, type]] = {} if is_post: attributes["email"] = fields.Email(required=is_post) attributes["name"] = fields.Str(required=is_post, validate=validate.Length(min=1)) attributes["surname"] = fields.Str(required=is_post, validate=validate.Length(min=1)) attributes["password"] = fields.Str( required=is_post, password=True, validate=validate.Length(min=auth.MIN_PASSWORD_LENGTH), ) if Connector.check_availability("smtp"): attributes["email_notification"] = fields.Bool( label="Notify password by email") attributes["is_active"] = fields.Bool(label="Activate user", default=True, required=False) roles = {r.name: r.description for r in auth.get_roles()} attributes["roles"] = AdvancedList( fields.Str(validate=validate.OneOf( choices=[r for r in roles.keys()], labels=[r for r in roles.values()], )), required=False, label="Roles", description="", unique=True, multiple=True, ) group_keys = [] group_labels = [] for g in auth.get_groups(): group_keys.append(g.uuid) group_labels.append(f"{g.shortname} - {g.fullname}") if len(group_keys) == 1: default_group = group_keys[0] else: default_group = None attributes["group"] = fields.Str( label="Group", description="The group to which the user belongs", required=is_post, default=default_group, validate=validate.OneOf(choices=group_keys, labels=group_labels), ) attributes["expiration"] = fields.DateTime( required=False, allow_none=True, label="Account expiration", description="This user will be blocked after this date", ) if custom_fields := mem.customizer.get_custom_input_fields( request=request, scope=mem.customizer.ADMIN): attributes.update(custom_fields)