-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Superadmin Implementation #168
Changes from 15 commits
2f56c37
cd2c32d
2269c6a
37031ec
f96ce6b
16861d7
efe3864
508c0a5
6bb7b2c
092f4d6
a63a8cb
73fd7ad
8d26ab2
c9bbb33
63bee9a
2853bad
3f713c8
a07bc60
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -557,9 +557,7 @@ def insert_user(self, user): | |
|
||
return self.users_col.insert(user.dump(context='db')) | ||
|
||
# throws invalidRegionsException, which is okay, as this is only used by a | ||
# script | ||
def create_user(self, username, password, regions): | ||
def create_user(self, username, password, regions, perm='REGION'): | ||
valid_regions = [ | ||
region.id for region in Dao.get_all_regions(self.mongo_client)] | ||
|
||
|
@@ -576,7 +574,8 @@ def create_user(self, username, password, regions): | |
admin_regions=regions, | ||
username=username, | ||
salt=salt, | ||
hashed_password=hashed_password) | ||
hashed_password=hashed_password, | ||
admin_level=perm) | ||
|
||
return self.insert_user(the_user) | ||
|
||
|
@@ -619,6 +618,25 @@ def get_user_by_session_id_or_none(self, session_id): | |
def get_user_by_region(self, regions): | ||
pass | ||
|
||
def get_is_superadmin(self, user_id): | ||
user = None | ||
if self.users_col.find_one({'_id': user_id}): | ||
user = M.User.load(self.users_col.find_one({'_id': user_id})) | ||
return user.admin_level == 'SUPER' | ||
else: | ||
return False | ||
|
||
def set_user_admin_level(self, user_id, admin_level): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we actually use this function anywhere else in the code yet? if not, I think it would be fine to just replace it with a simple |
||
if type(admin_level) is not M.AdminLevels: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think M.AdminLevels does not exist anymore, so this should error (is it being tested)? |
||
raise Exception('Submitted admin level is not of correct type') | ||
if self.users_col.find_one({'_id': user_id}): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. right now this way of updating bypasses the ORM completely and won't trigger any of the ORMs validators. the correct way to do this sort of thing is 1. read the user data into a User object (as is done in get_user_by_id_or_none), 2. update whatever properties of the User object, 3. then write the User object back into the database using |
||
self.user_col.update({'_id': user_id}, | ||
{'$set': | ||
{ | ||
'admin_level': admin_level.name | ||
} | ||
}) | ||
|
||
#### FOR INTERNAL USE ONLY #### | ||
#XXX: this method must NEVER be publicly routeable, or you have session-hijacking | ||
def get_session_id_by_user_or_none(self, User): | ||
|
@@ -645,6 +663,24 @@ def check_creds_and_get_session_id_or_none(self, username, password): | |
else: | ||
return None | ||
|
||
def check_creds(self, username, password): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should try not to repeat code (this is basically part of check_creds_and_get_session_id_or_none). maybe let's make (this could also be postponed to a later PR, but we should make a note). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK so. This one is interesting. I tried making its own function via the following:
However, when we run this function, I get problems with this function:
The error I get is that it can't get user.salt for NoneType There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh looking a little closer it might be because it finds no user in the db and then returns None... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm confused, it looks there are now 3 functions which repeat a lot of code?:
Right now 1 and 2 duplicate a lot of the same code. I recommend removing |
||
result = self.users_col.find({"username": username}) | ||
if result.count() == 0: | ||
return None | ||
assert result.count() == 1, "WE HAVE DUPLICATE USERNAMES IN THE DB" | ||
user = M.User.load(result[0], context='db') | ||
assert user, "mongo has stopped being consistent, abort ship" | ||
return verify_password(password, user.salt, user.hashed_password) | ||
|
||
def get_and_verify_user_by_username(self, username): | ||
result = self.users_col.find({"username": username}) | ||
if result.count() == 0: | ||
return None | ||
assert result.count() == 1, "WE HAVE DUPLICATE USERNAMES IN THE DB" | ||
user = M.User.load(result[0], context='db') | ||
assert user, "mongo has stopped being consistent, abort ship" | ||
return user | ||
|
||
def update_session_id_for_user(self, user_id, session_id): | ||
# lets force people to have only one session at a time | ||
self.sessions_col.remove({"user_id": user_id}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
TYPEAHEAD_PLAYER_LIMIT = 20 | ||
BASE_REGION = 'newjersey' | ||
|
||
|
||
# parse config file | ||
config = Config() | ||
|
||
|
@@ -118,6 +119,10 @@ | |
admin_functions_parser.add_argument('new_user_permissions', location='json', type=str) | ||
admin_functions_parser.add_argument('new_user_regions', location='json', type=list) | ||
|
||
user_parser = reqparse.RequestParser() | ||
user_parser.add_argument('old_pass', location='json', type=str) | ||
user_parser.add_argument('new_pass', location='json', type=str) | ||
|
||
#TODO: major refactor to move auth code to a decorator | ||
|
||
|
||
|
@@ -137,6 +142,8 @@ def get_user_from_request(request, dao): | |
|
||
|
||
def is_user_admin_for_region(user, region): | ||
if user.admin_level == 'SUPER': | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we want to stick with the Enum method, these 'SUPER's and everything should be M.AdminLevels.SUPERs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Going with the String method. so no need to change this |
||
return True | ||
if not region: | ||
return False | ||
if not user.admin_regions: | ||
|
@@ -150,7 +157,9 @@ def is_user_admin_for_regions(user, regions): | |
''' | ||
returns true is user is an admin for ANY of the regions | ||
''' | ||
if len(set(regions).intersection(user.admin_regions)) == 0: | ||
if user.admin_level == 'SUPER': | ||
return True | ||
elif len(set(regions).intersection(user.admin_regions)) == 0: | ||
return False | ||
else: | ||
return True | ||
|
@@ -1163,6 +1172,28 @@ def get(self): | |
|
||
return return_dict | ||
|
||
class UserResource(restful.Resource): | ||
def put(self): | ||
dao = Dao(None, mongo_client=mongo_client) | ||
|
||
if not dao: | ||
return 'Dao not found', 404 | ||
user = get_user_from_request(request, dao) | ||
if not user: | ||
return 'Permission denied', 403 | ||
|
||
args = user_parser.parse_args() | ||
old_pass = args['old_pass'] | ||
new_pass = args['new_pass'] | ||
|
||
try: | ||
if dao.check_creds(user.username, old_pass): | ||
dao.change_passwd(user.username, new_pass) | ||
return 200 | ||
else: return 'Bad password', 403 | ||
except Exception as ex: | ||
return 'Password change not successful', 400 | ||
|
||
|
||
class AdminFunctionsResource(restful.Resource): | ||
def get(self): | ||
|
@@ -1176,8 +1207,8 @@ def put(self): | |
user = get_user_from_request(request, dao) | ||
if not user: | ||
return 'Permission denied', 403 | ||
#if not is_user_admin_for_region(user, region='*'): | ||
# return 'Permission denied', 403 | ||
if not user.admin_level == 'SUPER': | ||
return 'Permission denied', 403 | ||
|
||
args = admin_functions_parser.parse_args() | ||
|
||
|
@@ -1196,9 +1227,12 @@ def put(self): | |
uperm = args['new_user_permissions'] | ||
uregions = args['new_user_regions'] | ||
|
||
perm = None | ||
if uperm is not 'REGION' and uperm is not 'SUPER': return 'Invalid permission selection!', 403 | ||
|
||
#Execute user addition | ||
dao = Dao(None, mongo_client) | ||
if dao.create_user(uname, upass, uregions): | ||
if dao.create_user(uname, upass, uregions, uperm): | ||
print("user created:" + uname) | ||
|
||
@api.representation('text/plain') | ||
|
@@ -1273,6 +1307,8 @@ def add_cors(resp): | |
|
||
api.add_resource(SessionResource, '/users/session') | ||
|
||
api.add_resource(UserResource, '/user') | ||
|
||
api.add_resource(LoaderIOTokenResource, | ||
'/{}/'.format(config.get_loaderio_token())) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is probably better to use
get_user_by_id_or_none
here instead of rewriting the Mongo queries (so something likeuser = get_user_by_id_or_none(user_id)
.Actually, do we use this function (get_is_super_admin) anywhere else in the code? If not, I think it's fine to remove this function (it is easy enough to just check whether user.admin_level=='SUPER' whenever we have a user object).