def test_post_user_profiles_min_fail(app, mocker): expected_status = 400 expected_json = {'error': { 'first_name': ["Value must be between 1 and 40 characters long."], 'last_name': ["Value must be between 2 and 40 characters long."], }} request_mock = mocker.patch('modules.user_profiles.routes_admin.request') request_mock.json = { 'user_id': 9, 'first_name': "", 'last_name': "A", 'joined_at': "2019-02-04T00:00:00+0000", 'status': UserProfile.STATUS_ENABLED } query_mock = mocker.patch('flask_sqlalchemy._QueryProperty.__get__') # mock exists() validation user_9 = User() user_9.id = 9 query_mock.return_value \ .get.return_value = user_9 result = post_user_profiles() assert result[1] == expected_status assert result[0].json == expected_json
def test_post_user_profiles_max_fail(app, mocker): expected_status = 400 expected_json = {'error': { 'first_name': ["Value must be between 1 and 40 characters long."], 'last_name': ["Value must be between 2 and 40 characters long."], }} request_mock = mocker.patch('modules.user_profiles.routes_admin.request') request_mock.json = { 'user_id': 9, 'first_name': "LRUNzhfsbfrfZ4BT9N6R3SNYVfAAuQdQdTSmrwFew", 'last_name': "z3Sytm4QrL8g7J4kgugEABnhwXZAnCZmrngUCeeXm", 'joined_at': "2019-02-04T00:00:00+0000", 'status': UserProfile.STATUS_ENABLED } query_mock = mocker.patch('flask_sqlalchemy._QueryProperty.__get__') # mock exists() validation user_9 = User() user_9.id = 9 query_mock.return_value \ .get.return_value = user_9 result = post_user_profiles() assert result[1] == expected_status assert result[0].json == expected_json
def test_put_user_profile_max_fail(app, mocker): expected_status = 400 expected_json = {'error': { 'first_name': ["Value must be between 1 and 40 characters long."], 'last_name': ["Value must be between 2 and 40 characters long."], }} request_mock = mocker.patch('modules.user_profiles.routes_admin.request') request_mock.json = { 'user_id': 9, 'first_name': "pSJxpg6GC2qRnekNVDKMkYqNqAbd7X5UzsKuhVzf4", 'last_name': "J5ATwnHEfD5YqSQNTDcb9bFbaD6ZRZvL3b9ugjyUK", 'joined_at': "2018-12-09T08:00:00+0000", 'status': UserProfile.STATUS_DISABLED } user_profile_2 = UserProfile() user_profile_2.id = 2 user_9 = User() user_9.id = 9 query_mock = mocker.patch('flask_sqlalchemy._QueryProperty.__get__') # mock initial resource query and exists() validation query_mock.return_value \ .get.side_effect = [user_profile_2, user_9] result = put_user_profile(2) assert result[1] == expected_status assert result[0].json == expected_json
def test_put_user_profile_ok(app, mocker): expected_status = 200 expected_m_length = 9 expected_m_id = 2 expected_m_user_id = 9 expected_m_first_name = "LynneA" expected_m_last_name = "HarfordA" expected_m_joined_at = "2018-12-09T08:00:00+0000" expected_m_status = UserProfile.STATUS_DISABLED expected_m_created_at = None expected_m_updated_at = None # @todo: timezone re_datetime = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$") request_mock = mocker.patch('modules.user_profiles.routes_admin.request') request_mock.json = { 'user_id': expected_m_user_id, 'first_name': expected_m_first_name, 'last_name': expected_m_last_name, 'joined_at': expected_m_joined_at, 'status': expected_m_status, } user_profile_2 = UserProfile() user_profile_2.id = expected_m_id user_9 = User() user_9.id = 9 query_mock = mocker.patch('flask_sqlalchemy._QueryProperty.__get__') # mock initial resource query and exists() validation query_mock.return_value \ .get.side_effect = [user_profile_2, user_9] db_mock = mocker.patch('modules.user_profiles.routes_admin.db') db_mock.commit.return_value = None result = put_user_profile(expected_m_id) assert result[1] == expected_status assert 'user_profile' in result[0].json assert len(result[0].json['user_profile']) == expected_m_length assert result[0].json['user_profile']['id'] == expected_m_id assert result[0].json['user_profile']['user_id'] == expected_m_user_id assert result[0].json['user_profile']['first_name'] == \ expected_m_first_name assert result[0].json['user_profile']['last_name'] == expected_m_last_name assert result[0].json['user_profile']['joined_at'] == expected_m_joined_at assert result[0].json['user_profile']['status'] == expected_m_status assert bool(re_datetime.match( result[0].json['user_profile']['status_changed_at'])) assert result[0].json['user_profile']['created_at'] == \ expected_m_created_at assert result[0].json['user_profile']['updated_at'] == \ expected_m_updated_at
def test_user_auth_token_pass(app, mocker): user1 = User() user1.id = 1 token = user1.generate_auth_token() # mock db query query_mock = mocker.patch('flask_sqlalchemy._QueryProperty.__get__') query_mock.return_value \ .get.return_value = user1 assert User.verify_auth_token(token)
def test_post_user_profile_ok(app, mocker): expected_status = 201 expected_m_length = 9 expected_m_id = None expected_m_user_id = 9 expected_m_first_name = "Service" expected_m_last_name = "Account" expected_m_joined_at = "2019-02-04T00:00:00+0000" expected_m_status = UserProfile.STATUS_ENABLED expected_m_created_at = None expected_m_updated_at = None # @todo: timezone re_datetime = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$") request_mock = mocker.patch('modules.user_profiles.routes_admin.request') request_mock.json = { 'user_id': expected_m_user_id, 'first_name': expected_m_first_name, 'last_name': expected_m_last_name, 'joined_at': expected_m_joined_at, 'status': expected_m_status, } query_mock = mocker.patch('flask_sqlalchemy._QueryProperty.__get__') # mock exists() validation user_9 = User() user_9.id = 9 query_mock.return_value \ .get.return_value = user_9 db_mock = mocker.patch('modules.user_profiles.routes_admin.db') db_mock.add.return_value = None db_mock.commit.return_value = None result = post_user_profiles() assert result[1] == expected_status assert 'user_profile' in result[0].json assert len(result[0].json['user_profile']) == expected_m_length assert result[0].json['user_profile']['id'] == expected_m_id assert result[0].json['user_profile']['user_id'] == expected_m_user_id assert result[0].json['user_profile']['first_name'] == \ expected_m_first_name assert result[0].json['user_profile']['last_name'] == expected_m_last_name assert result[0].json['user_profile']['joined_at'] == expected_m_joined_at assert result[0].json['user_profile']['status'] == expected_m_status assert bool(re_datetime.match( result[0].json['user_profile']['status_changed_at'])) assert result[0].json['user_profile']['created_at'] == \ expected_m_created_at assert result[0].json['user_profile']['updated_at'] == \ expected_m_updated_at
def test_get_auth_token_ok(app, mocker): expected_status = 200 expected_r_expiration = 14400 expected_r_user_id = 2 expected_r_username = "******" user2 = User() user2.id = 2 user2.username = '******' g_mock = mocker.patch('modules.users.routes_auth.g') g_mock.user = user2 result = get_auth_token() assert result[1] == expected_status assert result[0].json['expiration'] == expected_r_expiration assert result[0].json['user_id'] == expected_r_user_id assert result[0].json['username'] == expected_r_username assert 'token' in result[0].json
def test_get_auth_token_route_ok(app, mocker, client): expected_status = 200 expected_r_expiration = 14400 expected_r_user_id = 2 expected_r_username = "******" # mock db query query_mock = mocker.patch('flask_sqlalchemy._QueryProperty.__get__') # mock app key authorization db query query_mock.return_value \ .filter.return_value \ .one.return_value = AppKey() role1 = Role() role1.id = 1 role1.name = 'USER' role1.password_reset_days = 365 role1.password_policy = True role1.password_reuse_history = 10 user2 = User() user2.id = 2 user2.username = expected_r_username user2.password = "******" user2.roles = [role1] # mock user login db query query_mock.return_value \ .filter.return_value \ .first.return_value = user2 db_mock = mocker.patch('modules.users.authentication.db') db_mock.add.return_value = None db_mock.commit.return_value = None # mock user login auth_mock = mocker.patch('modules.users.Authentication.is_account_locked') auth_mock.return_value = False credentials = base64.b64encode( 'user2:user2pass'.encode('ascii')).decode('utf-8') response = client.get("/token?app_key=123", headers={"Authorization": f"Basic {credentials}"}) assert response.status_code == expected_status assert response.json['expiration'] == expected_r_expiration assert response.json['user_id'] == expected_r_user_id assert response.json['username'] == expected_r_username assert 'token' in response.json
def test_post_user_route_ok(app, mocker, client): expected_status = 201 expected_m_length = 9 expected_m_id = None expected_m_user_id = 9 expected_m_first_name = "Service" expected_m_last_name = "Account" expected_m_joined_at = "2019-02-04T00:00:00+0000" expected_m_status = UserProfile.STATUS_ENABLED expected_m_created_at = None expected_m_updated_at = None # @todo: timezone re_datetime = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$") data = { 'user_id': expected_m_user_id, 'first_name': expected_m_first_name, 'last_name': expected_m_last_name, 'joined_at': expected_m_joined_at, 'status': expected_m_status, } query_mock = mocker.patch('flask_sqlalchemy._QueryProperty.__get__') # mock app key authorization db query query_mock.return_value \ .filter.return_value \ .one.return_value = AppKey() # mock user login db query role2 = Role() role2.id = 2 role2.name = 'SUPER_ADMIN' role2.password_reset_days = 365 admin1 = Administrator() admin1.id = 1 admin1.password = '******' admin1.roles = [role2] query_mock.return_value \ .filter.return_value \ .first.return_value = admin1 auth_db_mock = mocker.patch('modules.administrators.authentication.db') auth_db_mock.add.return_value = None auth_db_mock.commit.return_value = None # mock exists() validation user_9 = User() user_9.id = 9 query_mock.return_value \ .get.return_value = user_9 db_mock = mocker.patch('modules.user_profiles.routes_admin.db') db_mock.add.return_value = None db_mock.commit.return_value = None # mock user login auth_mock = mocker.patch( 'modules.administrators.Authentication.is_account_locked') auth_mock.return_value = False credentials = base64.b64encode( 'admin1:admin1pass'.encode('ascii')).decode('utf-8') response = client.post( "/user_profiles?app_key=123", json=data, headers={"Authorization": f"Basic {credentials}"}) assert response.status_code == expected_status assert 'user_profile' in response.json assert len(response.json['user_profile']) == expected_m_length assert response.json['user_profile']['id'] == expected_m_id assert response.json['user_profile']['user_id'] == expected_m_user_id assert response.json['user_profile']['first_name'] == \ expected_m_first_name assert response.json['user_profile']['last_name'] == expected_m_last_name assert response.json['user_profile']['joined_at'] == expected_m_joined_at assert response.json['user_profile']['status'] == expected_m_status assert bool(re_datetime.match( response.json['user_profile']['status_changed_at'])) assert response.json['user_profile']['created_at'] == \ expected_m_created_at assert response.json['user_profile']['updated_at'] == \ expected_m_updated_at
def test_user_auth_token_fail(app): assert not User.verify_auth_token('badtoken')
def test_user_check_password_fail(app): user = User() user.password = '******' assert not user.check_password('testPass2')
def test_user_check_password_pass(app): user = User() user.password = '******' assert user.check_password('testPass1')
def put_password_reset(): """Updates the current user's password using a reset code. :returns: JSON string of a `true` value; status code :rtype: (str, int) """ # pylint: disable=too-many-branches # initialize user user = None # prep regex re_password = re.compile(UserAccountSchema.re_password) # validate data errors = {} if 'code' not in request.json or not request.json['code']: if 'code' not in errors: errors['code'] = [] errors['code'].append("Missing data for required field.") if 'email' not in request.json or not request.json['email']: if 'email' not in errors: errors['email'] = [] errors['email'].append("Missing data for required field.") if request.json.get('email'): temp_user = User(email=request.json.get('email')) if temp_user: user = User.query.filter( User.status == User.STATUS_ENABLED, User.roles.any(Role.id == 1), User.email_digest == temp_user.email_digest).first() if not user: if 'email' not in errors: errors['email'] = [] errors['email'].append("Email address not found.") if user and request.json.get('code'): password_reset = PasswordReset.query.filter( PasswordReset.status == PasswordReset.STATUS_ENABLED, PasswordReset.code == request.json.get('code'), PasswordReset.user_id == user.id, PasswordReset.is_used == False, # noqa; pylint: disable=singleton-comparison (PasswordReset.requested_at + timedelta(seconds=3600)) >= datetime.now()).first() if not password_reset: if 'code' not in errors: errors['code'] = [] errors['code'].append("Invalid reset code.") if 'password1' not in request.json or not request.json['password1']: if 'password1' not in errors: errors['password1'] = [] errors['password1'].append("Missing data for required field.") if ('password1' in request.json and not re_password.match(request.json['password1'])): if 'password1' not in errors: errors['password1'] = [] errors['password1'].append("Please choose a more complex password.") if 'password2' not in request.json or not request.json['password2']: if 'password2' not in errors: errors['password2'] = [] errors['password2'].append("Missing data for required field.") if 'password1' in request.json and 'password2' in request.json: if request.json['password1'] != request.json['password2']: if 'password2' not in errors: errors['password2'] = [] errors['password2'].append("New passwords must match.") if errors: return jsonify({"error": errors}), 400 # save password reset record password_reset.is_used = True # save user user.password = request.json.get('password1') db.session.commit() # response return jsonify({'success': 'true'}), 200
def post_password_request_reset_code(): """Creates a password reset code for the current user, send via email. :returns: JSON string of a `true` value; status code :rtype: (str, int) """ # initialize user user = None # validate data errors = {} if 'email' not in request.json or not request.json['email']: if 'email' not in errors: errors['email'] = [] errors['email'].append("Missing data for required field.") if request.json.get('email'): temp_user = User(email=request.json.get('email')) if temp_user: user = User.query.filter( User.status == User.STATUS_ENABLED, User.roles.any(Role.id == 1), User.email_digest == temp_user.email_digest).first() if not user: if 'email' not in errors: errors['email'] = [] errors['email'].append("Email address not found.") if errors: return jsonify({"error": errors}), 400 # generate random seed now = datetime.now() unixtime = time.mktime(now.timetuple()) hash_object = hashlib.sha256((str(unixtime) + str(os.getpid()) + User.CRYPT_DIGEST_SALT).encode('utf-8')) random_seed = hash_object.hexdigest() # save reset request password_reset = PasswordReset(user_id=user.id, code=RandomString.user_code(8, random_seed), is_used=False, requested_at=datetime.now(), ip_address=request.environ.get( 'HTTP_X_REAL_IP', request.remote_addr), status=PasswordReset.STATUS_ENABLED, status_changed_at=datetime.now()) db.session.add(password_reset) db.session.commit() # email notification notify = Notify(current_app.config['ENV'], db) response = notify.send( user, Notify.CHANNEL_EMAIL, 'password-reset-code', name=user.profile.first_name if user.profile else 'User', code=password_reset.code) # response return jsonify({'success': 'true', 'sent': response}), 201
def post_user_account_step1(): """User registration step 1. :returns: JSON string of the user's account information; status code :rtype: (str, int) """ # pre-validate data errors = unique({}, User, User.username, str(request.json.get('username')).lower().strip() if request.json.get('username', None) else None) errors = unique_email( errors, User, User.email, str(request.json.get('email')).lower().strip() if request.json.get( 'email', None) else None) errors, tos = exists(errors, TermsOfService, 'tos_id', request.json.get('tos_id', None), missing_error="Please agree to the terms of service.") if (request.json.get('password', None) and request.json.get('password2', None)): if request.json.get('password') != request.json.get('password2'): errors['password2'] = ["Passwords must match."] # validate data try: data = UserAccountSchema(exclude=( 'first_name', 'last_name', )).load(request.json) except ValidationError as err: errors = dict(list(errors.items()) + list(err.messages.items())) # return any errors if errors: return jsonify({"error": errors}), 400 # save user user = User(username=data['username'].lower().strip(), email=data['email'].lower().strip(), password=data['password'], is_verified=False, status=User.STATUS_ENABLED, status_changed_at=datetime.now()) user_role = Role.query.filter(Role.name == 'USER').first() if user_role: user.roles.append(user_role) db.session.add(user) # save user terms of service user_tos = UserTermsOfService(user=user, terms_of_service=tos, accept_date=datetime.now(), ip_address=request.environ.get( 'HTTP_X_REAL_IP', request.remote_addr)) db.session.add(user_tos) # save password history pass_history = UserPasswordHistory(user=user, password=user.password, set_date=datetime.now()) db.session.add(pass_history) db.session.commit() # prep output output = { 'id': user.id, 'username': user.username, 'email': user.email, 'password_changed_at': user.password_changed_at, 'is_verified': user.is_verified, 'first_name': None, 'last_name': None, 'joined_at': None, } # response return jsonify({'user_account': UserAccountSchema().dump(output)}), 201