diff --git a/pillar/api/organizations/__init__.py b/pillar/api/organizations/__init__.py index 72e9bec8..a95abc36 100644 --- a/pillar/api/organizations/__init__.py +++ b/pillar/api/organizations/__init__.py @@ -92,6 +92,21 @@ class OrgManager: unknown_users = set(emails) - {user['email'] for user in existing_user_docs} existing_users = {user['_id'] for user in existing_user_docs} + return self._assign_users(org_id, unknown_users, existing_users) + + def assign_single_user(self, org_id: bson.ObjectId, *, user_id: bson.ObjectId) -> dict: + """Assigns a single, known user to the organization. + + :returns: the new organization document. + """ + + self._log.info('Adding new member %s to organization %s', user_id, org_id) + return self._assign_users(org_id, set(), {user_id}) + + def _assign_users(self, org_id: bson.ObjectId, + unknown_users: typing.Set[str], + existing_users: typing.Set[bson.ObjectId]) -> dict: + if self._log.isEnabledFor(logging.INFO): self._log.info(' - found users: %s', ', '.join(str(uid) for uid in existing_users)) self._log.info(' - unknown users: %s', ', '.join(unknown_users)) diff --git a/pillar/api/organizations/patch.py b/pillar/api/organizations/patch.py index 7cbce1a1..dff004fd 100644 --- a/pillar/api/organizations/patch.py +++ b/pillar/api/organizations/patch.py @@ -43,6 +43,27 @@ class OrganizationPatchHandler(patch_handler.AbstractPatchHandler): org_doc = current_app.org_manager.assign_users(org_id, emails) return jsonify(org_doc) + @authorization.require_login() + def patch_assign_user(self, org_id: bson.ObjectId, patch: dict): + """Assigns a single user by User ID to an organization. + + The calling user must be admin of the organization. + """ + + self._assert_is_admin(org_id) + + # Do some basic validation. + try: + user_id = patch['user_id'] + except KeyError: + raise wz_exceptions.BadRequest('No key "user_id" in patch.') + + user_oid = str2id(user_id) + log.info('User %s uses PATCH to add user %s to organization %s', + current_user().user_id, user_oid, org_id) + org_doc = current_app.org_manager.assign_single_user(org_id, user_id=user_oid) + return jsonify(org_doc) + @authorization.require_login() def patch_remove_user(self, org_id: bson.ObjectId, patch: dict): """Removes a user from an organization. diff --git a/tests/test_api/test_organizations.py b/tests/test_api/test_organizations.py index 9b7e9498..9268879a 100644 --- a/tests/test_api/test_organizations.py +++ b/tests/test_api/test_organizations.py @@ -193,6 +193,31 @@ class OrganizationPatchTest(AbstractPillarTest): self.assertEqual([str(member1_uid)], new_org_doc['members']) self.assertEqual(['member2@example.com'], new_org_doc['unknown_members']) + def test_assign_single_user(self): + self.enter_app_context() + + admin_uid = self.create_user(24 * 'a', token='admin-token') + member1_uid = self.create_user(24 * 'b', email='member1@example.com') + + om = self.app.org_manager + org_doc = om.create_new_org('Хакеры', admin_uid, 25) + org_id = org_doc['_id'] + + # Try the PATCH + resp = self.patch(f'/api/organizations/{org_id}', + json={ + 'op': 'assign-user', + 'user_id': str(member1_uid), + }, + auth_token='admin-token') + new_org_doc = resp.get_json() + + db = self.app.db('organizations') + db_org = db.find_one(org_id) + + self.assertEqual([member1_uid], db_org['members']) + self.assertEqual([str(member1_uid)], new_org_doc['members']) + def test_assign_users_access_denied(self): self.enter_app_context()