diff options
Diffstat (limited to 'app/api/responders/auth/tokens.py')
-rw-r--r-- | app/api/responders/auth/tokens.py | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/app/api/responders/auth/tokens.py b/app/api/responders/auth/tokens.py new file mode 100644 index 0000000..0b3a22f --- /dev/null +++ b/app/api/responders/auth/tokens.py @@ -0,0 +1,117 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +from datetime import datetime + +from bson.objectid import ObjectId + +from api.auth.auth import Auth +from api.auth.token import Token +from api.responders.responder_base import ResponderBase +from api.validation.data_validate import DataValidate +from utils.string_utils import stringify_object_values_by_types + + +class Tokens(ResponderBase): + + def __init__(self): + super().__init__() + self.auth_requirements = { + 'methods': self.require(list, False, + DataValidate.LIST, + ['credentials', 'token'], + True), + 'credentials': self.require(dict, True), + 'token': self.require(str) + } + + self.credential_requirements = { + 'username': self.require(str, mandatory=True), + 'password': self.require(str, mandatory=True) + } + self.auth = Auth() + + def on_post(self, req, resp): + self.log.debug('creating new token') + error, data = self.get_content_from_request(req) + if error: + self.bad_request(error) + + if 'auth' not in data: + self.bad_request('Request must contain auth object') + + auth = data['auth'] + + self.validate_query_data(auth, self.auth_requirements) + + if 'credentials' in auth: + self.validate_query_data(auth['credentials'], + self.credential_requirements) + + auth_error = self.authenticate(auth) + if auth_error: + self.unauthorized(auth_error) + + new_token = Token.new_uuid_token(auth['method']) + write_error = self.auth.write_token(new_token) + + if write_error: + # TODO if writing token to the database failed, what kind of error should be return? + self.bad_request(write_error) + + stringify_object_values_by_types(new_token, [datetime, ObjectId]) + self.set_successful_response(resp, new_token, '201') + + def authenticate(self, auth): + error = None + methods = auth['methods'] + credentials = auth.get('credentials') + token = auth.get('token') + + if not token and not credentials: + return 'must provide credentials or token' + + if 'credentials' in methods: + if not credentials: + return'credentials must be provided for credentials method' + else: + if not self.auth.validate_credentials(credentials['username'], + credentials['password']): + error = 'authentication failed' + else: + auth['method'] = "credentials" + return None + + if 'token' in methods: + if not token: + return 'token must be provided for token method' + else: + error = self.auth.validate_token(token) + if not error: + auth['method'] = 'token' + + return error + + def on_delete(self, req, resp): + headers = self.change_dict_naming_convention(req.headers, + lambda s: s.upper()) + if Token.FIELD not in headers: + self.unauthorized('Authentication failed') + + token = headers[Token.FIELD] + error = self.auth.validate_token(token) + if error: + self.unauthorized(error) + + delete_error = self.auth.delete_token(token) + + if delete_error: + self.bad_request(delete_error) + + self.set_successful_response(resp) |