def createUserWithPassword(email, password, bcrypt, permission=1, cgac_code="SYS"):
    """Convenience function to set up fully-baked user (used for setup/testing only)."""
    sess = GlobalDB.db().session
    status = sess.query(UserStatus).filter(UserStatus.name == 'approved').one()
    user = User(email=email, user_status=status, permission_type_id=permission,
                cgac_code=cgac_code, name='Administrator', title='System Admin')
    user.salt, user.password_hash = getPasswordHash(password, bcrypt)
    sess.add(user)
    sess.commit()

    return user
def create_user_with_password(email, password, bcrypt, website_admin=False):
    """Convenience function to set up fully-baked user (used for setup/testing only)."""
    sess = GlobalDB.db().session
    user = User(email=email,
                name='Administrator',
                title='System Admin',
                website_admin=website_admin)
    user.salt, user.password_hash = get_password_hash(password, bcrypt)
    sess.add(user)
    sess.commit()

    return user
def create_user_with_password(email, password, bcrypt, website_admin=False):
    """Convenience function to set up fully-baked user (used for setup/testing only)."""
    sess = GlobalDB.db().session
    user = User(
        email=email, name='Administrator',
        title='System Admin', website_admin=website_admin
    )
    user.salt, user.password_hash = get_password_hash(password, bcrypt)
    sess.add(user)
    sess.commit()

    return user
    def create_email_confirmation(self,system_email):
        """

        Creates user record and email

        arguments:

        system_email  -- (string) email used to send messages

        """
        sess = GlobalDB.db().session
        request_fields = RequestDictionary.derive(self.request)
        try:
            if 'email' not in request_fields:
                raise ResponseException(
                    "Request body must include email", StatusCode.CLIENT_ERROR)
            email = request_fields['email']
            if not re.match("[^@]+@[^@]+\.[^@]+",email):
                raise ValueError("Invalid Email Format")
        except (ResponseException, ValueError) as exc:
            return JsonResponse.error(exc, StatusCode.CLIENT_ERROR)

        try :
            user = sess.query(User).filter(
                func.lower(User.email) == func.lower(request_fields['email'])
            ).one()
        except NoResultFound:
            # Create user with specified email if none is found
            user = User(email=email)
            user.user_status_id = USER_STATUS_DICT["awaiting_confirmation"]
            user.permissions = 0
            sess.add(user)
            sess.commit()
        else:
            try:
                good_statuses = (USER_STATUS_DICT["awaiting_confirmation"],
                                 USER_STATUS_DICT["email_confirmed"])
                if user.user_status_id not in good_statuses:
                    raise ResponseException(
                        "User already registered", StatusCode.CLIENT_ERROR)
            except ResponseException as exc:
                return JsonResponse.error(exc, exc.status)
        email_token = sesEmail.createToken(email, "validate_email")
        link= "".join([AccountHandler.FRONT_END,'#/registration/',email_token])
        email_template = {'[USER]': email, '[URL]':link}
        new_email = sesEmail(email, system_email,templateType="validate_email",parameters=email_template)
        new_email.send()
        return JsonResponse.create(StatusCode.OK,{"message":"Email Sent"})
Esempio n. 5
0
    def createUser(self, username):
        """ Creates a new entry for new usernames, if a user is found with this email that has not yet registered, just returns that user's ID.  Raises an exception if multiple results found, or if user has already registered.

        Arguments:
        username - username to find or create an id for
        Returns:
        user object
        """
        # Check if user exists
        queryResult = self.session.query(User).options(joinedload("user_status")).filter(User.username == username).all()
        if(len(queryResult) == 1):
            # If so, check their status
            user = queryResult[0]
            if(user.user_status.name == "awaiting_confirmation" or user.user_status.name == "email_confirmed"):
                # User has not yet registered, may restart process
                return user
        elif(len(queryResult) == 0):
            # If not, add new user
            newUser = User(username = username)
            self.session.add(newUser)
            self.session.commit()
            return newUser
        else:
            # Multiple entries for this user, server error
            raise MultipleResultsFound("Multiple entries for single username")
