async def post_groups(request: web.Request) -> web.Response: """Handler for POST /groups/""" claims = utils.enforce_authorization(request.headers, request.app["settings"]) input_data = await request.json() if "group" not in input_data: raise web.HTTPBadRequest(reason="Missing group") group_name = input_data["group"] if not is_authorized_for_group_create(claims["groups"], group_name): raise web.HTTPForbidden(reason="Not authorized to create group") storage_backend = request.app["storage_backend"] staff_group_name = f"{group_name}.staff" if await storage_backend.group_exists(group_name): raise web.HTTPConflict(reason="Group already exists") await storage_backend.create_group(group_name) await storage_backend.create_group(staff_group_name) await storage_backend.add_member_to_group(claims["sub"], staff_group_name) location = f"/groups/{group_name}/" return web.Response(status=201, headers={"Location": location})
async def post_group(request: web.Request) -> web.Response: """Handler for POST /groups/{group_id}/ add a user to {group_id} """ claims = utils.enforce_authorization(request.headers, request.app["settings"]) input_data = await request.json() storage_backend = request.app["storage_backend"] group = request.match_info["group_uid"] if not is_authorized_for_group(claims["groups"], group): raise web.HTTPForbidden(reason="Not authorized to manage group") if not await storage_backend.group_exists(group): raise web.HTTPNotFound(reason="Group does not exist") if "username" not in input_data: raise web.HTTPBadRequest(reason="Missing username in request body") username = input_data["username"] if not await storage_backend.user_exists(username): await request.app["storage_backend"].create_user(username) if await storage_backend.is_user_in_group(username, group): raise web.HTTPBadRequest(reason="User already in group") await storage_backend.add_member_to_group(username, group) return web.Response(status=201)
def test_enforce_authorization__not_valid_bearer_token(): with pytest.raises(Unauthorized): assert enforce_authorization( {"Authorization": f"Bearer {WRONG_TOKEN}"}, { "public_key": PUBLIC_KEY, "algorithm": ALGORITHM }, )
async def post_token(request: web.Request) -> web.Response: """Post to IDP to create a jwt token""" # Proceed as a refresh token handler if query string refresh is available if "refresh" in request.rel_url.query: if not request.headers.get("Authorization"): raise web.HTTPBadRequest( reason="Missing Authorization header for refreshing access token" ) claims = utils.enforce_authorization(request.headers, request.app["settings"]) if not claims.get("refresh_token", False): raise Unauthorized("Token is not a refresh token") else: claims = await authenticate_with_identity_provider(request) response_content: Dict[str, Any] = { "identify_to_kisee": coreapi.Link( action="post", title="Login via login/password pair", description=""" POSTing to this endpoint will identify you by login/password. """, fields=[ coreapi.Field(name="login", required=True), coreapi.Field(name="password", required=True), ], url="/tokens/?idp=kisee", ) } if "sub" in claims: storage_backend = request.app["storage_backend"] if not await storage_backend.user_exists(claims["sub"]): await storage_backend.create_user(claims["sub"]) claims["groups"] = await storage_backend.get_authorizations_for_user( claims["sub"] ) access_token, refresh_token = generate_access_token_and_refresh_token_pairs( claims, request.app["settings"]["private_key"], algorithm=request.app["settings"]["algorithm"], ) response_content["access_token"] = access_token response_content["refresh_token"] = refresh_token else: response_content["authorize_url"] = claims["authorize_url"] return serialize( request, coreapi.Document( url="/tokens/", title="Create a token with Identify Provider", content=response_content, ), headers={"Cache-Control": "no-store", "Pragma": "no-cache"}, status=201, )
def test_enforce_authorization__expired_token(): expired_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJmb28iLCJleHAiOjE1NDE2OTI1NjAuNDU1Nzc5fQ.o3ZPwFmqJ3PLpxoZ854sHuu5ozD18J6kD7EdG2ospWSlovcgxGvSo65Hdgd8_RPHUDDJu4RZ4FwkyT1VTFRi_A" with pytest.raises(Unauthorized): assert enforce_authorization( {"Authorization": f"Bearer {expired_token}"}, { "public_key": PUBLIC_KEY, "algorithm": ALGORITHM }, )
async def delete_user(request: web.Request) -> web.Response: """Handlers for DELETE /users/{username} Delete {username} """ username = request.match_info["username"] claims = utils.enforce_authorization(request.headers, request.app["settings"]) if not is_root(claims["groups"]): raise web.HTTPForbidden(reason="Do not have rights to delete user") await request.app["storage_backend"].delete_user(username) return web.Response(status=204)
async def delete_group_member(request: web.Request) -> web.Response: """Delete group member of group""" claims = utils.enforce_authorization(request.headers, request.app["settings"]) storage_backend = request.app["storage_backend"] group = request.match_info["group_uid"] username = request.match_info["username"] if not await storage_backend.group_exists(group): raise web.HTTPNotFound(reason="Group does not exist") if not is_authorized_for_group(claims["groups"], group): raise web.HTTPForbidden(reason="Not authorized to manage group") if not await storage_backend.is_user_in_group(username, group): raise web.HTTPNotFound(reason="User does not exist in group") await storage_backend.delete_member_in_group(username, group) return web.Response(status=204)
async def get_user(request: web.Request) -> web.Response: """Handlers for GET /users/{username} List groups of {username} """ hostname = request.app["settings"]["hostname"] username = request.match_info["username"] claims = utils.enforce_authorization(request.headers, request.app["settings"]) if not is_root( claims["groups"]) and not claims["sub"] == username: # is user raise web.HTTPForbidden(reason="Do not have rights to view user info") last_element = request.rel_url.query.get("last_element", "") user = await request.app["storage_backend"].get_user(username) if not user: raise web.HTTPNotFound(reason="User does not exist") groups = await request.app["storage_backend"].get_groups_of_user( username, last_element) content = user content["patch"] = coreapi.Link( action="patch", title="Patch fields of user", description="A method to patch fields of user", fields=[coreapi.Field(name="is_banned")], ) if groups: content["next"] = coreapi.Link( url=f"{hostname}/users/{username}?last_element={groups[-1]}") content["groups"] = [ coreapi.Document(url=f"{hostname}/groups/{group}/", content={"group": group}) for group in groups ] return serialize( request, coreapi.Document(url=f"{hostname}/users/{username}", title="User interface", content=content), headers={"Vary": "Origin"}, )
async def delete_group(request: web.Request) -> web.Response: """Handler for POST /groups/{group_id}/ add a user to {group_id} """ claims = utils.enforce_authorization(request.headers, request.app["settings"]) storage_backend = request.app["storage_backend"] group = request.match_info["group_uid"] if not is_authorized_for_group(claims["groups"], "staff"): raise web.HTTPForbidden(reason="Not authorized to manage group") if not await storage_backend.group_exists(group): raise web.HTTPNotFound(reason="Group does not exist") await storage_backend.delete_group(group) await storage_backend.delete_group(f"{group}.staff") return web.Response(status=204)
async def patch_user(request: web.Request) -> web.Response: """Handlers for PATCH /users/{username} Patch an user """ username = request.match_info["username"] claims = utils.enforce_authorization(request.headers, request.app["settings"]) if not is_root(claims["groups"]): raise web.HTTPForbidden(reason="Do not have rights to patch") user = await request.app["storage_backend"].get_user(username) if not user: raise web.HTTPNotFound(reason="User does not exist") input_data = await request.json() if "username" in input_data: raise web.HTTPBadRequest(reason="can not patch username") if "is_banned" in input_data: ban = input_data["is_banned"] await request.app["storage_backend"].ban_user(username, ban) return web.Response(status=204)
async def get_group(request: web.Request) -> web.Response: """Handler for GET /groups/{group_uid}""" hostname = request.app["settings"]["hostname"] claims = utils.enforce_authorization(request.headers, request.app["settings"]) storage_backend = request.app["storage_backend"] group = request.match_info["group_uid"] if not is_authorized_for_group(claims["groups"], group): raise web.HTTPForbidden(reason="Not authorized to view group") if not await storage_backend.group_exists(group): raise web.HTTPNotFound(reason="Group does not exist") members = await storage_backend.get_members_of_group(group) return serialize( request, coreapi.Document( url=f"{hostname}/groups/{{group}}/", title=f"{group} group management interface", content={ "members": [ coreapi.Document(url=f"{hostname}/users/{member}", content={"username": member}) for member in members ], "add_member": coreapi.Link( action="post", title="Add a member to group", description="A method to add a member to group", fields=[coreapi.Field(name="username", required=True)], ), }, ), headers={"Vary": "Origin"}, )
def test_enforce_authorization__wrong_scheme(): with pytest.raises(Unauthorized): assert enforce_authorization( {"Authorization": "not-expected-scheme token"}, {})
def test_enforce_authorization__no_scheme_value_value_pair_in_header(): with pytest.raises(Unauthorized): assert enforce_authorization({"Authorization": "some-weird-token"}, {})
def test_enforce_authorization__missing_authorization_header(): with pytest.raises(Unauthenticated): assert enforce_authorization({}, {})