Make API updates for member updates (#541)
* Add API endpoint that lists pending invites for all orgs (superuser-only) * Add API endpoint that lists pending invites for org * Add user emails to /api/orgs/<oid> response
This commit is contained in:
parent
9048d46c6c
commit
58aafc4191
@ -189,6 +189,14 @@ class InviteOps:
|
||||
|
||||
return False
|
||||
|
||||
async def get_pending_invites(self, org=None):
|
||||
"""return list of pending invites."""
|
||||
if org:
|
||||
invites = self.invites.find({"oid": org.id})
|
||||
else:
|
||||
invites = self.invites.find()
|
||||
return [invite async for invite in invites]
|
||||
|
||||
|
||||
def init_invites(mdb, email):
|
||||
"""init InviteOps"""
|
||||
|
@ -136,6 +136,7 @@ class Organization(BaseMongoModel):
|
||||
result["users"][id_] = {
|
||||
"role": role,
|
||||
"name": org_user.get("name", ""),
|
||||
"email": org_user.get("email", ""),
|
||||
}
|
||||
|
||||
return result
|
||||
@ -463,6 +464,11 @@ def init_orgs_api(app, mdb, user_manager, invites, user_dep: User):
|
||||
await user_manager.user_db.update(user)
|
||||
return {"added": True}
|
||||
|
||||
@router.get("/invites", tags=["invites"])
|
||||
async def get_pending_org_invites(org: Organization = Depends(org_owner_dep)):
|
||||
pending_invites = await user_manager.invites.get_pending_invites(org)
|
||||
return {"pending_invites": pending_invites}
|
||||
|
||||
@router.post("/remove", tags=["invites"])
|
||||
async def remove_user_from_org(
|
||||
remove: RemoveFromOrg, org: Organization = Depends(org_owner_dep)
|
||||
|
@ -151,7 +151,7 @@ class UserManager(BaseUserManager[UserCreate, UserDB]):
|
||||
"""return list of user names for given ids"""
|
||||
user_ids = [UUID4(id_) for id_ in user_ids]
|
||||
cursor = self.user_db.collection.find(
|
||||
{"id": {"$in": user_ids}}, projection=["id", "name"]
|
||||
{"id": {"$in": user_ids}}, projection=["id", "name", "email"]
|
||||
)
|
||||
return await cursor.to_list(length=1000)
|
||||
|
||||
@ -363,6 +363,7 @@ class BearerOrQueryTransport(BearerTransport):
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# pylint: disable=too-many-locals
|
||||
def init_users_api(app, user_manager):
|
||||
"""init fastapi_users"""
|
||||
bearer_transport = BearerOrQueryTransport(tokenUrl="auth/jwt/login")
|
||||
@ -488,6 +489,14 @@ def init_users_api(app, user_manager):
|
||||
await user_manager.invites.remove_invite(token)
|
||||
return {"removed": True}
|
||||
|
||||
@users_router.get("/invites", tags=["invites"])
|
||||
async def get_pending_invites(user: User = Depends(current_active_user)):
|
||||
if not user.is_superuser:
|
||||
raise HTTPException(status_code=403, detail="Not Allowed")
|
||||
|
||||
pending_invites = await user_manager.invites.get_pending_invites()
|
||||
return {"pending_invites": pending_invites}
|
||||
|
||||
app.include_router(users_router, prefix="/users", tags=["users"])
|
||||
|
||||
return fastapi_users
|
||||
|
@ -18,6 +18,8 @@ CRAWLER_PW = "crawlerPASSWORD!"
|
||||
_admin_config_id = None
|
||||
_crawler_config_id = None
|
||||
|
||||
NON_DEFAULT_ORG_NAME = "Non-default org"
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def admin_auth_headers():
|
||||
@ -52,6 +54,27 @@ def default_org_id(admin_auth_headers):
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def non_default_org_id(admin_auth_headers):
|
||||
r = requests.post(
|
||||
f"{API_PREFIX}/orgs/create",
|
||||
headers=admin_auth_headers,
|
||||
json={"name": NON_DEFAULT_ORG_NAME},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
|
||||
while True:
|
||||
r = requests.get(f"{API_PREFIX}/orgs", headers=admin_auth_headers)
|
||||
data = r.json()
|
||||
try:
|
||||
for org in data["orgs"]:
|
||||
if org["name"] == NON_DEFAULT_ORG_NAME:
|
||||
return org["id"]
|
||||
except:
|
||||
print("Waiting for non-default org id")
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def admin_crawl_id(admin_auth_headers, default_org_id):
|
||||
# Start crawl.
|
||||
|
40
backend/test/test_invites.py
Normal file
40
backend/test/test_invites.py
Normal file
@ -0,0 +1,40 @@
|
||||
import requests
|
||||
|
||||
from .conftest import API_PREFIX
|
||||
|
||||
|
||||
def test_pending_invites(admin_auth_headers, default_org_id):
|
||||
r = requests.get(f"{API_PREFIX}/users/invites", headers=admin_auth_headers)
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data["pending_invites"] == []
|
||||
|
||||
# Add a pending invite and check it's returned
|
||||
INVITE_EMAIL = "invite-pending@example.com"
|
||||
|
||||
r = requests.post(
|
||||
f"{API_PREFIX}/users/invite",
|
||||
headers=admin_auth_headers,
|
||||
json={"email": INVITE_EMAIL},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data["invited"] == "new_user"
|
||||
|
||||
r = requests.get(f"{API_PREFIX}/users/invites", headers=admin_auth_headers)
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
invites = data["pending_invites"]
|
||||
assert len(invites) == 1
|
||||
invite = invites[0]
|
||||
assert invite["_id"]
|
||||
assert invite["email"] == INVITE_EMAIL
|
||||
assert invite["oid"] == default_org_id
|
||||
assert invite["created"]
|
||||
assert invite["role"]
|
||||
|
||||
|
||||
def test_pending_invites_crawler(crawler_auth_headers, default_org_id):
|
||||
# Verify that only superusers can see pending invites
|
||||
r = requests.get(f"{API_PREFIX}/users/invites", headers=crawler_auth_headers)
|
||||
assert r.status_code == 403
|
@ -16,6 +16,34 @@ def test_ensure_only_one_default_org(admin_auth_headers):
|
||||
assert len(orgs_with_same_name) == 1
|
||||
|
||||
|
||||
def test_get_org_admin(admin_auth_headers, default_org_id):
|
||||
"""org owners should receive details on users."""
|
||||
r = requests.get(f"{API_PREFIX}/orgs/{default_org_id}", headers=admin_auth_headers)
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data["id"] == default_org_id
|
||||
assert data["name"]
|
||||
|
||||
users = data["users"]
|
||||
assert users
|
||||
for _, value in users.items():
|
||||
assert value["name"]
|
||||
assert value["email"]
|
||||
assert value["role"]
|
||||
|
||||
|
||||
def test_get_org_crawler(crawler_auth_headers, default_org_id):
|
||||
"""non-owners should *not* receive details on users."""
|
||||
r = requests.get(
|
||||
f"{API_PREFIX}/orgs/{default_org_id}", headers=crawler_auth_headers
|
||||
)
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data["id"] == default_org_id
|
||||
assert data["name"]
|
||||
assert data.get("users") is None
|
||||
|
||||
|
||||
def test_rename_org(admin_auth_headers, default_org_id):
|
||||
UPDATED_NAME = "updated org name"
|
||||
rename_data = {"name": UPDATED_NAME}
|
||||
@ -83,3 +111,44 @@ def test_remove_user_from_org(admin_auth_headers, default_org_id):
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data["removed"]
|
||||
|
||||
|
||||
def test_get_pending_org_invites(
|
||||
admin_auth_headers, default_org_id, non_default_org_id
|
||||
):
|
||||
# Invite user to non-default org
|
||||
INVITE_EMAIL = "non-default-invite@example.com"
|
||||
r = requests.post(
|
||||
f"{API_PREFIX}/orgs/{non_default_org_id}/invite",
|
||||
headers=admin_auth_headers,
|
||||
json={"email": INVITE_EMAIL, "role": 20},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data["invited"] == "new_user"
|
||||
|
||||
# Invite user to default org
|
||||
r = requests.post(
|
||||
f"{API_PREFIX}/orgs/{default_org_id}/invite",
|
||||
headers=admin_auth_headers,
|
||||
json={"email": "default-invite@example.com", "role": 10},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data["invited"] == "new_user"
|
||||
|
||||
# Check that only invite to non-default org is returned
|
||||
r = requests.get(
|
||||
f"{API_PREFIX}/orgs/{non_default_org_id}/invites",
|
||||
headers=admin_auth_headers,
|
||||
)
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
invites = data["pending_invites"]
|
||||
assert len(invites) == 1
|
||||
invite = invites[0]
|
||||
assert invite["_id"]
|
||||
assert invite["email"] == INVITE_EMAIL
|
||||
assert invite["oid"] == non_default_org_id
|
||||
assert invite["created"]
|
||||
assert invite["role"]
|
||||
|
Loading…
Reference in New Issue
Block a user