async def create_group(name, permission_pairs, database=None): """Create a group with specified permissions on targets, plus all join tables. :param name: Group name. :param permission_pairs: Tuple. List of permission names/targets to create. :param database: String. Database id to use, or default for AUTH. :return: Int. Group id created. """ database = database_key(database) async with Connection(database) as con: stmt = group.insert().values(name=name) await con.execute(stmt) query = sa.select('*').select_from(group).where( group.c.name == name).order_by(group.c.id.desc()) grp = await con.fetchrow(query) perms = [] for permission_pair in permission_pairs: stmt = permission.insert().values(**permission_pair) await con.execute(stmt) query = sa.select('*').select_from(permission).order_by( permission.c.id.desc()) perm = await con.fetchrow(query) perms.append(perm) for perm in perms: stmt = group_permission.insert().values( permission_id=perm.get('id'), group_id=grp.get('id') ) await con.execute(stmt) return grp.get('id')
async def check_permission(user_row, name, target, database=None): """Check permission via groups. :param user_row: SQLAlchemy Record. User row to check. :param name: String. Name of permission. :param target: String. Target for permission (eg a table). :return: Boolean. If user has permission on target. """ # TODO: Clean this up. if user_row.get('is_superuser'): return True database = database_key(database) async with Connection(database) as con: query = sa.select('*').select_from(user_group).where( user_group.c.user_id == user_row.get('id')) rows = await con.fetch(query) group_ids = [row.get('group_id') for row in rows] if not group_ids: return False query = sa.select('*').select_from(group_permission).where( group_permission.c.group_id.in_(group_ids)) rows = await con.fetch(query) permission_ids = [row.get('permission_id') for row in rows] if not permission_ids: return False query = sa.select('*').select_from(permission) \ .where(permission.c.id.in_(permission_ids)) \ .where(permission.c.name == name) \ .where(permission.c.target == target) row = await con.fetchrow(query) if row: return True return False
async def add_user_to_group(user_id, group_id, database=None): """Add user to group :param user_id: Int. User id to add. :param group_id: Int. Group id to add user to. :param database: String. Database id to use, or default for AUTH. """ database = database_key(database) async with Connection(database) as con: stmt = user_group.insert().values( user_id=user_id, group_id=group_id ) await con.execute(stmt)
async def create_user(username, password, first_name=None, last_name=None, email=None, user_type=None, is_staff=False, is_superuser=False, is_active=True, date_joined=None, database=None): """Create a user. :param username: String. Username. :param password: String. Password. Will be stored encoded. :param first_name: String. User's first name. (Default None) :param last_name: String. User's last name. (Default None) :param user_type: String. Optionally segment users with string categories. (Default None) :param is_active: Boolean. If user account is active. (Default True) :param is_staff: Boolean. If user is "staff" and has "admin" level access. (Default False) :param is_superuser: Boolean. If user is a "superuser" and has all permissions without being explicitly assigned. (Default False) :param date_joined: Datetime (with timezone). Date user account was created. :param database: String. Database name to connect to. (Default: None - use jawaf.auth default) """ database = database_key(database) if date_joined is None: date_joined = get_utc(datetime.datetime.now()) async with Connection(database) as con: stmt = user.insert().values(username=username, password=make_password(password), first_name=first_name, last_name=last_name, email=email, user_type=user_type, is_staff=is_staff, is_superuser=is_superuser, is_active=is_active, date_joined=date_joined, last_login=None) await con.execute(stmt)
async def check_user(username='', password='', database=None): """Check a user username/password combination agains the database. :param username: String. Username to check. :param password: String. password to verify. :param database: String. Database name to connect to. (Default: None - use jawaf.auth default) :return: Boolean. If username and password are a valid combination. """ database = database_key(database) async with Connection(database) as con: query = sa.select('*').select_from(user).where( user.c.username == username) row = await con.fetchrow(query) if not row: return False if check_password(password, row.get('password')): return row return False
async def check_user_reset_access(username, user_id, split_token, database=None): """Check password reset token for the current user. :param username: String. Username to check. :param user_id: Int. User_id.. :param split_token: String. Split token to search for and validate against. :param database: String. Database name to connect to. (Default: None - use jawaf.auth default) """ if username is None or user_id is None: return False database = database_key(database) selector = split_token[:SELECTOR_ENCODED_LENGTH].encode('utf-8') verifier = split_token[SELECTOR_ENCODED_LENGTH:].encode('utf-8') async with Connection(database) as con: query = sa.select('*').select_from(user) \ .where(user.c.username == username) \ .where(user.c.id == user_id) row = await con.fetchrow(query) if not row: # username and id don't match! return False query = sa.select('*').select_from(user_password_reset) \ .where(user_password_reset.c.selector == str(selector)) \ .where(user_password_reset.c.user_id == user_id) row = await con.fetchrow(query) if not row: # Selector not found - invalid link. return False if get_utc(datetime.datetime.now()) > row.get('expires'): # First remove the expired record. stmt = user_password_reset.delete().where( user_password_reset.c.id == row.get('id')) await con.execute(stmt) return False if hashlib.sha256(verifier).hexdigest() == row.get('verifier'): # Only allow this reset token to be used once. stmt = user_password_reset.delete().where( user_password_reset.c.id == row.get('id')) await con.execute(stmt) return True return False
async def generate_reset_split_token(user_id, database=None): """Generate a password reset token for the current user. :param user_id: Int. User id to generate split token for. :param database: String. Database name to connect to. (Default: None - use jawaf.auth default) :return: String. Joined token. """ database = database_key(database) selector, verifier = _generate_split_token() async with Connection(database) as con: stmt = user_password_reset.insert().values( user_id=user_id, selector=str(selector), verifier=hashlib.sha256(verifier).hexdigest(), expires=get_utc(datetime.datetime.now() + datetime.timedelta( hours=settings.AUTH_CONFIG['password_reset_expiration'])), ) await con.execute(stmt) return '{0}{1}'.format(selector.decode('utf-8'), verifier.decode('utf-8'))
async def update_user(database=None, target_username=None, target_user_id=None, **kwargs): """Update user. Accepts same optional kwargs as create_user + last_login. :param database: String. Database name to connect to. (Default: None - use jawaf.auth default) :param target_username: String. Username to search against. :param username: String. Username. :param password: String. Password. Will be stored encoded. :param first_name: String. User's first name. (Default None) :param last_name: String. User's last name. (Default None) :param user_type: String. Optionally segment users with string categories. (Default None) :param is_active: Boolean. If user account is active. (Default True) :param is_staff: Boolean. If user is "staff" and has "admin" level access. (Default False) :param is_superuser: Boolean. If user is a "superuser" and has all permissions without being explicitly assigned. (Default False) :param date_joined: Datetime (with timezone). Date user account was created. :param last_login: Datetime (with timezone). Date user account was last accessed. """ database = database_key(database) if 'password' in kwargs: kwargs['password'] = make_password(kwargs['password']) if not target_username and not target_user_id: raise ServerError('Must provide username or user_id to update') async with Connection(database) as con: if target_username: stmt = user.update().where( user.c.username == target_username).values(**kwargs) else: stmt = user.update().where(user.c.id == target_user_id).values( **kwargs) await con.execute(stmt)