Esempio n. 6
0
    def createUserWithPassword(self,email,password,bcrypt,permission=1,cgac_code="SYS"):
        """ This directly creates a valid user in the database with password and permissions set.  Not used during normal
        behavior of the app, but useful for configuration and testing.

        Arguments:
            email - Email for new user
            password - Password to assign to user
            bcrypt - bcrypt to use for password hashing
            admin - Whether the new user should be an admin
        """
        user = User(email = email)
        self.session.add(user)
        self.setPassword(user,password,bcrypt)
        self.changeStatus(user,"approved")
        self.setPermission(user,permission)
        user.cgac_code = cgac_code
        self.session.commit()
Esempio n. 7
0
    def addUnconfirmedEmail(self,email):
        """ Create user with specified email

        Arguments:
            email - Add user with specified email
        """
        user = User(email = email)
        self.changeStatus(user,"awaiting_confirmation")
        self.setPermission(user,0) # Users start with no permissions
        self.session.add(user)
        self.session.commit()
Esempio n. 8
0
    def setUpClass(cls):
        """Set up class-wide resources like users."""
        super(DomainTests, cls).setUpClass()

        with create_app().app_context():
            sess = GlobalDB.db().session

            user = User()
            r_cgac = CGACFactory()
            w_cgac = CGACFactory()
            s_frec_cgac = CGACFactory()
            s_frec = FRECFactory(cgac=s_frec_cgac)
            e_frec_cgac = CGACFactory()
            e_frec = FRECFactory(cgac=e_frec_cgac)
            f_cgac = CGACFactory()
            user.affiliations = [
                UserAffiliation(cgac=r_cgac,
                                frec=None,
                                permission_type_id=PERMISSION_SHORT_DICT['r']),
                UserAffiliation(cgac=w_cgac,
                                frec=None,
                                permission_type_id=PERMISSION_SHORT_DICT['w']),
                UserAffiliation(cgac=None,
                                frec=s_frec,
                                permission_type_id=PERMISSION_SHORT_DICT['s']),
                UserAffiliation(cgac=None,
                                frec=e_frec,
                                permission_type_id=PERMISSION_SHORT_DICT['e']),
                UserAffiliation(cgac=f_cgac,
                                frec=None,
                                permission_type_id=PERMISSION_SHORT_DICT['f'])
            ]
            sess.add_all([
                r_cgac, w_cgac, s_frec_cgac, s_frec, e_frec_cgac, e_frec,
                f_cgac
            ])
            sess.commit()
    def setUpClass(cls):
        """Set up resources to be shared within a test class"""
        cls.session_id = ""

        with createValidatorApp().app_context():

            # update application's db config options so unittests
            # run against test databases
            suite = cls.__name__.lower()
            config = dataactcore.config.CONFIG_DB
            cls.num = randint(1, 9999)
            config['db_name'] = 'unittest{}_{}_data_broker'.format(
                cls.num, suite)
            dataactcore.config.CONFIG_DB = config
            createDatabase(CONFIG_DB['db_name'])
            runMigrations()

            # drop and re-create test user db/tables
            setupUserDB()
            # drop and re-create test job db/tables
            setupJobTrackerDB()
            # drop and re-create test error db/tables
            setupErrorDB()
            # drop and re-create test validation db/tables
            setupValidationDB()
            # load e-mail templates
            setupEmails()

            # set up default e-mails for tests
            test_users = {}
            test_users['admin_email'] = '*****@*****.**'
            test_users['change_user_email'] = '*****@*****.**'
            test_users['password_reset_email'] = '*****@*****.**'
            test_users['inactive_email'] = '*****@*****.**'
            test_users['password_lock_email'] = '*****@*****.**'
            test_users['expired_lock_email'] = '*****@*****.**'
            test_users['agency_admin_email'] = '*****@*****.**'

            # this email is for a regular agency_user email that is to be used for
            # testing functionality expected by a normal, base user
            test_users['agency_user'] = '******'
            test_users['approved_email'] = '*****@*****.**'
            test_users['submission_email'] = '*****@*****.**'
            user_password = '******'
            admin_password = '******'

            # set up users for status tests
            StatusTestUser = namedtuple(
                'StatusTestUser',
                ['email', 'user_status', 'permissions', 'user_type'])
            StatusTestUser.__new__.__defaults__ = (None, None,
                                                   AccountType.AGENCY_USER,
                                                   None)
            status_test_users = []
            status_test_users.append(
                StatusTestUser('*****@*****.**', 'awaiting_confirmation', 0))
            status_test_users.append(
                StatusTestUser('*****@*****.**', 'email_confirmed'))
            status_test_users.append(
                StatusTestUser('*****@*****.**', 'awaiting_approval'))
            status_test_users.append(
                StatusTestUser('*****@*****.**', 'awaiting_approval'))
            status_test_users.append(
                StatusTestUser('*****@*****.**',
                               'awaiting_approval'))
            status_test_users.append(
                StatusTestUser(
                    test_users['admin_email'], 'approved',
                    AccountType.WEBSITE_ADMIN + AccountType.AGENCY_USER))
            status_test_users.append(
                StatusTestUser(test_users['approved_email'], 'approved'))
            status_test_users.append(
                StatusTestUser('*****@*****.**', 'denied'))

            # add new users
            createUserWithPassword(test_users["submission_email"],
                                   user_password, Bcrypt())
            createUserWithPassword(test_users["change_user_email"],
                                   user_password, Bcrypt())
            createUserWithPassword(test_users["password_reset_email"],
                                   user_password, Bcrypt())
            createUserWithPassword(test_users["inactive_email"], user_password,
                                   Bcrypt())
            createUserWithPassword(test_users["password_lock_email"],
                                   user_password, Bcrypt())
            createUserWithPassword(test_users['expired_lock_email'],
                                   user_password, Bcrypt())
            createUserWithPassword(test_users['agency_admin_email'],
                                   admin_password,
                                   Bcrypt(),
                                   permission=4)
            createUserWithPassword(test_users['agency_user'], user_password,
                                   Bcrypt())

            # get user info and save as class variables for use by tests

            sess = GlobalDB.db().session

            agencyUser = sess.query(User).filter(
                User.email == test_users['agency_user']).one()
            cls.agency_user_id = agencyUser.user_id

            # set the specified account to be expired
            expiredUser = sess.query(User).filter(
                User.email == test_users['expired_lock_email']).one()
            today = parse(time.strftime("%c"))
            expiredUser.last_login_date = (today -
                                           timedelta(days=120)).strftime("%c")
            sess.add(expiredUser)

            # create users for status testing
            for u in status_test_users:
                user = User(email=u.email,
                            permissions=u.permissions,
                            user_status=sess.query(UserStatus).filter(
                                UserStatus.name == u.user_status).one())
                sess.add(user)

            # set up approved user
            user = sess.query(User).filter(
                User.email == test_users['approved_email']).one()
            user.username = "******"
            user.cgac_code = "000"
            user.salt, user.password_hash = getPasswordHash(
                user_password, Bcrypt())
            sess.add(user)
            cls.approved_user_id = user.user_id

            # set up admin user
            admin = sess.query(User).filter(
                User.email == test_users['admin_email']).one()
            admin.salt, admin.password_hash = getPasswordHash(
                admin_password, Bcrypt())
            admin.name = "Mr. Manager"
            admin.cgac_code = "SYS"
            sess.add(admin)

            # set up status changed user
            statusChangedUser = sess.query(User).filter(
                User.email == test_users['change_user_email']).one()
            statusChangedUser.name = "Test User"
            statusChangedUser.user_status = sess.query(UserStatus).filter(
                UserStatus.name == 'email_confirmed').one()
            sess.add(statusChangedUser)
            cls.status_change_user_id = statusChangedUser.user_id

            # set up deactivated user
            deactivated_user = sess.query(User).filter(
                User.email == test_users['inactive_email']).one()
            deactivated_user.last_login_date = time.strftime("%c")
            deactivated_user.is_active = False
            sess.add(deactivated_user)

            sess.commit()

        # get lookup dictionaries
        cls.jobStatusDict = lookups.JOB_STATUS_DICT
        cls.jobTypeDict = lookups.JOB_TYPE_DICT
        cls.fileTypeDict = lookups.FILE_TYPE_DICT
        cls.fileStatusDict = lookups.FILE_STATUS_DICT
        cls.ruleSeverityDict = lookups.RULE_SEVERITY_DICT
        cls.errorTypeDict = lookups.ERROR_TYPE_DICT
        cls.publishStatusDict = lookups.PUBLISH_STATUS_DICT
        cls.userStatusDict = lookups.USER_STATUS_DICT

        # set up info needed by the individual test classes
        cls.test_users = test_users
        cls.user_password = user_password
        cls.admin_password = admin_password
        cls.local = CONFIG_BROKER['local']
