diff --git a/apricot/apricot_server.py b/apricot/apricot_server.py index 0cca36c..d7f65c8 100644 --- a/apricot/apricot_server.py +++ b/apricot/apricot_server.py @@ -18,6 +18,7 @@ def __init__( client_secret: str, domain: str, port: int, + uid_attribute: str, **kwargs: Any, ) -> None: # Log to stdout @@ -29,6 +30,7 @@ def __init__( client_id=client_id, client_secret=client_secret, domain=domain, + uid_attribute=uid_attribute, **kwargs, ) except Exception as exc: diff --git a/apricot/oauth/microsoft_entra_client.py b/apricot/oauth/microsoft_entra_client.py index 148163f..3b4ae6a 100644 --- a/apricot/oauth/microsoft_entra_client.py +++ b/apricot/oauth/microsoft_entra_client.py @@ -41,7 +41,18 @@ def groups(self) -> list[LDAPAttributeDict]: def users(self) -> list[LDAPAttributeDict]: output = [] try: - user_data = self.query("https://graph.microsoft.com/v1.0/users/") + queries = [ + "displayName", + "givenName", + "id", + "mail", + "surname", + "userPrincipalName", + self.uid_attribute, + ] + user_data = self.query( + f"https://graph.microsoft.com/v1.0/users?$select={','.join(queries)}" + ) for user_dict in user_data["value"]: attributes = {k: [v if v else ""] for k, v in dict(user_dict).items()} attributes["objectclass"] = [ @@ -62,6 +73,7 @@ def users(self) -> list[LDAPAttributeDict]: attributes["domain"] = [ str(user_dict["userPrincipalName"]).split("@")[1] ] + attributes["uid"] = [str(user_dict[self.uid_attribute])] output.append(attributes) except KeyError: pass diff --git a/apricot/oauth/oauth_client.py b/apricot/oauth/oauth_client.py index d3dcdb3..eeaefdf 100644 --- a/apricot/oauth/oauth_client.py +++ b/apricot/oauth/oauth_client.py @@ -24,11 +24,13 @@ def __init__( redirect_uri: str, scopes: list[str], token_url: str, + uid_attribute: str, ) -> None: # Set attributes self.client_secret = client_secret self.domain = domain self.token_url = token_url + self.uid_attribute = uid_attribute # Allow token scope to not match requested scope. (Other auth libraries allow # this, but Requests-OAuthlib raises exception on scope mismatch by default.) os.environ["OAUTHLIB_RELAX_TOKEN_SCOPE"] = "1" # noqa: S105 diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 6655fdd..00789e9 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -2,6 +2,7 @@ # shellcheck disable=SC2086 # shellcheck disable=SC2089 +# Required arguments if [ -z "${BACKEND}" ]; then echo "BACKEND environment variable is not set" exit 1 @@ -22,6 +23,17 @@ if [ -z "${DOMAIN}" ]; then exit 1 fi +if [ -z "${UID_ATTRIBUTE}" ]; then + echo "UID_ATTRIBUTE environment variable is not set" + exit 1 +fi + +# Arguments with defaults +if [ -z "${PORT}" ]; then + echo "PORT environment variable is not set: using default of 1389" + PORT="1389" +fi + # Optional arguments EXTRA_OPTS="" if [ -n "${ENTRA_TENANT_ID}" ]; then @@ -34,5 +46,6 @@ hatch run python run.py \ --client-id "$CLIENT_ID" \ --client-secret "$CLIENT_SECRET" \ --domain "$DOMAIN" \ - --port 1389 \ + --port "${PORT}" \ + --uid-attribute "${UID_ATTRIBUTE}" \ $EXTRA_OPTS diff --git a/run.py b/run.py index e1eeb6b..75f3c11 100644 --- a/run.py +++ b/run.py @@ -16,6 +16,7 @@ parser.add_argument("-p", "--port", type=int, default=1389, help="Port to run on.") parser.add_argument("-i", "--client-id", type=str, help="OAuth client ID.") parser.add_argument("-s", "--client-secret", type=str, help="OAuth client secret.") + parser.add_argument("-u", "--uid-attribute", type=str, help="Which user attribute to use for UID.") # Options for Microsoft Entra backend group = parser.add_argument_group("Microsoft Entra") group.add_argument("-t", "--entra-tenant-id", type=str, help="Microsoft Entra tenant ID.", required=False)