mirror of
https://github.com/GSA/notifications-api.git
synced 2026-02-04 02:11:11 -05:00
Created endpoints for create and delete token.
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
from app import db
|
||||
from app.models import ApiToken
|
||||
|
||||
|
||||
def save_token_model(token, update_dict={}):
|
||||
if update_dict:
|
||||
del update_dict['id']
|
||||
db.session.query(ApiToken).filter_by(id=token.id).update(update_dict)
|
||||
else:
|
||||
db.session.add(token)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def get_model_api_tokens(token=None):
|
||||
if token:
|
||||
return ApiToken.query.filter_by(token=token).one()
|
||||
return ApiToken.query.filter_by().all()
|
||||
22
app/dao/tokens_dao.py
Normal file
22
app/dao/tokens_dao.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from app import db
|
||||
from app.models import Token
|
||||
|
||||
|
||||
def save_token_model(token, update_dict={}):
|
||||
if update_dict:
|
||||
del update_dict['id']
|
||||
db.session.query(Token).filter_by(id=token.id).update(update_dict)
|
||||
else:
|
||||
db.session.add(token)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def get_model_tokens(service_id=None):
|
||||
if service_id:
|
||||
return Token.query.filter_by(service_id=service_id).one()
|
||||
return Token.query.filter_by().all()
|
||||
|
||||
|
||||
def delete_model_token(token):
|
||||
db.session.delete(token)
|
||||
db.session.commit()
|
||||
@@ -61,8 +61,8 @@ class Service(db.Model):
|
||||
restricted = db.Column(db.Boolean, index=False, unique=False, nullable=False)
|
||||
|
||||
|
||||
class ApiToken(db.Model):
|
||||
__tablename__ = 'api_tokens'
|
||||
class Token(db.Model):
|
||||
__tablename__ = 'tokens'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
token = db.Column(db.String, unique=True, nullable=False)
|
||||
|
||||
@@ -28,15 +28,15 @@ class TemplateSchema(ma.ModelSchema):
|
||||
exclude = ("updated_at", "created_at", "service_id")
|
||||
|
||||
|
||||
class ApiTokenSchema(ma.ModelSchema):
|
||||
class TokenSchema(ma.ModelSchema):
|
||||
class Meta:
|
||||
model = models.ApiToken
|
||||
model = models.Token
|
||||
|
||||
user_schema = UserSchema()
|
||||
users_schema = UserSchema(many=True)
|
||||
service_schema = ServiceSchema()
|
||||
services_schema = ServiceSchema(many=True)
|
||||
api_token_schema = ApiTokenSchema()
|
||||
api_tokens_schema = ApiTokenSchema(many=True)
|
||||
template_schema = TemplateSchema()
|
||||
templates_schema = TemplateSchema(many=True)
|
||||
token_schema = TokenSchema()
|
||||
tokens_schema = TokenSchema(many=True)
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
from flask import (jsonify, request)
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from flask import (jsonify, request, current_app)
|
||||
from sqlalchemy.exc import DataError
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from app.dao.services_dao import (
|
||||
save_model_service, get_model_services, delete_model_service)
|
||||
from app.dao.tokens_dao import (save_token_model, get_model_tokens, delete_model_token)
|
||||
from app.dao.users_dao import get_model_users
|
||||
from app.dao.templates_dao import (
|
||||
save_model_template, get_model_templates)
|
||||
from app.dao import DAOException
|
||||
from .. import service
|
||||
from app import db
|
||||
from app.schemas import (services_schema, service_schema, token_schema)
|
||||
from app.models import Token
|
||||
from itsdangerous import URLSafeSerializer
|
||||
from app.schemas import (
|
||||
services_schema, service_schema, template_schema, templates_schema)
|
||||
|
||||
@@ -73,8 +80,48 @@ def get_service(service_id=None):
|
||||
|
||||
# TODO auth to be added
|
||||
@service.route('/<int:service_id>/token', methods=['POST'])
|
||||
def create_token():
|
||||
request.get_json()
|
||||
def create_token(service_id=None):
|
||||
try:
|
||||
service = get_model_services(service_id=service_id)
|
||||
except DataError:
|
||||
return jsonify(result="error", message="Invalid service id"), 400
|
||||
except NoResultFound:
|
||||
return jsonify(result="error", message="Service not found"), 404
|
||||
|
||||
token = _generate_token()
|
||||
try:
|
||||
try:
|
||||
service_token = get_model_tokens(service_id=service_id)
|
||||
save_token_model(service_token, update_dict={'id': service_token.id,
|
||||
'token': service_token.token,
|
||||
'expiry_date': datetime.now()})
|
||||
except NoResultFound:
|
||||
pass
|
||||
save_token_model(Token(service_id=service_id, token=token))
|
||||
except DAOException as e:
|
||||
return jsonify(result='error', message=str(e)), 400
|
||||
return jsonify(token=str(token)), 201
|
||||
|
||||
|
||||
@service.route('/<int:service_id>/token', methods=['DELETE'])
|
||||
def delete_token(service_id):
|
||||
try:
|
||||
token = get_model_tokens(service_id=service_id)
|
||||
delete_model_token(token)
|
||||
return jsonify(data=token_schema.dump(token).data), 202
|
||||
except NoResultFound:
|
||||
return jsonify(result="error", message="Token not found"), 404
|
||||
|
||||
|
||||
def _generate_token():
|
||||
token = uuid.uuid4()
|
||||
serializer = URLSafeSerializer(current_app.config.get('SECRET_KEY'))
|
||||
return serializer.dumps(str(token), current_app.config.get('DANGEROUS_SALT'))
|
||||
|
||||
|
||||
def _get_token(token):
|
||||
serializer = URLSafeSerializer(current_app.config.get('SECRET_KEY'))
|
||||
return serializer.loads(token, salt=current_app.config.get('DANGEROUS_SALT'))
|
||||
|
||||
|
||||
# TODO auth to be added.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
class Config(object):
|
||||
DEBUG = False
|
||||
NOTIFY_LOG_LEVEL = 'DEBUG'
|
||||
@@ -11,11 +10,15 @@ class Config(object):
|
||||
|
||||
class Development(Config):
|
||||
DEBUG = True
|
||||
SECRET_KEY = 'secret-key'
|
||||
DANGEROUS_SALT = 'dangerous-salt'
|
||||
|
||||
|
||||
class Test(Config):
|
||||
DEBUG = True
|
||||
SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/test_notification_api'
|
||||
SECRET_KEY = 'secret-key'
|
||||
DANGEROUS_SALT = 'dangerous-salt'
|
||||
|
||||
|
||||
class Live(Config):
|
||||
|
||||
36
migrations/versions/0002_create_tokens.py
Normal file
36
migrations/versions/0002_create_tokens.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 0002_create_api_token
|
||||
Revises: 0001_initialise_data
|
||||
Create Date: 2016-01-13 10:21:14.651691
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0002_create_api_token'
|
||||
down_revision = '0001_initialise_data'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('tokens',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('token', sa.String(), nullable=False),
|
||||
sa.Column('service_id', sa.Integer(), nullable=False),
|
||||
sa.Column('expiry_date', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['service_id'], ['services.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('token')
|
||||
)
|
||||
op.create_index(op.f('ix_tokens_service_id'), 'tokens', ['service_id'], unique=False)
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_tokens_service_id'), table_name='tokens')
|
||||
op.drop_table('tokens')
|
||||
### end Alembic commands ###
|
||||
@@ -9,6 +9,7 @@ PyJWT==1.4.0
|
||||
marshmallow==2.4.2
|
||||
marshmallow-sqlalchemy==0.8.0
|
||||
flask-marshmallow==0.6.2
|
||||
itsdangerous==0.24
|
||||
|
||||
git+https://github.com/alphagov/notifications-python-client.git@0.1.5#egg=notifications-python-client==0.1.5
|
||||
|
||||
|
||||
@@ -1,16 +1,45 @@
|
||||
import uuid
|
||||
from app.dao import tokens_dao
|
||||
|
||||
from app.dao import api_tokens_dao
|
||||
from app.models import ApiToken
|
||||
from app.models import Token
|
||||
|
||||
|
||||
def test_should_create_token(notify_api, notify_db, notify_db_session, sample_service):
|
||||
token = uuid.uuid4()
|
||||
api_token = ApiToken(**{'token': token, 'service_id': sample_service.id})
|
||||
api_token = Token(**{'token': token, 'service_id': sample_service.id})
|
||||
|
||||
api_tokens_dao.save_token_model(api_token)
|
||||
|
||||
all_tokens = api_tokens_dao.get_model_api_tokens()
|
||||
tokens_dao.save_token_model(api_token)
|
||||
|
||||
all_tokens = tokens_dao.get_model_tokens()
|
||||
assert len(all_tokens) == 1
|
||||
assert all_tokens[0].token == str(token)
|
||||
|
||||
|
||||
def test_should_delete_api_token(notify_api, notify_db, notify_db_session, sample_service):
|
||||
token = uuid.uuid4()
|
||||
api_token = Token(**{'token': token, 'service_id': sample_service.id})
|
||||
tokens_dao.save_token_model(api_token)
|
||||
all_tokens = tokens_dao.get_model_tokens()
|
||||
assert len(all_tokens) == 1
|
||||
|
||||
tokens_dao.delete_model_token(all_tokens[0])
|
||||
empty_token_list = tokens_dao.get_model_tokens()
|
||||
assert len(empty_token_list) == 0
|
||||
|
||||
|
||||
def test_should_return_token_for_service(notify_api, notify_db, notify_db_session, sample_service):
|
||||
the_token = str(uuid.uuid4())
|
||||
api_token = Token(**{'token': the_token, 'service_id': sample_service.id})
|
||||
tokens_dao.save_token_model(api_token)
|
||||
token = tokens_dao.get_model_tokens(sample_service.id)
|
||||
assert token.service_id == sample_service.id
|
||||
assert token.token == str(the_token)
|
||||
|
||||
|
||||
def test_delete_model_token_should_remove_token(notify_api, notify_db, notify_db_session, sample_service):
|
||||
api_token = Token(**{'token': str(uuid.uuid4()), 'service_id': sample_service.id})
|
||||
tokens_dao.save_token_model(api_token)
|
||||
all_tokens = tokens_dao.get_model_tokens()
|
||||
assert len(all_tokens) == 1
|
||||
tokens_dao.delete_model_token(all_tokens[0])
|
||||
assert len(tokens_dao.get_model_tokens()) == 0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import json
|
||||
from app.models import (Service, User, Template)
|
||||
from app.models import (Service, User, Token, Template)
|
||||
from app.dao.services_dao import save_model_service
|
||||
from tests.app.conftest import sample_user as create_sample_user
|
||||
from flask import url_for
|
||||
@@ -263,6 +263,61 @@ def test_delete_service_not_exists(notify_api, notify_db, notify_db_session, sam
|
||||
assert Service.query.count() == 1
|
||||
|
||||
|
||||
def test_create_token_should_return_token_when_successful(notify_api, notify_db, notify_db_session, sample_service):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
response = client.post(url_for('service.create_token', service_id=sample_service.id),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert response.status_code == 201
|
||||
assert response.get_data is not None
|
||||
saved_token = Token.query.first()
|
||||
assert saved_token.service_id == sample_service.id
|
||||
|
||||
|
||||
def test_create_token_should_expire_the_old_token_and_create_a_new_token(notify_api, notify_db, notify_db_session,
|
||||
sample_service):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
response = client.post(url_for('service.create_token', service_id=sample_service.id),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert response.status_code == 201
|
||||
assert len(Token.query.all()) == 1
|
||||
saved_token = Token.query.first()
|
||||
|
||||
response = client.post(url_for('service.create_token', service_id=sample_service.id),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert response.status_code == 201
|
||||
all_tokens = Token.query.all()
|
||||
assert len(all_tokens) == 2
|
||||
for x in all_tokens:
|
||||
if x.id == saved_token.id:
|
||||
assert x.expiry_date is not None
|
||||
else:
|
||||
assert x.expiry_date is None
|
||||
assert x.token is not saved_token.token
|
||||
|
||||
|
||||
def test_create_token_should_return_error_when_service_does_not_exist(notify_api, notify_db, notify_db_session,
|
||||
sample_service):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
response = client.post(url_for('service.create_token', service_id=123),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_delete_token(notify_api, notify_db, notify_db_session, sample_service):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
client.post(url_for('service.create_token', service_id=sample_service.id),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
all_tokens = Token.query.all()
|
||||
assert len(all_tokens) == 1
|
||||
response = client.delete(url_for('service.delete_token', service_id=sample_service.id))
|
||||
assert response.status_code == 202
|
||||
assert len(Token.query.all()) == 0
|
||||
|
||||
|
||||
def test_create_template(notify_api, notify_db, notify_db_session, sample_service):
|
||||
"""
|
||||
Tests POST endpoint '/<service_id>/template' a template can be created
|
||||
|
||||
Reference in New Issue
Block a user