def require_auth(): if request.method == "OPTIONS": return auth = request.headers.get("Authorization") g.auth_user = None if auth and auth.startswith("Bearer "): token = auth.split(" ", 1)[1] g.auth_user = User.verify_session_token(token) # Not a session token? Maybe APIKey token if g.auth_user is None: g.auth_user = APIKey.verify_token(token) # Still nothing? Maybe legacy API key if g.auth_user is None: g.auth_user = User.verify_legacy_token(token) if g.auth_user is not None: getLogger().warning( "'%s' used legacy auth token for authentication", g.auth_user.login) if g.auth_user: if (app_config.mwdb.enable_maintenance and g.auth_user.login != app_config.mwdb.admin_login): raise Forbidden("Maintenance underway. Please come back later.") if g.auth_user.disabled: raise Forbidden("User has been disabled.")
def require_auth(): if request.method == "OPTIONS": return auth = request.headers.get("Authorization") g.auth_user = None if auth and auth.startswith("Bearer "): token = auth.split(" ", 1)[1] g.auth_user = User.verify_session_token(token) # Not a session token? Maybe APIKey token if g.auth_user is None: g.auth_user = APIKey.verify_token(token) # Still nothing? Maybe legacy API key if g.auth_user is None: g.auth_user = User.verify_legacy_token(token) if g.auth_user is not None: getLogger().warning( "'%s' used legacy auth token for authentication", g.auth_user.login ) if g.auth_user: if ( app_config.mwdb.enable_maintenance and g.auth_user.login != app_config.mwdb.admin_login ): raise Forbidden("Maintenance underway. Please come back later.") if g.auth_user.disabled: raise Forbidden("User has been disabled.") if app_config.mwdb.enable_rate_limit and not g.auth_user.has_rights( Capabilities.unlimited_requests ): """ Single sample view in malwarefront generates 7 requests (6 GET, 1 POST) """ if request.method == "GET": """ DownloadResource is token-based and shouldn't be limited """ if request.endpoint != api.endpoint_for(DownloadResource): # 1000 per 10 seconds rate_limit("get-request", 10, 1000) # 2000 per 1 minute rate_limit("get-request", 60, 2000) # 6000 per 5 minutes rate_limit("get-request", 5 * 60, 6000) # 10000 per 15 minutes rate_limit("get-request", 15 * 60, 10000) else: # 10 per 10 seconds rate_limit("set-request", 10, 10) # 30 per 1 minute rate_limit("set-request", 60, 30) # 100 per 5 minutes rate_limit("set-request", 5 * 60, 100) # 200 per 15 minutes rate_limit("set-request", 15 * 60, 200)
def post(self): """ --- summary: Request a new user account description: Creates a new pending user account. tags: - auth requestBody: description: User basic information content: application/json: schema: AuthRegisterRequestSchema responses: 200: description: User login on successful registration. content: application/json: schema: UserSuccessResponseSchema 400: description: When request body is invalid. 403: description: When registration feature is disabled or reCAPTCHA token wasn't valid. 409: description: When user login or group name already exists. 500: description: When ReCAPTCHA verification service is unavailable. """ if not app_config.mwdb.enable_registration: raise Forbidden("User registration is not enabled.") schema = AuthRegisterRequestSchema() obj = loads_schema(request.get_data(as_text=True), schema) login = obj["login"] if db.session.query(exists().where(User.login == login)).scalar(): raise Conflict("Name already exists") if db.session.query(exists().where(Group.name == login)).scalar(): raise Conflict("Name already exists") verify_recaptcha(obj.get("recaptcha")) user = User.create(login, obj["email"], obj["additional_info"], pending=True) try: send_email_notification("pending", "Pending registration in MWDB", user.email, base_url=app_config.mwdb.base_url, login=user.login) except MailError: logger.exception("Can't send e-mail notification") logger.info('User registered', extra={'user': user.login}) schema = UserSuccessResponseSchema() return schema.dump({"login": user.login})
def _initialize(admin_password): """ Creates initial objects in database """ public_group = Group(name=Group.PUBLIC_GROUP_NAME, capabilities=[]) db.session.add(public_group) everything_group = Group(name=Group.EVERYTHING_GROUP_NAME, capabilities=[Capabilities.access_all_objects]) db.session.add(everything_group) admin_group = Group(name=app_config.mwdb.admin_login, capabilities=Capabilities.all(), private=True) db.session.add(admin_group) admin_user = User( login=app_config.mwdb.admin_login, email="*****@*****.**", additional_info="MWDB built-in administrator account", groups=[admin_group, everything_group, public_group], ) admin_user.reset_sessions() admin_user.set_password(admin_password) db.session.add(admin_user) db.session.commit()
def post(self): """ --- summary: Set a new password for user description: Sets a new password for user using password change token. tags: - auth requestBody: description: | User set password token and new password. Password must be longer than 8 chars and shorter than 72 UTF-8 encoded bytes. content: application/json: schema: AuthSetPasswordRequestSchema responses: 200: description: User login on successful password set content: application/json: schema: UserSuccessResponseSchema 400: description: | When request body is invalid or provided password doesn't match the policy 403: description: When set password token is no longer valid 503: description: | Request canceled due to database statement timeout. """ schema = AuthSetPasswordRequestSchema() obj = loads_schema(request.get_data(as_text=True), schema) user = User.verify_set_password_token(obj["token"]) if user is None: raise Forbidden("Set password token expired") user.set_password(password=obj["password"]) db.session.commit() logger.info("Password changed", extra={"user": user.login}) schema = UserSuccessResponseSchema() return schema.dump({"login": user.login})
def _initialize(admin_password): """ Creates initial objects in database """ public_group = Group(name=Group.PUBLIC_GROUP_NAME, capabilities=[], workspace=False, default=True) db.session.add(public_group) everything_group = Group( name=Group.DEFAULT_EVERYTHING_GROUP_NAME, capabilities=[Capabilities.access_all_objects], workspace=False, ) db.session.add(everything_group) registered_group = Group( name=Group.DEFAULT_REGISTERED_GROUP_NAME, capabilities=[ Capabilities.adding_files, Capabilities.manage_profile, Capabilities.personalize, ], workspace=False, default=True, ) db.session.add(registered_group) admin_group = Group(name=app_config.mwdb.admin_login, capabilities=Capabilities.all(), private=True) db.session.add(admin_group) admin_user = User( login=app_config.mwdb.admin_login, email="*****@*****.**", additional_info="MWDB built-in administrator account", groups=[admin_group, everything_group, public_group, registered_group], ) admin_user.reset_sessions() admin_user.set_password(admin_password) db.session.add(admin_user) db.session.commit()
def post(self, provider_name): provider = (db.session.query(OpenIDProvider).filter( OpenIDProvider.name == provider_name).first()) if not provider: raise NotFound( f"Requested provider name '{provider_name}' not found") schema = OpenIDAuthorizeRequestSchema() obj = loads_schema(request.get_data(as_text=True), schema) redirect_uri = f"{app_config.mwdb.base_url}/oauth/callback" userinfo = provider.fetch_id_token(obj["code"], obj["state"], obj["nonce"], redirect_uri) # register user with information from provider if db.session.query(exists().where( and_( OpenIDUserIdentity.provider_id == provider.id, OpenIDUserIdentity.sub_id == userinfo["sub"], ))).scalar(): raise Conflict("User is already bound with selected provider.") login_claims = ["preferred_username", "nickname", "name"] for claim in login_claims: username = userinfo.get(claim) if not username: continue try: UserLoginSchemaBase().load({"login": username}) except ValidationError: continue already_exists = db.session.query( exists().where(Group.name == username)).scalar() if not already_exists: break # If no candidates in claims: try fallback login else: # If no candidates in claims: try fallback login sub_md5 = hashlib.md5( userinfo["sub"].encode("utf-8")).hexdigest()[:8] username = f"{provider_name}-{sub_md5}" if "email" in userinfo.keys(): user_email = userinfo["email"] else: user_email = f'{userinfo["sub"]}@mwdb.local' user = User.create( username, user_email, "Registered via OpenID Connect protocol", ) identity = OpenIDUserIdentity(sub_id=userinfo["sub"], provider_id=provider.id, user_id=user.id) db.session.add(identity) user.logged_on = datetime.datetime.now() db.session.commit() auth_token = user.generate_session_token() user_private_group = next( (g for g in user.groups if g.name == user.login), None) hooks.on_created_user(user) if user_private_group: hooks.on_created_group(user_private_group) logger.info( "User logged in via OpenID Provider", extra={ "login": user.login, "provider": provider_name }, ) schema = AuthSuccessResponseSchema() return schema.dump({ "login": user.login, "token": auth_token, "capabilities": user.capabilities, "groups": user.group_names, })
def post(self, login): """ --- summary: Create a new user description: | Creates new user account Requires `manage_users` capability. security: - bearerAuth: [] tags: - user parameters: - in: path name: login schema: type: string description: New user login requestBody: description: User information content: application/json: schema: UserCreateRequestSchema responses: 200: description: When user was created successfully content: application/json: schema: UserSuccessResponseSchema 400: description: When request body is invalid 403: description: When user doesn't have `manage_users` capability. 409: description: When user or group with provided name already exists. 500: description: | When SMTP server is unavailable or not properly configured on the server. """ schema = UserCreateRequestSchema() obj = loads_schema(request.get_data(as_text=True), schema) user_login_obj = load_schema({"login": login}, UserLoginSchemaBase()) if db.session.query(exists().where( User.login == user_login_obj["login"])).scalar(): raise Conflict("User exists yet") if db.session.query(exists().where( Group.name == user_login_obj["login"])).scalar(): raise Conflict("Group exists yet") user = User.create( user_login_obj["login"], obj["email"], obj["additional_info"], pending=False, feed_quality=obj["feed_quality"], ) if obj["send_email"]: try: send_email_notification( "register", "New account registered in MWDB", user.email, base_url=app_config.mwdb.base_url, login=user.login, set_password_token=user.generate_set_password_token(). decode("utf-8"), ) except MailError: logger.exception("Can't send e-mail notification") raise InternalServerError( "SMTP server needed to fulfill this request is" " not configured or unavailable.") db.session.commit() logger.info("User created", extra={"user": user.login}) schema = UserSuccessResponseSchema() return schema.dump({"login": user.login})