async def remove_user(
        user_id: str,
        user: UserInfo = Depends(Authentication()),
):
    if 'admin' not in user['roles']:
        raise HTTPException(401)
    result = await async_user_collection.delete_one({'_id': user_id})
    if result.deleted_count != 1:
        raise HTTPException(404, "User does not exist")
    if user.picture:
        try:
            await async_user_picture_bucket.delete(user.picture)
        except gridfs.errors.NoFile:
            pass
    await async_session_collection.delete({'user_id': user_id})
    await async_authorization_code_collection.delete({'user_id': user_id})
    await async_token_collection.delete({'user_id': user_id})
    await async_user_group_collection.update_many({'members': user_id}, {
        '$pull': {
            'members': user_id,
            'email_forward_members': user_id,
            'email_allowed_forward_members': user_id,
            'email_postbox_access_members': user_id,
        }
    })
    await async_client_user_cache_collection.delete_many({'user_id': user_id})
async def request_reset_user_password(
        user_id: str,
        user: UserInfo = Depends(Authentication()),
):
    is_admin = 'admin' in user['roles']
    if not is_admin:
        raise HTTPException(401)
    user_data = DotDict.from_obj(await async_user_collection.find_one(
        {'_id': user_id}))
    if user_data is None:
        raise HTTPException(404, "User not found")
    if user_data.get('registration_token'):
        await update_resend_registration(user_data)
    else:
        token_valid_until = int(time.time() +
                                config.manager.token_valid.password_reset)
        user_data['password_reset_token'] = create_token(
            user_data['_id'], token_valid_until)

        await async_user_collection.update_one({'_id': user_data['_id']}, {
            '$set': {
                'password_reset_token': user_data['password_reset_token']
            },
        })
        await async_send_mail_reset_password(user_data, token_valid_until)
async def get_user(
    user_id: str, user: UserInfo = Depends(Authentication())) -> UserViewData:
    """Gets user data."""
    is_admin = 'admin' in user['roles']
    is_self = user.sub == user_id
    if not is_self and not is_admin:
        raise HTTPException(401)
    user_data = DotDict.from_obj(await async_user_collection.find_one(
        {'_id': user_id}))
    if user_data is None:
        raise HTTPException(404)
    user_data['sub'] = user_data['_id']
    if user_data.get('picture') is not None:
        user_data[
            'picture'] = f"{config.oauth2.base_url}/picture/{user_data['picture']}"
    if 'password' in user_data:
        del user_data['password']
    return UserViewData(
        user_id=user_data['_id'],
        properties=[
            UserPropertyWithValue(key=prop,
                                  value=_get_user_property_value(
                                      prop, user_data, is_self, is_admin),
                                  **config.oauth2.user.properties[prop].dict())
            for prop in config.manager.view
            if config.oauth2.user.properties[prop].can_read.has_access(
                is_self, is_admin) or config.oauth2.user.properties[prop].
            can_edit.has_access(is_self, is_admin)
        ])
async def delete_group(
        group_id: str,
        user: UserInfo = Depends(Authentication()),
):
    """Deletes a group."""
    if 'admin' not in user['roles']:
        raise HTTPException(401)
    await async_user_collection.update_many(
        {'groups': group_id},
        {
            '$pull': {
                'groups': group_id,
                'email_forward_groups': group_id,
                'email_allowed_forward_groups': group_id,
                'email_postbox_access_groups': group_id,
            },
            '$set': {'updated_at': int(time.time())},
        }
    )
    await async_client_collection.update_many(
        {'access_groups.group': group_id},
        {'$pull': {'access_groups': {'group': group_id}}},
    )
    await async_client_user_cache_collection.delete_many({'groups': group_id})
    await async_user_group_collection.update_many(
        {'member_groups': group_id},
        {'$pull': {
            'member_groups': group_id,
        }}
    )
    result = await async_user_group_collection.delete_one({'_id': group_id})
    if result.deleted_count != 1:
        raise HTTPException(404)
