Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions driftbase/auth/authenticate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def abort_unauthorized(description):
def authenticate_with_provider(auth_info):
"""
Supported schemas:
provider: "uuid", provider_details: { key, secret } -> key
provider: "uuid", provider_details: { key, secret } -> uuid:key
provider: "device_id", provider_details: { uuid:username, password } -> uuid:username
provider: "user+pass", provider_details: { username, password } -> username
provider: "viveport", provider_details: { username, password } -> viveport:username
Expand All @@ -38,7 +38,8 @@ def authenticate_with_provider(auth_info):
try:
identity = authenticate('uuid:' + provider_details['key'],
provider_details['secret'],
automatic_account_creation)
automatic_account_creation,
allow_fallback_to_non_identity=True)
except KeyError:
abort(http.client.BAD_REQUEST, "Bad Request. Missing expected argument.")

Expand Down Expand Up @@ -86,7 +87,7 @@ def authenticate_with_provider(auth_info):
return identity


def authenticate(username, password, automatic_account_creation=True):
def authenticate(username, password, automatic_account_creation=True, allow_fallback_to_non_identity=False):
"""basic authentication"""
identity_type = ""
create_roles = []
Expand All @@ -105,6 +106,17 @@ def authenticate(username, password, automatic_account_creation=True):
.filter(UserIdentity.name == username) \
.first()

# attempt to locate a legacy non-specific version of the identity, in case it hasn't yet been migrated
if my_identity is None and not is_old and allow_fallback_to_non_identity:
legacy_identity = g.db.query(UserIdentity) \
.filter(UserIdentity.name == lst[1]) \
.first()
if legacy_identity is not None:
my_identity = legacy_identity
# migrate the identity to new-style on demand
my_identity.name = username
my_identity.identity_type = identity_type

try:
service_user = g.conf.tier.get('service_user')
if not service_user and g.conf.tenant:
Expand Down
80 changes: 80 additions & 0 deletions driftbase/tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import http.client as http_client
import jwt
from mock import patch, MagicMock

from drift.core.extensions.jwt import JWT_ALGORITHM
from drift.systesthelper import setup_tenant, remove_tenant
from drift.utils import get_config
from driftbase.systesthelper import DriftBaseTestCase
Expand Down Expand Up @@ -264,3 +266,81 @@ def test_uuid_methods_resolve_to_same_user(self):
self.assertEqual(user1['user_id'], user2['user_id'])
self.assertEqual(user2['identity_id'], user3['identity_id'])
self.assertEqual(user2['user_id'], user3['user_id'])

def test_userpass_auth_fallback(self):
# Create or login
data1 = {
"username": "foo",
"password": "bar",
"automatic_account_creation": True
}
resp1 = self.post('/auth', data=data1, expected_status_code=http_client.OK).json()
data2 = {
"provider": "user+pass",
"username": "foo",
"password": "bar",
"automatic_account_creation": True
}
resp2 = self.post('/auth', data=data2, expected_status_code=http_client.OK).json()
data3 = {
"provider": "user+pass",
"provider_details": {
"username": "foo",
"password": "bar"
},
"automatic_account_creation": True
}
resp3 = self.post('/auth', data=data3, expected_status_code=http_client.OK).json()
options = {"verify_signature": False}
token1 = jwt.decode(resp1['token'], algorithms=[JWT_ALGORITHM], options=options)
token2 = jwt.decode(resp2['token'], algorithms=[JWT_ALGORITHM], options=options)
token3 = jwt.decode(resp3['token'], algorithms=[JWT_ALGORITHM], options=options)
self.assertEqual(token1['user_id'], token2['user_id'])
self.assertEqual(token1['user_name'], token2['user_name'])
self.assertEqual(token1['player_id'], token2['player_id'])
self.assertEqual(token1['identity_id'], token2['identity_id'])
self.assertEqual(token1['user_id'], token3['user_id'])
self.assertEqual(token1['user_name'], token3['user_name'])
self.assertEqual(token1['player_id'], token3['player_id'])
self.assertEqual(token1['identity_id'], token3['identity_id'])

def test_uuid_auth_fallback(self):
# Will be converted to user+pass
data1 = {
"username": "foo",
"password": "bar",
"automatic_account_creation": True
}
resp1 = self.post('/auth', data=data1, expected_status_code=http_client.OK).json()
# Will be treated as uuid
data2 = {
"provider": "uuid",
"provider_details": {
"key": "foo",
"secret": "bar"
},
"username": "foo",
"password": "bar",
"automatic_account_creation": True
}
resp2 = self.post('/auth', data=data2, expected_status_code=http_client.OK).json()
# Will be treated as uuid
data3 = {
"provider": "uuid",
"provider_details": {
"key": "foo",
"secret": "bar"
},
"automatic_account_creation": True
}
resp3 = self.post('/auth', data=data3, expected_status_code=http_client.OK).json()
options = {"verify_signature": False}
token1 = jwt.decode(resp1['token'], algorithms=[JWT_ALGORITHM], options=options)
token2 = jwt.decode(resp2['token'], algorithms=[JWT_ALGORITHM], options=options)
token3 = jwt.decode(resp3['token'], algorithms=[JWT_ALGORITHM], options=options)
self.assertEqual(token1['user_id'], token2['user_id'])
self.assertEqual(token1['player_id'], token2['player_id'])
self.assertEqual(token1['identity_id'], token2['identity_id'])
self.assertEqual(token1['user_id'], token3['user_id'])
self.assertEqual(token1['player_id'], token3['player_id'])
self.assertEqual(token1['identity_id'], token3['identity_id'])