예제 #1
0
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.")
예제 #2
0
파일: app.py 프로젝트: KWMORALE/mwdb-core
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)
예제 #3
0
파일: auth.py 프로젝트: sarulon/mwdb-core
    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})
예제 #4
0
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()
예제 #5
0
    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})
예제 #6
0
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()
예제 #7
0
    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,
        })
예제 #8
0
    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})