async def upload_picture(
        user_id: str,
        file: UploadFile = File(..., media_type='application/octet-stream'),
        user: UserInfo = Depends(Authentication(auto_error=False)),
        registration_token: Optional[str] = Header(None, alias="x-token"),
):
    """Uploads a new picture for the passed user."""
    if user is not None:
        is_admin = 'admin' in user['roles']
        is_self = user.sub == user_id
        if not is_self or not is_admin:
            raise HTTPException(401)
        user_data = await async_user_collection.find_one({'_id': user_id})
    elif registration_token is not None:
        check_token(registration_token)
        user_data = await async_user_collection.find_one(
            {'registration_token': registration_token})
    else:
        raise HTTPException(401)
    if user_data is None:
        raise HTTPException(404)
    user = User.validate(user_data)
    if user.picture is None:
        user.picture = generate_token(48)
        updated_at = int(time.time())
        await async_user_collection.update_one(
            {'_id': user_id},
            {'$set': {
                'picture': user.picture,
                'updated_at': updated_at
            }})
        await async_client_user_cache_collection.update_many(
            {'user_id': user_data['_id']},
            {'$set': {
                'last_modified': updated_at
            }},
        )
    else:
        try:
            await async_user_picture_bucket.delete(user.picture)
        except gridfs.errors.NoFile:
            pass
    hash_ = hashlib.sha512()
    while True:
        chunk = await file.read(4 * 1024)
        if not chunk:
            break
        hash_.update(chunk)
    await file.seek(0)
    file.file.seek(0)
    await async_user_picture_bucket.upload_from_stream_with_id(
        user.picture,
        user.id,
        file.file,
        metadata={
            'content_type': file.content_type,
            'hash': hash_.digest()
        })
Exemple #6
0
async def get_clients(
        user: UserInfo = Depends(Authentication()), ) -> List[ClientInList]:
    """Gets all clients."""
    if 'admin' not in user['roles']:
        raise HTTPException(401)
    return [
        ClientInList.validate(Client.validate(client))
        async for client in async_client_collection.find()
    ]
Exemple #7
0
async def create_client(
        client_data: ClientInCreate = Body(...),
        user: UserInfo = Depends(Authentication()),
):
    """Creates a client."""
    if 'admin' not in user['roles']:
        raise HTTPException(401)
    new_client = Client.validate(client_data)
    await async_client_collection.insert_one(
        new_client.dict(exclude_none=True, by_alias=True))
Exemple #8
0
async def get_client(
        client_id: str,
        user: UserInfo = Depends(Authentication()),
):
    """Gets a client."""
    if 'admin' not in user['roles']:
        raise HTTPException(401)
    client_data = await async_client_collection.find_one({'_id': client_id})
    if client_data is None:
        raise HTTPException(404)
    return ClientInRead.validate(Client.validate(client_data))
async def get_group(
        group_id: str,
        user: UserInfo = Depends(Authentication()),
):
    """Gets a group."""
    if 'admin' not in user['roles']:
        raise HTTPException(401)
    group_data = await async_user_group_collection.find_one({'_id': group_id})
    if group_data is None:
        raise HTTPException(404)
    return GroupInRead.validate(UserGroup.validate(group_data))
async def get_groups(
        user: UserInfo = Depends(Authentication()),
) -> List[GroupInList]:
    """Gets all groups."""
    if 'admin' in user['roles']:
        group_filter = {}
    else:
        group_filter = {'visible': True}
    return [
        GroupInList.validate(UserGroup.validate(group))
        async for group in async_user_group_collection.find(group_filter)
    ]
Exemple #11
0
async def delete_client(
        client_id: str,
        user: UserInfo = Depends(Authentication()),
):
    """Deletes a client."""
    if 'admin' not in user['roles']:
        raise HTTPException(401)
    await async_client_user_cache_collection.delete_many(
        {'client_id': client_id})
    result = await async_client_collection.delete_one({'_id': client_id})
    if result.deleted_count != 1:
        raise HTTPException(404)
Exemple #12
0
async def resend_registration(
        user_id: str,
        user: UserInfo = Depends(Authentication()),
):
    """Sends the registration token email again."""
    is_admin = 'admin' in user['roles']
    if not is_admin:
        raise HTTPException(401)
    user_data = DotDict.from_obj(await async_user_collection.find_one(
        {'_id': user_id}))
    if user_data is None:
        raise HTTPException(404)
    await update_resend_registration(user_data)
