[WIP] added endpoint and dao to create invites for users.

Droped token as later code to send email invite can generate
timebased url to send to user. That can then be checked
against configurable time threshold for expiry. Therefore
no need to store a token.
This commit is contained in:
Adam Shimali
2016-02-24 14:01:19 +00:00
parent fb0e17a7db
commit e6fe10cbdc
10 changed files with 164 additions and 13 deletions

View File

@@ -45,6 +45,7 @@ def create_app():
from app.status.healthcheck import status as status_blueprint
from app.job.rest import job as job_blueprint
from app.notifications.rest import notifications as notifications_blueprint
from app.invite.rest import invite as invite_blueprint
application.register_blueprint(service_blueprint, url_prefix='/service')
application.register_blueprint(user_blueprint, url_prefix='/user')
@@ -52,6 +53,7 @@ def create_app():
application.register_blueprint(status_blueprint, url_prefix='/status')
application.register_blueprint(notifications_blueprint, url_prefix='/notifications')
application.register_blueprint(job_blueprint)
application.register_blueprint(invite_blueprint)
return application

View File

@@ -0,0 +1,6 @@
from app import db
def save_invited_user(invited_user):
db.session.add(invited_user)
db.session.commit()

0
app/invite/__init__.py Normal file
View File

24
app/invite/rest.py Normal file
View File

@@ -0,0 +1,24 @@
from flask import (
Blueprint,
request,
jsonify
)
from app.dao.invited_user_dao import save_invited_user
from app.schemas import invited_user_schema
invite = Blueprint('invite', __name__, url_prefix='/service/<service_id>/invite')
from app.errors import register_errors
register_errors(invite)
@invite.route('', methods=['POST'])
def create_invite_user(service_id):
invited_user, errors = invited_user_schema.load(request.get_json())
if errors:
return jsonify(result="error", message=errors), 400
save_invited_user(invited_user)
return jsonify(data=invited_user_schema.dump(invited_user).data), 201

View File

@@ -239,7 +239,6 @@ class InvitedUser(db.Model):
from_user = db.relationship('User')
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, unique=False)
service = db.relationship('Service')
_token = db.Column(db.String, nullable=False)
created_at = db.Column(
db.DateTime,
index=False,
@@ -247,15 +246,4 @@ class InvitedUser(db.Model):
nullable=False,
default=datetime.datetime.now)
status = db.Column(
db.Enum(*INVITED_USER_STATUS_TYPES, name='invited_users_status_types'), nullable=False, default='invited')
@property
def token(self):
raise AttributeError("Token not readable")
@token.setter
def token(self, token):
self._token = hashpw(token)
def check_token(self, token):
return check_hash(token, self._token)
db.Enum(*INVITED_USER_STATUS_TYPES, name='invited_users_status_types'), nullable=False, default='pending')

View File

@@ -118,6 +118,16 @@ class NotificationStatusSchema(BaseSchema):
model = models.Notification
class InvitedUserSchema(BaseSchema):
class Meta:
model = models.InvitedUser
@validates('email_address')
def validate_to(self, value):
if not email_regex.match(value):
raise ValidationError('Invalid email')
user_schema = UserSchema()
user_schema_load_json = UserSchema(load_json=True)
users_schema = UserSchema(many=True)
@@ -142,3 +152,5 @@ email_notification_schema = EmailNotificationSchema()
notification_status_schema = NotificationStatusSchema()
notifications_status_schema = NotificationStatusSchema(many=True)
notification_status_schema_load_json = NotificationStatusSchema(load_json=True)
invited_user_schema = InvitedUserSchema()
invited_users_schema = InvitedUserSchema(many=True)

View File

@@ -0,0 +1,26 @@
"""empty message
Revision ID: 0023_drop_token
Revises: 0022_add_invite_users
Create Date: 2016-02-24 13:58:04.440296
"""
# revision identifiers, used by Alembic.
revision = '0023_drop_token'
down_revision = '0022_add_invite_users'
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_column('invited_users', '_token')
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('invited_users', sa.Column('_token', sa.VARCHAR(), autoincrement=False, nullable=False))
### end Alembic commands ###

View File

@@ -0,0 +1,23 @@
from app.models import InvitedUser
from app.dao.invited_user_dao import save_invited_user
def test_create_invited_user(notify_db, notify_db_session, sample_service):
assert InvitedUser.query.count() == 0
email_address = 'invited_user@service.gov.uk'
invite_from = sample_service.users[0]
data = {
'service': sample_service,
'email_address': email_address,
'from_user': invite_from
}
invited_user = InvitedUser(**data)
save_invited_user(invited_user)
assert InvitedUser.query.count() == 1
assert invited_user.email_address == email_address
assert invited_user.from_user == invite_from

View File

View File

@@ -0,0 +1,70 @@
import json
from tests import create_authorization_header
def test_create_invited_user(notify_api, sample_service):
with notify_api.test_request_context():
with notify_api.test_client() as client:
email_address = 'invited_user@service.gov.uk'
invite_from = sample_service.users[0]
data = {
'service': str(sample_service.id),
'email_address': email_address,
'from_user': invite_from.id
}
data = json.dumps(data)
auth_header = create_authorization_header(
path='/service/{}/invite'.format(sample_service.id),
method='POST',
request_body=data
)
response = client.post(
'/service/{}/invite'.format(sample_service.id),
headers=[('Content-Type', 'application/json'), auth_header],
data=data
)
assert response.status_code == 201
json_resp = json.loads(response.get_data(as_text=True))
assert json_resp['data']['service'] == str(sample_service.id)
assert json_resp['data']['email_address'] == email_address
assert json_resp['data']['from_user'] == invite_from.id
assert json_resp['data']['id']
def test_create_invited_user_invalid_email(notify_api, sample_service):
with notify_api.test_request_context():
with notify_api.test_client() as client:
email_address = 'notanemail'
invite_from = sample_service.users[0]
data = {
'service': str(sample_service.id),
'email_address': email_address,
'from_user': invite_from.id
}
data = json.dumps(data)
auth_header = create_authorization_header(
path='/service/{}/invite'.format(sample_service.id),
method='POST',
request_body=data
)
response = client.post(
'/service/{}/invite'.format(sample_service.id),
headers=[('Content-Type', 'application/json'), auth_header],
data=data
)
assert response.status_code == 400
json_resp = json.loads(response.get_data(as_text=True))
assert json_resp['result'] == 'error'
assert json_resp['message'] == {'email_address': ['Invalid email']}