From 03961488533d1d17d7451705bf190dc641aff7e0 Mon Sep 17 00:00:00 2001 From: Nicholas Staples Date: Fri, 19 Feb 2016 14:47:45 +0000 Subject: [PATCH] Permissions added. --- app/models.py | 3 +- environment_test.sh | 1 + migrations/versions/0015_add_permissions.py | 26 ++++ tests/app/conftest.py | 3 +- tests/app/user/test_rest.py | 147 ++++++++++++++++++-- 5 files changed, 169 insertions(+), 11 deletions(-) create mode 100644 migrations/versions/0015_add_permissions.py diff --git a/app/models.py b/app/models.py index 4b53483dc..4ae7871c4 100644 --- a/app/models.py +++ b/app/models.py @@ -3,7 +3,7 @@ import uuid from sqlalchemy import UniqueConstraint, Sequence from . import db import datetime -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.dialects.postgresql import (UUID, ARRAY) from app.encryption import ( hashpw, check_hash @@ -40,6 +40,7 @@ class User(db.Model): logged_in_at = db.Column(db.DateTime, nullable=True) failed_login_count = db.Column(db.Integer, nullable=False, default=0) state = db.Column(db.String, nullable=False, default='pending') + permissions = db.Column("permissions", ARRAY(db.String)) @property def password(self): diff --git a/environment_test.sh b/environment_test.sh index bbc44a855..c4ce96bcf 100644 --- a/environment_test.sh +++ b/environment_test.sh @@ -1,3 +1,4 @@ +#!/bin/bash export NOTIFY_API_ENVIRONMENT='config.Test' export ADMIN_CLIENT_USER_NAME='dev-notify-admin' export ADMIN_CLIENT_SECRET='dev-notify-secret-key' diff --git a/migrations/versions/0015_add_permissions.py b/migrations/versions/0015_add_permissions.py new file mode 100644 index 000000000..507a5effa --- /dev/null +++ b/migrations/versions/0015_add_permissions.py @@ -0,0 +1,26 @@ +"""empty message + +Revision ID: 0015_add_permissions +Revises: 0014_job_id_nullable +Create Date: 2016-02-19 14:16:59.359493 + +""" + +# revision identifiers, used by Alembic. +revision = '0015_add_permissions' +down_revision = '0014_job_id_nullable' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +def upgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.add_column('users', sa.Column('permissions', postgresql.ARRAY(sa.String()), nullable=True)) + ### end Alembic commands ### + + +def downgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.drop_column('users', 'permissions') + ### end Alembic commands ### diff --git a/tests/app/conftest.py b/tests/app/conftest.py index 309f43871..69a3524ef 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -31,7 +31,8 @@ def sample_user(notify_db, 'email_address': email, 'password': 'password', 'mobile_number': '+447700900986', - 'state': 'active' + 'state': 'active', + 'permissions': [] } usr = User.query.filter_by(email_address=email).first() if not usr: diff --git a/tests/app/user/test_rest.py b/tests/app/user/test_rest.py index 008da0558..d89558a9c 100644 --- a/tests/app/user/test_rest.py +++ b/tests/app/user/test_rest.py @@ -1,6 +1,7 @@ import json from flask import url_for from app.models import (User, Service) +from app.dao.users_dao import save_model_user from tests import create_authorization_header from tests.app.conftest import sample_service as create_sample_service @@ -27,7 +28,8 @@ def test_get_user_list(notify_api, notify_db, notify_db_session, sample_user, sa "password_changed_at": None, "logged_in_at": None, "state": "active", - "failed_login_count": 0 + "failed_login_count": 0, + "permissions": [] } assert expected in json_resp['data'] @@ -54,7 +56,8 @@ def test_get_user(notify_api, notify_db, notify_db_session, sample_user, sample_ "password_changed_at": None, "logged_in_at": None, "state": "active", - "failed_login_count": 0 + "failed_login_count": 0, + "permissions": [] } assert json_resp['data'] == expected @@ -74,7 +77,8 @@ def test_post_user(notify_api, notify_db, notify_db_session, sample_admin_servic "password_changed_at": None, "logged_in_at": None, "state": "active", - "failed_login_count": 0 + "failed_login_count": 0, + "permissions": [] } auth_header = create_authorization_header(service_id=sample_admin_service_id, path=url_for('user.create_user'), @@ -107,7 +111,8 @@ def test_post_user_missing_attribute_email(notify_api, notify_db, notify_db_sess "password_changed_at": None, "logged_in_at": None, "state": "active", - "failed_login_count": 0 + "failed_login_count": 0, + "permissions": [] } auth_header = create_authorization_header(service_id=sample_admin_service_id, path=url_for('user.create_user'), @@ -138,7 +143,8 @@ def test_post_user_missing_attribute_password(notify_api, notify_db, notify_db_s "password_changed_at": None, "logged_in_at": None, "state": "active", - "failed_login_count": 0 + "failed_login_count": 0, + "permissions": [] } auth_header = create_authorization_header(service_id=sample_admin_service_id, path=url_for('user.create_user'), @@ -166,7 +172,8 @@ def test_put_user(notify_api, notify_db, notify_db_session, sample_user, sample_ data = { 'name': sample_user.name, 'email_address': new_email, - 'mobile_number': sample_user.mobile_number + 'mobile_number': sample_user.mobile_number, + 'permissions': [] } auth_header = create_authorization_header(service_id=sample_admin_service_id, path=url_for('user.update_user', user_id=sample_user.id), @@ -189,7 +196,8 @@ def test_put_user(notify_api, notify_db, notify_db_session, sample_user, sample_ "id": user.id, "logged_in_at": None, "state": "active", - "failed_login_count": 0 + "failed_login_count": 0, + "permissions": [] } assert json_resp['data'] == expected assert json_resp['data']['email_address'] == new_email @@ -211,7 +219,8 @@ def test_put_user_update_password(notify_api, 'name': sample_user.name, 'email_address': sample_user.email_address, 'mobile_number': sample_user.mobile_number, - 'password': new_password + 'password': new_password, + 'permissions': [] } auth_header = create_authorization_header(service_id=sample_admin_service_id, path=url_for('user.update_user', user_id=sample_user.id), @@ -386,7 +395,8 @@ def test_delete_user(notify_api, notify_db, notify_db_session, sample_user, samp "id": sample_user.id, "logged_in_at": None, "state": "active", - "failed_login_count": 0 + "failed_login_count": 0, + "permissions": [] } assert json_resp['data'] == expected @@ -407,3 +417,122 @@ def test_delete_user_not_exists(notify_api, notify_db, notify_db_session, sample headers=[('Content-Type', 'application/json'), auth_header]) assert resp.status_code == 404 assert User.query.count() == 2 + + +def test_post_with_permissions(notify_api, notify_db, notify_db_session, sample_admin_service_id): + """ + Tests POST endpoint '/' to create a user with permissions. + """ + with notify_api.test_request_context(): + with notify_api.test_client() as client: + assert User.query.count() == 1 + permissions = ['new permission'] + data = { + "name": "Test User", + "email_address": "user@digital.cabinet-office.gov.uk", + "password": "password", + "mobile_number": "+447700900986", + "password_changed_at": None, + "logged_in_at": None, + "state": "active", + "failed_login_count": 0, + "permissions": permissions + } + auth_header = create_authorization_header(service_id=sample_admin_service_id, + path=url_for('user.create_user'), + method='POST', + request_body=json.dumps(data)) + headers = [('Content-Type', 'application/json'), auth_header] + resp = client.post( + url_for('user.create_user'), + data=json.dumps(data), + headers=headers) + assert resp.status_code == 201 + user = User.query.filter_by(email_address='user@digital.cabinet-office.gov.uk').first() + json_resp = json.loads(resp.get_data(as_text=True)) + json_resp['data'] == {"email_address": user.email_address, "id": user.id} + assert json_resp['data']['email_address'] == user.email_address + assert json_resp['data']['id'] == user.id + assert json_resp['data']['permissions'] == permissions + + +def test_put_add_permissions(notify_api, notify_db, notify_db_session, sample_user, sample_admin_service_id): + """ + Tests PUT endpoint '/' to update user permissions. + """ + with notify_api.test_request_context(): + with notify_api.test_client() as client: + permissions = ['one permission', 'another permission'] + data = { + 'name': sample_user.name, + 'email_address': sample_user.email_address, + 'mobile_number': sample_user.mobile_number, + 'permissions': permissions + } + auth_header = create_authorization_header(service_id=sample_admin_service_id, + path=url_for('user.update_user', user_id=sample_user.id), + method='PUT', + request_body=json.dumps(data)) + headers = [('Content-Type', 'application/json'), auth_header] + resp = client.put( + url_for('user.update_user', user_id=sample_user.id), + data=json.dumps(data), + headers=headers) + assert resp.status_code == 200 + assert User.query.count() == 2 + user = User.query.filter_by(email_address=sample_user.email_address).first() + json_resp = json.loads(resp.get_data(as_text=True)) + expected = { + "name": user.name, + "email_address": user.email_address, + "mobile_number": user.mobile_number, + "password_changed_at": None, + "id": user.id, + "logged_in_at": None, + "state": user.state, + "failed_login_count": 0, + "permissions": permissions + } + assert json_resp['data'] == expected + + +def test_put_remove_permissions(notify_api, notify_db, notify_db_session, sample_user, sample_admin_service_id): + """ + Tests PUT endpoint '/' to update user permissions. + """ + with notify_api.test_request_context(): + with notify_api.test_client() as client: + old_permissions = ['one permission', 'another permission'] + save_model_user(sample_user, {'permissions': old_permissions}) + permissions = ['new permissions'] + data = { + 'name': sample_user.name, + 'email_address': sample_user.email_address, + 'mobile_number': sample_user.mobile_number, + 'permissions': permissions + } + auth_header = create_authorization_header(service_id=sample_admin_service_id, + path=url_for('user.update_user', user_id=sample_user.id), + method='PUT', + request_body=json.dumps(data)) + headers = [('Content-Type', 'application/json'), auth_header] + resp = client.put( + url_for('user.update_user', user_id=sample_user.id), + data=json.dumps(data), + headers=headers) + assert resp.status_code == 200 + assert User.query.count() == 2 + user = User.query.filter_by(email_address=sample_user.email_address).first() + json_resp = json.loads(resp.get_data(as_text=True)) + expected = { + "name": user.name, + "email_address": user.email_address, + "mobile_number": user.mobile_number, + "password_changed_at": None, + "id": user.id, + "logged_in_at": None, + "state": user.state, + "failed_login_count": 0, + "permissions": permissions + } + assert json_resp['data'] == expected