Exemple #13
0
async def create_user(no_registration: bool = Query(False),
                      create_data: Dict[str, Any] = Body(...),
                      user: UserInfo = Depends(Authentication())):
    """Updates user data."""
    is_admin = 'admin' in user['roles']
    if not is_admin:
        raise HTTPException(401)

    user_data = DotDict()
    await _update_user(user_data,
                       create_data,
                       is_new=True,
                       is_admin=True,
                       no_registration=no_registration)
async def create_group(
        group_data: GroupInCreate = Body(...),
        user: UserInfo = Depends(Authentication()),
):
    """Creates a group."""
    if 'admin' not in user['roles']:
        raise HTTPException(401)
    new_group = UserGroup.validate(group_data)
    new_group.id = new_group.id.lower()
    if not new_group.enable_email:
        new_group.email_forward_members = []
        new_group.email_allowed_forward_members = []
    if not new_group.enable_postbox:
        new_group.email_postbox_access_members = []
    await async_user_group_collection.insert_one(new_group.dict(exclude_none=True, by_alias=True))
    timestamp = int(time.time())
    if new_group.members:
        await async_client_user_cache_collection.delete_many({'user_id': {'$in': new_group.members}})
        await async_user_collection.update_many(
            {'_id': {'$in': new_group.members}},
            {
                '$addToSet': {'groups': new_group.id},
                '$set': {'updated_at': timestamp},
            }
        )
    if new_group.email_forward_members:
        await async_user_collection.update_many(
            {'_id': {'$in': new_group.email_forward_members}},
            {
                '$addToSet': {'email_forward_groups': new_group.id},
                '$set': {'updated_at': timestamp},
            }
        )
    if new_group.email_allowed_forward_members:
        await async_user_collection.update_many(
            {'_id': {'$in': new_group.email_allowed_forward_members}},
            {
                '$addToSet': {'email_allowed_forward_groups': new_group.id},
                '$set': {'updated_at': timestamp},
            }
        )
    if new_group.email_postbox_access_members:
        await async_user_collection.update_many(
            {'_id': {'$in': new_group.email_postbox_access_members}},
            {
                '$addToSet': {'email_postbox_access_groups': new_group.id},
                '$set': {'updated_at': timestamp},
            }
        )
Exemple #15
0
async def reverify_email(
        user_id: str,
        user: UserInfo = Depends(Authentication()),
):
    """Updates user data."""
    is_admin = 'admin' in user['roles']
    is_self = user.sub == user_id
    if not is_self and not is_admin:
        raise HTTPException(401)
    user_data = DotDict.from_obj(await async_user_collection.find_one(
        {'_id': user_id}))
    if user_data is None:
        raise HTTPException(404)
    user_data['email_verified'] = False
    await _update_user(user_data, {'email': user_data['email']},
                       is_admin=is_admin,
                       is_self=is_self)
Exemple #16
0
async def update_user(user_id: str,
                      update_data: Dict[str, Any] = Body(...),
                      user: UserInfo = Depends(Authentication())):
    """Updates user data."""
    is_admin = 'admin' in user['roles']
    is_self = user.sub == user_id
    if not is_self and not is_admin:
        raise HTTPException(401)
    user_data = DotDict.from_obj(await async_user_collection.find_one(
        {'_id': user_id}))
    if user_data is None:
        raise HTTPException(404)
    if not update_data:
        return
    await _update_user(user_data,
                       update_data,
                       is_admin=is_admin,
                       is_self=is_self)
Exemple #17
0
def get_create_user(user: UserInfo = Depends(
    Authentication())) -> UserViewData:
    """Gets user data for creation."""
    is_admin = 'admin' in user['roles']
    if not is_admin:
        raise HTTPException(401)

    return UserViewData(
        user_id="new",
        properties=[
            UserPropertyWithValue(
                key=prop,
                value=config.oauth2.user.properties[prop].default,
                **config.oauth2.user.properties[prop].dict())
            for prop in config.manager.view
            if config.oauth2.user.properties[prop].can_read.has_access(
                is_admin=is_admin) or config.oauth2.user.properties[prop].
            can_edit.has_access(is_admin=is_admin)
        ])
