server/users: fix hiding email from admins etc
This commit is contained in:
parent
d67a1b2f1c
commit
da5b32feeb
12 changed files with 36 additions and 22 deletions
3
API.md
3
API.md
|
@ -1181,7 +1181,8 @@ A single user.
|
||||||
**Field meaning**
|
**Field meaning**
|
||||||
- `<name>`: the user name.
|
- `<name>`: the user name.
|
||||||
- `<email>`: the user email. It is available only if the request is
|
- `<email>`: the user email. It is available only if the request is
|
||||||
authenticated by the same user.
|
authenticated by the same user, or the authenticated user can change the
|
||||||
|
email.
|
||||||
- `<rank>`: the user rank, which effectively affects their privileges. The
|
- `<rank>`: the user rank, which effectively affects their privileges. The
|
||||||
available ranks are stored in the server configuration.
|
available ranks are stored in the server configuration.
|
||||||
- `<rank-name>`: the text representation of user's rank. Like `<rank>`, the
|
- `<rank-name>`: the text representation of user's rank. Like `<rank>`, the
|
||||||
|
|
|
@ -27,7 +27,8 @@ class UserListApi(BaseApi):
|
||||||
ctx.get_file('avatar'))
|
ctx.get_file('avatar'))
|
||||||
ctx.session.add(user)
|
ctx.session.add(user)
|
||||||
ctx.session.commit()
|
ctx.session.commit()
|
||||||
return users.serialize_user_with_details(user, ctx.user)
|
return users.serialize_user_with_details(
|
||||||
|
user, ctx.user, force_show_email=True)
|
||||||
|
|
||||||
class UserDetailApi(BaseApi):
|
class UserDetailApi(BaseApi):
|
||||||
def get(self, ctx, user_name):
|
def get(self, ctx, user_name):
|
||||||
|
|
|
@ -36,15 +36,16 @@ def is_valid_password(user, password):
|
||||||
]
|
]
|
||||||
return valid_hash in possible_hashes
|
return valid_hash in possible_hashes
|
||||||
|
|
||||||
def verify_privilege(user, privilege_name):
|
def has_privilege(user, privilege_name):
|
||||||
''' Throw an AuthError if the given user doesn't have given privilege. '''
|
|
||||||
all_ranks = config.config['ranks']
|
all_ranks = config.config['ranks']
|
||||||
|
|
||||||
assert privilege_name in config.config['privileges']
|
assert privilege_name in config.config['privileges']
|
||||||
assert user.rank in all_ranks
|
assert user.rank in all_ranks
|
||||||
minimal_rank = config.config['privileges'][privilege_name]
|
minimal_rank = config.config['privileges'][privilege_name]
|
||||||
good_ranks = all_ranks[all_ranks.index(minimal_rank):]
|
good_ranks = all_ranks[all_ranks.index(minimal_rank):]
|
||||||
if user.rank not in good_ranks:
|
return user.rank in good_ranks
|
||||||
|
|
||||||
|
def verify_privilege(user, privilege_name):
|
||||||
|
if not has_privilege(user, privilege_name):
|
||||||
raise errors.AuthError('Insufficient privileges to do this.')
|
raise errors.AuthError('Insufficient privileges to do this.')
|
||||||
|
|
||||||
def generate_authentication_token(user):
|
def generate_authentication_token(user):
|
||||||
|
|
|
@ -13,7 +13,7 @@ class InvalidPasswordError(errors.ValidationError): pass
|
||||||
class InvalidRankError(errors.ValidationError): pass
|
class InvalidRankError(errors.ValidationError): pass
|
||||||
class InvalidAvatarError(errors.ValidationError): pass
|
class InvalidAvatarError(errors.ValidationError): pass
|
||||||
|
|
||||||
def serialize_user(user, authenticated_user):
|
def serialize_user(user, authenticated_user, force_show_email=False):
|
||||||
if not user:
|
if not user:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@ -23,7 +23,8 @@ def serialize_user(user, authenticated_user):
|
||||||
'rankName': config.config['rank_names'].get(user.rank, 'Unknown'),
|
'rankName': config.config['rank_names'].get(user.rank, 'Unknown'),
|
||||||
'creationTime': user.creation_time,
|
'creationTime': user.creation_time,
|
||||||
'lastLoginTime': user.last_login_time,
|
'lastLoginTime': user.last_login_time,
|
||||||
'avatarStyle': user.avatar_style
|
'avatarStyle': user.avatar_style,
|
||||||
|
'email': user.email,
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.avatar_style == user.AVATAR_GRAVATAR:
|
if user.avatar_style == user.AVATAR_GRAVATAR:
|
||||||
|
@ -36,13 +37,15 @@ def serialize_user(user, authenticated_user):
|
||||||
ret['avatarUrl'] = '%s/avatars/%s.jpg' % (
|
ret['avatarUrl'] = '%s/avatars/%s.jpg' % (
|
||||||
config.config['data_url'].rstrip('/'), user.name.lower())
|
config.config['data_url'].rstrip('/'), user.name.lower())
|
||||||
|
|
||||||
if authenticated_user.user_id == user.user_id:
|
if authenticated_user.user_id != user.user_id \
|
||||||
ret['email'] = user.email
|
and not force_show_email \
|
||||||
|
and not auth.has_privilege(authenticated_user, 'users:edit:any:email'):
|
||||||
|
del ret['email']
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def serialize_user_with_details(user, authenticated_user):
|
def serialize_user_with_details(user, authenticated_user, **kwargs):
|
||||||
return {'user': serialize_user(user, authenticated_user)}
|
return {'user': serialize_user(user, authenticated_user, **kwargs)}
|
||||||
|
|
||||||
def get_user_count():
|
def get_user_count():
|
||||||
return db.session.query(db.User).count()
|
return db.session.query(db.User).count()
|
||||||
|
|
|
@ -136,7 +136,6 @@ class SearchExecutor(object):
|
||||||
sort_desc: lambda input: input.desc(),
|
sort_desc: lambda input: input.desc(),
|
||||||
None: lambda input: input,
|
None: lambda input: input,
|
||||||
}
|
}
|
||||||
print(sort)
|
|
||||||
transform = transform_map[sort]
|
transform = transform_map[sort]
|
||||||
return query.order_by(transform(column))
|
return query.order_by(transform(column))
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,12 @@ from szurubooru.func import util, comments, scores
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def test_ctx(config_injector, context_factory, user_factory, comment_factory):
|
def test_ctx(config_injector, context_factory, user_factory, comment_factory):
|
||||||
config_injector({
|
config_injector({
|
||||||
'ranks': ['anonymous', 'regular_user'],
|
'ranks': ['anonymous', 'regular_user', 'mod'],
|
||||||
'rank_names': {'anonymous': 'Peasant', 'regular_user': 'Lord'},
|
'rank_names': {'anonymous': 'Peasant', 'regular_user': 'Lord'},
|
||||||
'privileges': {'comments:score': 'regular_user'},
|
'privileges': {
|
||||||
|
'comments:score': 'regular_user',
|
||||||
|
'users:edit:any:email': 'mod',
|
||||||
|
},
|
||||||
'thumbnails': {'avatar_width': 200},
|
'thumbnails': {'avatar_width': 200},
|
||||||
})
|
})
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
|
|
|
@ -6,13 +6,14 @@ from szurubooru.func import util, comments
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def test_ctx(context_factory, config_injector, user_factory, comment_factory):
|
def test_ctx(context_factory, config_injector, user_factory, comment_factory):
|
||||||
config_injector({
|
config_injector({
|
||||||
|
'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
|
||||||
|
'rank_names': {'regular_user': 'Peasant'},
|
||||||
'privileges': {
|
'privileges': {
|
||||||
'comments:list': 'regular_user',
|
'comments:list': 'regular_user',
|
||||||
'comments:view': 'regular_user',
|
'comments:view': 'regular_user',
|
||||||
|
'users:edit:any:email': 'mod',
|
||||||
},
|
},
|
||||||
'thumbnails': {'avatar_width': 200},
|
'thumbnails': {'avatar_width': 200},
|
||||||
'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
|
|
||||||
'rank_names': {'regular_user': 'Peasant'},
|
|
||||||
})
|
})
|
||||||
ret = util.dotdict()
|
ret = util.dotdict()
|
||||||
ret.context_factory = context_factory
|
ret.context_factory = context_factory
|
||||||
|
|
|
@ -11,6 +11,7 @@ def test_ctx(config_injector, context_factory, user_factory, comment_factory):
|
||||||
'privileges': {
|
'privileges': {
|
||||||
'comments:edit:self': 'regular_user',
|
'comments:edit:self': 'regular_user',
|
||||||
'comments:edit:any': 'mod',
|
'comments:edit:any': 'mod',
|
||||||
|
'users:edit:any:email': 'mod',
|
||||||
},
|
},
|
||||||
'thumbnails': {'avatar_width': 200},
|
'thumbnails': {'avatar_width': 200},
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,9 +6,12 @@ from szurubooru.func import util, posts
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def test_ctx(config_injector, context_factory, user_factory, post_factory):
|
def test_ctx(config_injector, context_factory, user_factory, post_factory):
|
||||||
config_injector({
|
config_injector({
|
||||||
'ranks': ['anonymous', 'regular_user'],
|
'ranks': ['anonymous', 'regular_user', 'mod'],
|
||||||
'rank_names': {'anonymous': 'Peasant', 'regular_user': 'Lord'},
|
'rank_names': {'anonymous': 'Peasant', 'regular_user': 'Lord'},
|
||||||
'privileges': {'posts:favorite': 'regular_user'},
|
'privileges': {
|
||||||
|
'posts:favorite': 'regular_user',
|
||||||
|
'users:edit:any:email': 'mod',
|
||||||
|
},
|
||||||
'thumbnails': {'avatar_width': 200},
|
'thumbnails': {'avatar_width': 200},
|
||||||
})
|
})
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
|
|
|
@ -46,6 +46,7 @@ def test_creating_user(test_ctx, fake_datetime):
|
||||||
'name': 'chewie1',
|
'name': 'chewie1',
|
||||||
'rank': 'admin',
|
'rank': 'admin',
|
||||||
'rankName': 'Unknown',
|
'rankName': 'Unknown',
|
||||||
|
'email': 'asd@asd.asd',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
user = users.get_user_by_name('chewie1')
|
user = users.get_user_by_name('chewie1')
|
||||||
|
|
|
@ -6,13 +6,14 @@ from szurubooru.func import util, users
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def test_ctx(context_factory, config_injector, user_factory):
|
def test_ctx(context_factory, config_injector, user_factory):
|
||||||
config_injector({
|
config_injector({
|
||||||
|
'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
|
||||||
|
'rank_names': {'regular_user': 'Peasant'},
|
||||||
'privileges': {
|
'privileges': {
|
||||||
'users:list': 'regular_user',
|
'users:list': 'regular_user',
|
||||||
'users:view': 'regular_user',
|
'users:view': 'regular_user',
|
||||||
|
'users:edit:any:email': 'mod',
|
||||||
},
|
},
|
||||||
'thumbnails': {'avatar_width': 200},
|
'thumbnails': {'avatar_width': 200},
|
||||||
'ranks': ['anonymous', 'regular_user', 'mod', 'admin'],
|
|
||||||
'rank_names': {'regular_user': 'Peasant'},
|
|
||||||
})
|
})
|
||||||
ret = util.dotdict()
|
ret = util.dotdict()
|
||||||
ret.context_factory = context_factory
|
ret.context_factory = context_factory
|
||||||
|
|
|
@ -12,7 +12,6 @@ def verify_unpaged(executor):
|
||||||
def verify(input, expected_comment_text):
|
def verify(input, expected_comment_text):
|
||||||
actual_count, actual_comments = executor.execute(
|
actual_count, actual_comments = executor.execute(
|
||||||
input, page=1, page_size=100)
|
input, page=1, page_size=100)
|
||||||
print(actual_comments)
|
|
||||||
actual_comment_text = [c.text for c in actual_comments]
|
actual_comment_text = [c.text for c in actual_comments]
|
||||||
assert actual_count == len(expected_comment_text)
|
assert actual_count == len(expected_comment_text)
|
||||||
assert actual_comment_text == expected_comment_text
|
assert actual_comment_text == expected_comment_text
|
||||||
|
|
Loading…
Reference in a new issue