Adding Token generation for users on /tokens
This commit is contained in:
parent
38b4d01581
commit
a45a4b491e
@ -1,12 +1,50 @@
|
|||||||
from eve import Eve
|
from eve import Eve
|
||||||
from eve.auth import TokenAuth
|
|
||||||
|
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
|
||||||
|
from eve.auth import TokenAuth
|
||||||
|
from eve.auth import BasicAuth
|
||||||
from eve.io.mongo import Validator
|
from eve.io.mongo import Validator
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
|
|
||||||
|
|
||||||
|
class TokensAuth(TokenAuth):
|
||||||
|
def check_auth(self, token, allowed_roles, resource, method):
|
||||||
|
tokens = app.data.driver.db['tokens']
|
||||||
|
lookup = {'token': token}
|
||||||
|
token = tokens.find_one(lookup)
|
||||||
|
if not token:
|
||||||
|
return False
|
||||||
|
users = app.data.driver.db['users']
|
||||||
|
lookup = {'firstname': token['username']}
|
||||||
|
if allowed_roles:
|
||||||
|
lookup['role'] = {'$in': allowed_roles}
|
||||||
|
user = users.find_one(lookup)
|
||||||
|
if not user:
|
||||||
|
return False
|
||||||
|
return token
|
||||||
|
|
||||||
|
class BasicsAuth(BasicAuth):
|
||||||
|
def check_auth(self, username, password, allowed_roles, resource, method):
|
||||||
|
return username == 'admin' and password == 'secret'
|
||||||
|
|
||||||
|
|
||||||
|
class MyTokenAuth(BasicsAuth):
|
||||||
|
def __init__(self):
|
||||||
|
self.token_auth = TokensAuth()
|
||||||
|
self.authorized_protected = BasicsAuth.authorized
|
||||||
|
|
||||||
|
def authorized(self, allowed_roles, resource, method):
|
||||||
|
if resource=='tokens':
|
||||||
|
return self.authorized_protected(self, allowed_roles, resource, method)
|
||||||
|
else:
|
||||||
|
return self.token_auth.authorized(allowed_roles, resource, method)
|
||||||
|
|
||||||
|
def authorized_protected(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ValidateCustomFields(Validator):
|
class ValidateCustomFields(Validator):
|
||||||
def _validate_valid_properties(self, valid_properties, field, value):
|
def _validate_valid_properties(self, valid_properties, field, value):
|
||||||
node_types = app.data.driver.db['node_types']
|
node_types = app.data.driver.db['node_types']
|
||||||
@ -22,23 +60,14 @@ class ValidateCustomFields(Validator):
|
|||||||
self._error(field, "Must be hi")
|
self._error(field, "Must be hi")
|
||||||
|
|
||||||
|
|
||||||
class RolesAuth(TokenAuth):
|
|
||||||
def check_auth(self, token, allowed_roles, resource, method):
|
|
||||||
accounts = app.data.driver.db['users']
|
|
||||||
lookup = {'token': token}
|
|
||||||
if allowed_roles:
|
|
||||||
lookup['role'] = {'$in': allowed_roles}
|
|
||||||
account = accounts.find_one(lookup)
|
|
||||||
return account
|
|
||||||
|
|
||||||
|
|
||||||
def add_token(documents):
|
def add_token(documents):
|
||||||
# Don't use this in production:
|
# Don't use this in production:
|
||||||
# You should at least make sure that the token is unique.
|
# You should at least make sure that the token is unique.
|
||||||
|
# print ("Adding Token")
|
||||||
for document in documents:
|
for document in documents:
|
||||||
document["token"] = (''.join(random.choice(string.ascii_uppercase)
|
document["token"] = (''.join(random.choice(string.ascii_uppercase)
|
||||||
for x in range(10)))
|
for x in range(10)))
|
||||||
|
|
||||||
app = Eve(validator=ValidateCustomFields, auth=RolesAuth)
|
|
||||||
app.on_insert_users += add_token
|
|
||||||
|
|
||||||
|
app = Eve(validator=ValidateCustomFields, auth=MyTokenAuth)
|
||||||
|
app.on_insert_tokens += add_token
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
|
from authentication import RolesAuth
|
||||||
|
|
||||||
# Enable reads (GET), inserts (POST) and DELETE for resources/collections
|
# Enable reads (GET), inserts (POST) and DELETE for resources/collections
|
||||||
# (if you omit this line, the API will default to ['GET'] and provide
|
# (if you omit this line, the API will default to ['GET'] and provide
|
||||||
@ -12,8 +13,6 @@ ITEM_METHODS = ['GET', 'PATCH', 'PUT', 'DELETE']
|
|||||||
|
|
||||||
|
|
||||||
users_schema = {
|
users_schema = {
|
||||||
# Schema definition, based on Cerberus grammar. Check the Cerberus project
|
|
||||||
# (https://github.com/nicolaiarocci/cerberus) for details.
|
|
||||||
'firstname': {
|
'firstname': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'minlength': 1,
|
'minlength': 1,
|
||||||
@ -23,13 +22,7 @@ users_schema = {
|
|||||||
'type': 'string',
|
'type': 'string',
|
||||||
'minlength': 1,
|
'minlength': 1,
|
||||||
'maxlength': 15,
|
'maxlength': 15,
|
||||||
'required': True,
|
|
||||||
# talk about hard constraints! For the purpose of the demo
|
|
||||||
# 'lastname' is an API entry-point, so we need it to be unique.
|
|
||||||
'unique': True,
|
|
||||||
},
|
},
|
||||||
# 'role' is a list, and can only contain values from 'allowed'.
|
|
||||||
# changed to string
|
|
||||||
'role': {
|
'role': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'allowed': ["author", "contributor", "copy"],
|
'allowed': ["author", "contributor", "copy"],
|
||||||
@ -102,6 +95,17 @@ node_types_schema = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tokens_schema = {
|
||||||
|
'username': {
|
||||||
|
'type': 'string',
|
||||||
|
'required': True,
|
||||||
|
},
|
||||||
|
'token': {
|
||||||
|
'type': 'string',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
nodes = {
|
nodes = {
|
||||||
# We choose to override global cache-control directives for this resource.
|
# We choose to override global cache-control directives for this resource.
|
||||||
'cache_control': 'max-age=10,must-revalidate',
|
'cache_control': 'max-age=10,must-revalidate',
|
||||||
@ -120,7 +124,7 @@ node_types = {
|
|||||||
|
|
||||||
'resource_methods': ['GET', 'POST'],
|
'resource_methods': ['GET', 'POST'],
|
||||||
|
|
||||||
'schema' : node_types_schema,
|
'schema': node_types_schema,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -143,25 +147,32 @@ users = {
|
|||||||
# most global settings can be overridden at resource level
|
# most global settings can be overridden at resource level
|
||||||
'resource_methods': ['GET', 'POST'],
|
'resource_methods': ['GET', 'POST'],
|
||||||
|
|
||||||
# Allow 'token' to be returned with POST responses
|
|
||||||
'extra_response_fields': ['token'],
|
|
||||||
|
|
||||||
'public_methods': ['GET', 'POST'],
|
'public_methods': ['GET', 'POST'],
|
||||||
# 'public_item_methods': ['GET'],
|
# 'public_item_methods': ['GET'],
|
||||||
|
|
||||||
|
|
||||||
'schema': users_schema
|
'schema': users_schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens = {
|
||||||
|
'resource_methods': ['POST'],
|
||||||
|
|
||||||
|
# Allow 'token' to be returned with POST responses
|
||||||
|
'extra_response_fields': ['token'],
|
||||||
|
|
||||||
|
'schema' : tokens_schema
|
||||||
|
}
|
||||||
|
|
||||||
DOMAIN = {
|
DOMAIN = {
|
||||||
'users': users,
|
'users': users,
|
||||||
'nodes' : nodes,
|
'nodes' : nodes,
|
||||||
'node_types': node_types,
|
'node_types': node_types,
|
||||||
|
'tokens': tokens,
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.environ['TEST_ATTRACT']
|
os.environ['TEST_ATTRACT']
|
||||||
#print ("Using attract_test database")
|
|
||||||
MONGO_DBNAME = 'attract_test'
|
MONGO_DBNAME = 'attract_test'
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
@ -4,10 +4,17 @@ import unittest
|
|||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
from bson import ObjectId
|
from bson import ObjectId
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import base64
|
||||||
|
|
||||||
|
|
||||||
class AttractTestCase(unittest.TestCase):
|
class AttractTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
|
def encodeUsrPass(self, user, password):
|
||||||
|
usrPass = "{0}:{1}".format(user, password)
|
||||||
|
b64Val = base64.b64encode(usrPass)
|
||||||
|
return b64Val
|
||||||
|
|
||||||
def addUser(self, firstname, lastname, role):
|
def addUser(self, firstname, lastname, role):
|
||||||
return self.app.post('/users', data=dict(
|
return self.app.post('/users', data=dict(
|
||||||
firstname=firstname,
|
firstname=firstname,
|
||||||
@ -46,6 +53,21 @@ class AttractTestCase(unittest.TestCase):
|
|||||||
headers=headers,
|
headers=headers,
|
||||||
follow_redirects=True)
|
follow_redirects=True)
|
||||||
|
|
||||||
|
def login(self, username, password):
|
||||||
|
headers = {
|
||||||
|
'content-type': 'application/json',
|
||||||
|
'Authorization': 'Basic {0}'.format(
|
||||||
|
self.encodeUsrPass(username, password))
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
'username': username,
|
||||||
|
}
|
||||||
|
return self.app.post(
|
||||||
|
'/tokens',
|
||||||
|
data=json.dumps(data),
|
||||||
|
headers=headers,
|
||||||
|
follow_redirects=True)
|
||||||
|
|
||||||
def logout(self):
|
def logout(self):
|
||||||
return self.app.get('/logout', follow_redirects=True)
|
return self.app.get('/logout', follow_redirects=True)
|
||||||
|
|
||||||
@ -72,6 +94,11 @@ class AttractTestCase(unittest.TestCase):
|
|||||||
rv = self.addNode('Shot01', '55016a52135d32466fc800be', properties)
|
rv = self.addNode('Shot01', '55016a52135d32466fc800be', properties)
|
||||||
assert 201 == rv.status_code
|
assert 201 == rv.status_code
|
||||||
|
|
||||||
|
def test_login(self):
|
||||||
|
rv = self.login('admin', 'secret')
|
||||||
|
#print (rv.data)
|
||||||
|
assert 201 == rv.status_code
|
||||||
|
|
||||||
def test_empty_db(self):
|
def test_empty_db(self):
|
||||||
rv = self.app.get('/')
|
rv = self.app.get('/')
|
||||||
assert 401 == rv.status_code
|
assert 401 == rv.status_code
|
||||||
@ -93,7 +120,6 @@ class AttractTestCase(unittest.TestCase):
|
|||||||
"_updated": datetime.now(),
|
"_updated": datetime.now(),
|
||||||
"firstname": "TestFirstname",
|
"firstname": "TestFirstname",
|
||||||
"lastname": "TestLastname",
|
"lastname": "TestLastname",
|
||||||
"token": "ANLGNSIEZJ",
|
|
||||||
"role": "author",
|
"role": "author",
|
||||||
"_created": datetime.now(),
|
"_created": datetime.now(),
|
||||||
"_etag": "302236e27f51d2e26041ae9de49505d77332b260"
|
"_etag": "302236e27f51d2e26041ae9de49505d77332b260"
|
||||||
@ -108,8 +134,16 @@ class AttractTestCase(unittest.TestCase):
|
|||||||
"_etag": "0ea3c4f684a0cda85525184d5606c4f4ce6ac5f5"
|
"_etag": "0ea3c4f684a0cda85525184d5606c4f4ce6ac5f5"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_token = {
|
||||||
|
"-id": ObjectId("5502f289135d3274cb658ba7"),
|
||||||
|
"username": "TestFirstname",
|
||||||
|
"token": "ANLGNSIEZJ",
|
||||||
|
"_etag": "1e96ed46b133b7ede5ce6ef0d6d4fc53edd9f2ba"
|
||||||
|
}
|
||||||
|
|
||||||
db.users.insert(test_user)
|
db.users.insert(test_user)
|
||||||
db.node_types.insert(test_node_type)
|
db.node_types.insert(test_node_type)
|
||||||
|
db.tokens.insert(test_token)
|
||||||
|
|
||||||
# Initialize Attract
|
# Initialize Attract
|
||||||
os.environ['TEST_ATTRACT'] = '1'
|
os.environ['TEST_ATTRACT'] = '1'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user