2016-04-16 13:07:33 +00:00
|
|
|
import datetime
|
2016-04-03 20:03:58 +00:00
|
|
|
import re
|
2016-04-06 15:12:40 +00:00
|
|
|
from sqlalchemy import func
|
2016-04-03 20:03:58 +00:00
|
|
|
from szurubooru import config, db, errors
|
2016-04-20 09:59:30 +00:00
|
|
|
from szurubooru.func import auth, util, files, images
|
2016-04-03 20:03:58 +00:00
|
|
|
|
2016-04-16 13:07:33 +00:00
|
|
|
class UserNotFoundError(errors.NotFoundError): pass
|
|
|
|
class UserAlreadyExistsError(errors.ValidationError): pass
|
2016-04-19 09:50:47 +00:00
|
|
|
class InvalidUserNameError(errors.ValidationError): pass
|
2016-04-16 13:07:33 +00:00
|
|
|
class InvalidEmailError(errors.ValidationError): pass
|
|
|
|
class InvalidPasswordError(errors.ValidationError): pass
|
|
|
|
class InvalidRankError(errors.ValidationError): pass
|
|
|
|
class InvalidAvatarError(errors.ValidationError): pass
|
|
|
|
|
2016-05-30 20:07:44 +00:00
|
|
|
def _get_avatar_url(user):
|
2016-04-24 07:47:58 +00:00
|
|
|
if user.avatar_style == user.AVATAR_GRAVATAR:
|
2016-05-30 20:07:44 +00:00
|
|
|
return 'http://gravatar.com/avatar/%s?d=retro&s=%d' % (
|
2016-04-30 21:17:08 +00:00
|
|
|
util.get_md5((user.email or user.name).lower()),
|
|
|
|
config.config['thumbnails']['avatar_width'])
|
2016-04-24 07:47:58 +00:00
|
|
|
else:
|
2016-05-30 20:07:44 +00:00
|
|
|
return '%s/avatars/%s.png' % (
|
2016-04-24 07:47:58 +00:00
|
|
|
config.config['data_url'].rstrip('/'), user.name.lower())
|
|
|
|
|
2016-05-30 20:07:44 +00:00
|
|
|
def _get_email(user, authenticated_user, force_show_email):
|
|
|
|
if not force_show_email \
|
|
|
|
and authenticated_user.user_id != user.user_id \
|
|
|
|
and not auth.has_privilege(authenticated_user, 'users:edit:any:email'):
|
|
|
|
return False
|
|
|
|
return user.email
|
2016-04-24 07:47:58 +00:00
|
|
|
|
2016-05-30 21:08:22 +00:00
|
|
|
def serialize_user(user, authenticated_user, options=None, force_show_email=False):
|
2016-05-30 20:07:44 +00:00
|
|
|
return util.serialize_entity(
|
|
|
|
user,
|
|
|
|
{
|
|
|
|
'name': lambda: user.name,
|
|
|
|
'rank': lambda: user.rank,
|
|
|
|
'creationTime': lambda: user.creation_time,
|
|
|
|
'lastLoginTime': lambda: user.last_login_time,
|
|
|
|
'avatarStyle': lambda: user.avatar_style,
|
|
|
|
'avatarUrl': lambda: _get_avatar_url(user),
|
|
|
|
'email': lambda: _get_email(user, authenticated_user, force_show_email),
|
2016-05-30 21:08:22 +00:00
|
|
|
},
|
|
|
|
options)
|
2016-04-28 16:20:50 +00:00
|
|
|
|
2016-04-18 20:41:39 +00:00
|
|
|
def get_user_count():
|
|
|
|
return db.session.query(db.User).count()
|
|
|
|
|
2016-04-24 12:24:41 +00:00
|
|
|
def try_get_user_by_name(name):
|
2016-04-19 15:39:16 +00:00
|
|
|
return db.session \
|
|
|
|
.query(db.User) \
|
2016-04-16 13:07:33 +00:00
|
|
|
.filter(func.lower(db.User.name) == func.lower(name)) \
|
2016-04-24 12:24:41 +00:00
|
|
|
.one_or_none()
|
2016-04-16 13:07:33 +00:00
|
|
|
|
2016-04-24 12:24:41 +00:00
|
|
|
def get_user_by_name(name):
|
|
|
|
user = try_get_user_by_name(name)
|
|
|
|
if not user:
|
|
|
|
raise UserNotFoundError('User %r not found.' % name)
|
|
|
|
return user
|
|
|
|
|
|
|
|
def try_get_user_by_name_or_email(name_or_email):
|
2016-04-19 15:39:16 +00:00
|
|
|
return db.session \
|
|
|
|
.query(db.User) \
|
2016-04-16 13:07:33 +00:00
|
|
|
.filter(
|
|
|
|
(func.lower(db.User.name) == func.lower(name_or_email))
|
|
|
|
| (func.lower(db.User.email) == func.lower(name_or_email))) \
|
2016-04-24 12:24:41 +00:00
|
|
|
.one_or_none()
|
|
|
|
|
|
|
|
def get_user_by_name_or_email(name_or_email):
|
|
|
|
user = try_get_user_by_name_or_email(name_or_email)
|
|
|
|
if not user:
|
|
|
|
raise UserNotFoundError('User %r not found.' % name_or_email)
|
|
|
|
return user
|
2016-04-16 13:07:33 +00:00
|
|
|
|
2016-05-08 15:03:55 +00:00
|
|
|
def create_user(name, password, email):
|
2016-04-03 20:03:58 +00:00
|
|
|
user = db.User()
|
2016-05-08 15:03:55 +00:00
|
|
|
update_user_name(user, name)
|
2016-04-30 21:53:50 +00:00
|
|
|
update_user_password(user, password)
|
|
|
|
update_user_email(user, email)
|
2016-04-18 20:41:39 +00:00
|
|
|
if get_user_count() > 0:
|
2016-05-10 09:58:55 +00:00
|
|
|
user.rank = util.flip(auth.RANK_MAP)[config.config['default_rank']]
|
2016-04-18 20:41:39 +00:00
|
|
|
else:
|
2016-05-08 14:59:25 +00:00
|
|
|
user.rank = db.User.RANK_ADMINISTRATOR
|
2016-04-16 13:07:33 +00:00
|
|
|
user.creation_time = datetime.datetime.now()
|
2016-04-03 20:03:58 +00:00
|
|
|
user.avatar_style = db.User.AVATAR_GRAVATAR
|
|
|
|
return user
|
|
|
|
|
2016-05-08 15:03:55 +00:00
|
|
|
def update_user_name(user, name):
|
2016-04-19 09:50:47 +00:00
|
|
|
if not name:
|
|
|
|
raise InvalidUserNameError('Name cannot be empty.')
|
2016-04-20 09:59:30 +00:00
|
|
|
if util.value_exceeds_column_size(name, db.User.name):
|
2016-04-19 09:50:47 +00:00
|
|
|
raise InvalidUserNameError('User name is too long.')
|
2016-04-24 12:24:41 +00:00
|
|
|
other_user = try_get_user_by_name(name)
|
2016-05-08 15:03:55 +00:00
|
|
|
if other_user and other_user.user_id != user.user_id:
|
2016-04-16 13:07:33 +00:00
|
|
|
raise UserAlreadyExistsError('User %r already exists.' % name)
|
2016-04-03 20:03:58 +00:00
|
|
|
name = name.strip()
|
2016-04-06 18:38:45 +00:00
|
|
|
name_regex = config.config['user_name_regex']
|
2016-04-03 20:03:58 +00:00
|
|
|
if not re.match(name_regex, name):
|
2016-04-19 09:50:47 +00:00
|
|
|
raise InvalidUserNameError(
|
2016-04-16 13:07:33 +00:00
|
|
|
'User name %r must satisfy regex %r.' % (name, name_regex))
|
2016-04-03 20:03:58 +00:00
|
|
|
user.name = name
|
|
|
|
|
2016-04-30 21:53:50 +00:00
|
|
|
def update_user_password(user, password):
|
2016-04-19 09:50:47 +00:00
|
|
|
if not password:
|
|
|
|
raise InvalidPasswordError('Password cannot be empty.')
|
2016-04-06 18:38:45 +00:00
|
|
|
password_regex = config.config['password_regex']
|
2016-04-03 20:03:58 +00:00
|
|
|
if not re.match(password_regex, password):
|
2016-04-16 13:07:33 +00:00
|
|
|
raise InvalidPasswordError(
|
2016-04-03 20:03:58 +00:00
|
|
|
'Password must satisfy regex %r.' % password_regex)
|
|
|
|
user.password_salt = auth.create_password()
|
|
|
|
user.password_hash = auth.get_password_hash(user.password_salt, password)
|
|
|
|
|
2016-04-30 21:53:50 +00:00
|
|
|
def update_user_email(user, email):
|
2016-04-19 09:50:47 +00:00
|
|
|
if email:
|
|
|
|
email = email.strip()
|
|
|
|
if not email:
|
|
|
|
email = None
|
2016-04-20 09:59:30 +00:00
|
|
|
if email and util.value_exceeds_column_size(email, db.User.email):
|
2016-04-16 13:07:33 +00:00
|
|
|
raise InvalidEmailError('Email is too long.')
|
2016-04-20 09:59:30 +00:00
|
|
|
if not util.is_valid_email(email):
|
2016-04-16 13:07:33 +00:00
|
|
|
raise InvalidEmailError('E-mail is invalid.')
|
2016-04-03 20:03:58 +00:00
|
|
|
user.email = email
|
|
|
|
|
2016-04-30 21:53:50 +00:00
|
|
|
def update_user_rank(user, rank, authenticated_user):
|
2016-04-19 09:50:47 +00:00
|
|
|
if not rank:
|
|
|
|
raise InvalidRankError('Rank cannot be empty.')
|
2016-05-10 09:58:55 +00:00
|
|
|
rank = util.flip(auth.RANK_MAP).get(rank.strip(), None)
|
|
|
|
all_ranks = list(auth.RANK_MAP.values())
|
|
|
|
if not rank:
|
2016-04-16 13:07:33 +00:00
|
|
|
raise InvalidRankError(
|
2016-05-10 09:58:55 +00:00
|
|
|
'Rank can be either of %r.' % all_ranks)
|
2016-05-08 16:02:19 +00:00
|
|
|
if rank in (db.User.RANK_ANONYMOUS, db.User.RANK_NOBODY):
|
2016-05-10 09:58:55 +00:00
|
|
|
raise InvalidRankError('Rank %r cannot be used.' % auth.RANK_MAP[rank])
|
|
|
|
if all_ranks.index(authenticated_user.rank) \
|
|
|
|
< all_ranks.index(rank) and get_user_count() > 0:
|
2016-04-09 19:41:10 +00:00
|
|
|
raise errors.AuthError('Trying to set higher rank than your own.')
|
2016-04-06 17:16:44 +00:00
|
|
|
user.rank = rank
|
2016-04-03 20:03:58 +00:00
|
|
|
|
2016-04-30 21:53:50 +00:00
|
|
|
def update_user_avatar(user, avatar_style, avatar_content):
|
2016-04-09 19:41:10 +00:00
|
|
|
if avatar_style == 'gravatar':
|
|
|
|
user.avatar_style = user.AVATAR_GRAVATAR
|
|
|
|
elif avatar_style == 'manual':
|
|
|
|
user.avatar_style = user.AVATAR_MANUAL
|
|
|
|
if not avatar_content:
|
2016-04-16 13:07:33 +00:00
|
|
|
raise InvalidAvatarError('Avatar content missing.')
|
2016-04-09 19:41:10 +00:00
|
|
|
image = images.Image(avatar_content)
|
|
|
|
image.resize_fill(
|
|
|
|
int(config.config['thumbnails']['avatar_width']),
|
|
|
|
int(config.config['thumbnails']['avatar_height']))
|
2016-05-08 15:29:23 +00:00
|
|
|
files.save('avatars/' + user.name.lower() + '.png', image.to_png())
|
2016-04-09 19:41:10 +00:00
|
|
|
else:
|
2016-04-16 13:07:33 +00:00
|
|
|
raise InvalidAvatarError(
|
|
|
|
'Avatar style %r is invalid. Valid avatar styles: %r.' % (
|
|
|
|
avatar_style, ['gravatar', 'manual']))
|
2016-04-09 19:41:10 +00:00
|
|
|
|
2016-04-30 21:53:50 +00:00
|
|
|
def bump_user_login_time(user):
|
2016-04-16 13:07:33 +00:00
|
|
|
user.last_login_time = datetime.datetime.now()
|
2016-04-03 20:03:58 +00:00
|
|
|
|
2016-04-30 21:53:50 +00:00
|
|
|
def reset_user_password(user):
|
2016-04-03 20:03:58 +00:00
|
|
|
password = auth.create_password()
|
|
|
|
user.password_salt = auth.create_password()
|
|
|
|
user.password_hash = auth.get_password_hash(user.password_salt, password)
|
|
|
|
return password
|