Esempio n. 10
0
    def max_login(self, session):
        """ Logs a user in if their password matches using MAX

            Args:
                session: Session object from flask

            Returns:
                A JsonResponse containing the user information or details on which error occurred, such as whether a
                type was wrong, something wasn't implemented, invalid keys were provided, login was denied, or a
                different, unexpected error occurred.
        """
        try:
            safe_dictionary = RequestDictionary(self.request)

            ticket = safe_dictionary.get_value("ticket")
            service = safe_dictionary.get_value('service')

            # Call MAX's serviceValidate endpoint and retrieve the response
            max_dict = get_max_dict(ticket, service)

            if 'cas:authenticationSuccess' not in max_dict[
                    'cas:serviceResponse']:
                raise ValueError(
                    "The Max CAS endpoint was unable to locate your session "
                    "using the ticket/service combination you provided.")
            cas_attrs = max_dict['cas:serviceResponse'][
                'cas:authenticationSuccess']['cas:attributes']

            # Grab MAX ID to see if a service account is being logged in
            max_id_components = cas_attrs['maxAttribute:MAX-ID'].split('_')
            service_account_flag = (len(max_id_components) > 1
                                    and max_id_components[0].lower() == 's')

            # Grab the email and list of groups from MAX's response
            email = cas_attrs['maxAttribute:Email-Address']

            try:
                sess = GlobalDB.db().session
                user = sess.query(User).filter(
                    func.lower(User.email) == func.lower(email)).one_or_none()

                # If the user does not exist, create them since they are allowed to access the site because they got
                # past the above group membership checks
                if user is None:
                    user = User()
                    user.email = email

                set_user_name(user, cas_attrs)

                set_max_perms(user, cas_attrs['maxAttribute:GroupList'],
                              service_account_flag)

                sess.add(user)
                sess.commit()

            except MultipleResultsFound:
                raise ValueError("An error occurred during login.")

            return self.create_session_and_response(session, user)

        # Catch any specifically raised errors or any other errors that may have happened and return them cleanly.
        # We add the error parameter here because this endpoint needs to provide better feedback, and to avoid changing
        # the default behavior of the JsonResponse class globally.
        except (TypeError, KeyError, NotImplementedError) as e:
            # Return a 400 with appropriate message
            return JsonResponse.error(e, StatusCode.CLIENT_ERROR, error=str(e))
        except ValueError as e:
            # Return a 401 for login denied
            return JsonResponse.error(e,
                                      StatusCode.LOGIN_REQUIRED,
                                      error=str(e))
        except Exception as e:
            # Return 500
            return JsonResponse.error(e,
                                      StatusCode.INTERNAL_ERROR,
                                      error=str(e))
    def max_login(self, session):
        """ Logs a user in if their password matches using MAX

            Args:
                session: Session object from flask

            Returns:
                A JsonResponse containing the user information or details on which error occurred, such as whether a
                type was wrong, something wasn't implemented, invalid keys were provided, login was denied, or a
                different, unexpected error occurred.
        """
        try:
            safe_dictionary = RequestDictionary(self.request)

            ticket = safe_dictionary.get_value("ticket")
            service = safe_dictionary.get_value('service')

            # Call MAX's serviceValidate endpoint and retrieve the response
            max_dict = get_max_dict(ticket, service)

            if 'cas:authenticationSuccess' not in max_dict['cas:serviceResponse']:
                raise ValueError("The Max CAS endpoint was unable to locate your session "
                                 "using the ticket/service combination you provided.")
            cas_attrs = max_dict['cas:serviceResponse']['cas:authenticationSuccess']['cas:attributes']

            # Grab MAX ID to see if a service account is being logged in
            max_id_components = cas_attrs['maxAttribute:MAX-ID'].split('_')
            service_account_flag = (len(max_id_components) > 1 and max_id_components[0].lower() == 's')

            # Grab the email and list of groups from MAX's response
            email = cas_attrs['maxAttribute:Email-Address']

            try:
                sess = GlobalDB.db().session
                user = sess.query(User).filter(func.lower(User.email) == func.lower(email)).one_or_none()

                # If the user does not exist, create them since they are allowed to access the site because they got
                # past the above group membership checks
                if user is None:
                    user = User()
                    user.email = email

                set_user_name(user, cas_attrs)

                set_max_perms(user, cas_attrs['maxAttribute:GroupList'], service_account_flag)

                sess.add(user)
                sess.commit()

            except MultipleResultsFound:
                raise ValueError("An error occurred during login.")

            return self.create_session_and_response(session, user)

        # Catch any specifically raised errors or any other errors that may have happened and return them cleanly.
        # We add the error parameter here because this endpoint needs to provide better feedback, and to avoid changing
        # the default behavior of the JsonResponse class globally.
        except (TypeError, KeyError, NotImplementedError) as e:
            # Return a 400 with appropriate message
            return JsonResponse.error(e, StatusCode.CLIENT_ERROR, error=str(e))
        except ValueError as e:
            # Return a 401 for login denied
            return JsonResponse.error(e, StatusCode.LOGIN_REQUIRED, error=str(e))
        except Exception as e:
            # Return 500
            return JsonResponse.error(e, StatusCode.INTERNAL_ERROR, error=str(e))
    def max_login(self,session):
        """

        Logs a user in if their password matches

        arguments:

        session  -- (Session) object from flask

        return the reponse object

        """
        try:
            safeDictionary = RequestDictionary(self.request)

            # Obtain POST content
            ticket = safeDictionary.getValue("ticket")
            service = safeDictionary.getValue('service')
            parent_group = CONFIG_BROKER['parent_group']

            # Call MAX's serviceValidate endpoint and retrieve the response
            max_dict = self.get_max_dict(ticket, service)

            if not 'cas:authenticationSuccess' in max_dict['cas:serviceResponse']:
                raise ValueError("You have failed to login successfully with MAX")

            # Grab the email and list of groups from MAX's response
            email = max_dict['cas:serviceResponse']['cas:authenticationSuccess']['cas:attributes']['maxAttribute:Email-Address']
            group_list_all = max_dict['cas:serviceResponse']['cas:authenticationSuccess']['cas:attributes']['maxAttribute:GroupList'].split(',')
            group_list = [g for g in group_list_all if g.startswith(parent_group)]

            cgac_group = [g for g in group_list if g.startswith(parent_group+"-CGAC_")]

            # Deny access if they are not aligned with an agency
            if not cgac_group:
                raise ValueError("You have logged in with MAX but do not have permission to access the broker.")

            try:
                sess = GlobalDB.db().session
                user = sess.query(User).filter(func.lower(User.email) == func.lower(email)).one_or_none()

                # If the user does not exist, create them since they are allowed to access the site because they got
                # past the above group membership checks
                if user is None:
                    user = User()

                    first_name = max_dict["cas:serviceResponse"]['cas:authenticationSuccess']['cas:attributes'][
                        'maxAttribute:First-Name']
                    middle_name = max_dict["cas:serviceResponse"]['cas:authenticationSuccess']['cas:attributes'][
                        'maxAttribute:Middle-Name']
                    last_name = max_dict["cas:serviceResponse"]['cas:authenticationSuccess']['cas:attributes'][
                        'maxAttribute:Last-Name']

                    user.email = email

                    # Check for None first so the condition can short-circuit without
                    # having to worry about calling strip() on a None object
                    if middle_name is None or middle_name.strip() == '':
                        user.name = first_name + " " + last_name
                    else:
                        user.name = first_name + " " + middle_name[0] + ". " + last_name
                    user.user_status_id = user.user_status_id = USER_STATUS_DICT['approved']

                    sess.add(user)
                    sess.commit()

                # update user's cgac based on their current membership
                # If part of the SYS agency, use that as the cgac otherwise use the first agency provided
                if [g for g in cgac_group if g.endswith("SYS")]:
                    user.cgac_code = "SYS"
                else:
                    user.cgac_code = cgac_group[0][-3:]

                self.grant_highest_permission(sess, user, group_list, cgac_group[0])

            except MultipleResultsFound:
                raise ValueError("An error occurred during login.")

            return self.create_session_and_response(session, user)

        except (TypeError, KeyError, NotImplementedError) as e:
            # Return a 400 with appropriate message
            return JsonResponse.error(e,StatusCode.CLIENT_ERROR)
        except ValueError as e:
            # Return a 401 for login denied
            return JsonResponse.error(e,StatusCode.LOGIN_REQUIRED)
        except Exception as e:
            # Return 500
            return JsonResponse.error(e,StatusCode.INTERNAL_ERROR)
        return self.response
    def max_login(self, session):
        """

        Logs a user in if their password matches

        arguments:

        session  -- (Session) object from flask

        return the response object

        """
        try:
            safe_dictionary = RequestDictionary(self.request)

            # Obtain POST content
            ticket = safe_dictionary.get_value("ticket")
            service = safe_dictionary.get_value('service')

            # Call MAX's serviceValidate endpoint and retrieve the response
            max_dict = get_max_dict(ticket, service)

            if 'cas:authenticationSuccess' not in max_dict['cas:serviceResponse']:
                raise ValueError("You have failed to login successfully with MAX")
            cas_attrs = max_dict['cas:serviceResponse']['cas:authenticationSuccess']['cas:attributes']

            # Grab the email and list of groups from MAX's response
            email = cas_attrs['maxAttribute:Email-Address']

            try:
                sess = GlobalDB.db().session
                user = sess.query(User).filter(func.lower(User.email) == func.lower(email)).one_or_none()

                # If the user does not exist, create them since they are allowed to access the site because they got
                # past the above group membership checks
                if user is None:
                    user = User()

                    first_name = cas_attrs['maxAttribute:First-Name']
                    middle_name = cas_attrs['maxAttribute:Middle-Name']
                    last_name = cas_attrs['maxAttribute:Last-Name']

                    user.email = email

                    # Check for None first so the condition can short-circuit without
                    # having to worry about calling strip() on a None object
                    if middle_name is None or middle_name.strip() == '':
                        user.name = first_name + " " + last_name
                    else:
                        user.name = first_name + " " + middle_name[0] + ". " + last_name

                set_max_perms(user, cas_attrs['maxAttribute:GroupList'])

                sess.add(user)
                sess.commit()

            except MultipleResultsFound:
                raise ValueError("An error occurred during login.")

            return self.create_session_and_response(session, user)

        except (TypeError, KeyError, NotImplementedError) as e:
            # Return a 400 with appropriate message
            return JsonResponse.error(e, StatusCode.CLIENT_ERROR)
        except ValueError as e:
            # Return a 401 for login denied
            return JsonResponse.error(e, StatusCode.LOGIN_REQUIRED)
        except Exception as e:
            # Return 500
            return JsonResponse.error(e, StatusCode.INTERNAL_ERROR)
    def max_login(self, session):
        """

        Logs a user in if their password matches

        arguments:

        session  -- (Session) object from flask

        return the response object

        """
        try:
            safe_dictionary = RequestDictionary(self.request)

            # Obtain POST content
            ticket = safe_dictionary.get_value("ticket")
            service = safe_dictionary.get_value('service')

            # Call MAX's serviceValidate endpoint and retrieve the response
            max_dict = get_max_dict(ticket, service)

            if 'cas:authenticationSuccess' not in max_dict[
                    'cas:serviceResponse']:
                raise ValueError(
                    "You have failed to login successfully with MAX")
            cas_attrs = max_dict['cas:serviceResponse'][
                'cas:authenticationSuccess']['cas:attributes']

            # Grab the email and list of groups from MAX's response
            email = cas_attrs['maxAttribute:Email-Address']

            try:
                sess = GlobalDB.db().session
                user = sess.query(User).filter(
                    func.lower(User.email) == func.lower(email)).one_or_none()

                # If the user does not exist, create them since they are allowed to access the site because they got
                # past the above group membership checks
                if user is None:
                    user = User()

                    first_name = cas_attrs['maxAttribute:First-Name']
                    middle_name = cas_attrs['maxAttribute:Middle-Name']
                    last_name = cas_attrs['maxAttribute:Last-Name']

                    user.email = email

                    # Check for None first so the condition can short-circuit without
                    # having to worry about calling strip() on a None object
                    if middle_name is None or middle_name.strip() == '':
                        user.name = first_name + " " + last_name
                    else:
                        user.name = first_name + " " + middle_name[
                            0] + ". " + last_name

                set_max_perms(user, cas_attrs['maxAttribute:GroupList'])

                sess.add(user)
                sess.commit()

            except MultipleResultsFound:
                raise ValueError("An error occurred during login.")

            return self.create_session_and_response(session, user)

        except (TypeError, KeyError, NotImplementedError) as e:
            # Return a 400 with appropriate message
            return JsonResponse.error(e, StatusCode.CLIENT_ERROR)
        except ValueError as e:
            # Return a 401 for login denied
            return JsonResponse.error(e, StatusCode.LOGIN_REQUIRED)
        except Exception as e:
            # Return 500
            return JsonResponse.error(e, StatusCode.INTERNAL_ERROR)