Exemple #18
0
async def update_client(
        client_id: str,
        client_update: ClientInWrite,
        user: UserInfo = Depends(Authentication()),
):
    """Updates a client."""
    if 'admin' not in user['roles']:
        raise HTTPException(401)
    if await async_client_collection.count_documents({'_id': client_id}) != 1:
        raise HTTPException(404)

    client = Client.validate(client_update)
    await async_client_user_cache_collection.delete_many(
        {'client_id': client_id})
    result = await async_client_collection.replace_one(
        {'_id': client_id},
        client.dict(exclude_none=True, by_alias=True),
    )
    if result.matched_count != 1:
        raise HTTPException(404)
Exemple #19
0
async def get_users(user: UserInfo = Depends(
    Authentication())) -> UsersListViewData:
    """Gets user data."""
    is_admin = 'admin' in user['roles']
    if not is_admin:
        raise HTTPException(401)
    users_data = async_user_collection.find()
    users: List[UserListViewData] = []
    async for user_data in users_data:
        user_data['sub'] = user_data['_id']
        if user_data.get('picture') is not None:
            user_data[
                'picture'] = f"{config.oauth2.base_url}/picture/{user_data['picture']}"
        if 'password' in user_data:
            del user_data['password']
        users.append(
            UserListViewData(
                user_id=user_data['_id'],
                properties=[
                    UserListProperty(
                        key=prop,
                        value=_get_user_property_value(
                            prop, user_data, user_data['_id'] == user.sub,
                            is_admin),
                    ) for prop in config.manager.list
                    if config.oauth2.user.properties[prop].can_read.has_access(
                        user_data['_id'] == user.sub, is_admin)
                    or config.oauth2.user.properties[prop].can_edit.has_access(
                        user_data['_id'] == user.sub, is_admin)
                ]))
    return UsersListViewData(
        properties=[
            UserPropertyWithKey(key=prop,
                                **config.oauth2.user.properties[prop].dict())
            for prop in config.manager.list
        ],
        users=users,
    )
async def update_group(
        group_id: str,
        group_update: GroupInWrite,
        user: UserInfo = Depends(Authentication()),
):
    """Updates a group."""
    if 'admin' not in user['roles']:
        raise HTTPException(401)
    group_data = await async_user_group_collection.find_one({'_id': group_id})
    if group_data is None:
        raise HTTPException(404)
    group = UserGroup.validate(group_data)

    if not group_update.enable_email:
        group_update.email_forward_members = []
        group_update.email_allowed_forward_members = []
    if not group_update.enable_postbox:
        group_update.email_postbox_access_members = []

    await _update_groups(
        group,
        group_update,
        attr_name='members',
        add_user_attr_name='groups',
        pull_user_attr_names=('groups', 'email_forward_groups', 'email_allowed_forward_groups'),
        pull_group_attr_names=(
            'email_forward_members', 'email_allowed_forward_members', 'email_postbox_access_members'
        ),
        clear_cache=True,
    )
    await _update_groups(
        group,
        group_update,
        attr_name='email_allowed_forward_members',
        add_user_attr_name='email_allowed_forward_groups',
        pull_user_attr_names=('email_forward_groups', 'email_allowed_forward_groups'),
        pull_group_attr_names=('email_forward_members',),
        clear_cache=False,
    )
    await _update_groups(
        group,
        group_update,
        attr_name='email_forward_members',
        add_user_attr_name='email_forward_groups',
        pull_user_attr_names=('email_forward_groups',),
        clear_cache=False,
    )
    await _update_groups(
        group,
        group_update,
        attr_name='email_postbox_access_members',
        add_user_attr_name='email_postbox_access_groups',
        pull_user_attr_names=('email_postbox_access_groups',),
        clear_cache=False,
    )

    group.update_from(group_update)
    result = await async_user_group_collection.replace_one(
        {'_id': group_id},
        group.dict(exclude_none=True, by_alias=True),
    )
    if result.matched_count != 1:
        raise HTTPException(404)