Repository: minio/minio (language: go)

Issue:
Keycloak IDP user removal and Service Account
## Expected Behavior
After the user is deleted from Keycloak, the associated service account is also deleted/disabled

## Current Behavior
The service account still works after the user is deleted

## Steps to Reproduce (for bugs)
On Keycloak, create a test user
Add a policy attribute with value readwrite to the user
Login to Minio Console with the test user and create a service account
Configure Minio Client with the newly created service account -> able to list buckets
Back to Keycloak, delete the user
List buckets again using minio client -> still works

## Your Environment
* Version used (`minio version RELEASE.2021-12-20T22-07-16Z`):
* Server setup and configuration:

```
MINIO_IDENTITY_OPENID_CONFIG_URL=https://keycloak-idp.domain.example/auth/realms/test-realm/.well-known/openid-configuration
MINIO_IDENTITY_OPENID_CLIENT_ID=client-id-test
MINIO_IDENTITY_OPENID_CLIENT_SECRET=<hidden>
MINIO_ROOT_USER=<hidden>
MINIO_ROOT_PASSWORD=<hidden>
MINIO_SERVER_URL=https://example.com
MINIO_BROWSER_REDIRECT_URL=https://example.com
MINIO_IDENTITY_OPENID_SCOPES=openid
MINIO_IDENTITY_OPENID_VENDOR=keycloak
MINIO_IDENTITY_OPENID_KEYCLOAK_REALM=test-realm
MINIO_IDENTITY_OPENID_KEYCLOAK_ADMIN_URL=https://keycloak-idp.domain.example/auth/admin
MINIO_ACCESS_KEY_FILE=access_key
MINIO_SECRET_KEY_FILE=secret_key
MINIO_ROOT_USER_FILE=access_key
MINIO_ROOT_PASSWORD_FILE=secret_key
MINIO_KMS_SECRET_KEY_FILE=kms_master_key
MINIO_UPDATE_MINISIGN_PUBKEY=<hidden>
MINIO_CONFIG_ENV_FILE=config.env
HOME=/root
```
* Operating System and version (`Linux a988454148df 5.11.0-40-generic #44~20.04.2-Ubuntu`):

## Keycloak configuration:
At first I followed the instructions [keycloak sts](https://github.com/minio/minio/blob/master/docs/sts/keycloak.md). However I was not able to query the users as keycloak returned 403 error.

```
curl -H "Authorization: Bearer eyxxxxxxxxxx..." \
  "https://keycloak-idp.domain.example/auth/admin/realms/test-realm/users/{userid}"
```

Then only after adding the **realm-management** role to **client-id-test**, I could get the requested user: on the tab **Roles**, select client role **realm-management** , add role **manage-users**




Discussion / hints:
@tanrobotix you need to provide more details on the setup etc. Closing this until you provide enough information by filling out the issue template properly and a reproducer if any.
Hi @harshavardhana , I added the detail info, would you please reopen the isssue.
> Hi @harshavardhana , I added the detail info, would you please reopen the isssue.

Thanks please make sure to follow this from next time.
right, we face the same issue. In our case would be enough to just being able to identify the existing service accounts with the OIDC/LDAP users, so we can go through them and close the ones that are gone. In our case, we lock accounts in LDAP by setting some hash in the password field that doesn't match any password they potential user may type. How would Minio then know whether this account is gone or not?
> right, we face the same issue. In our case would be enough to just being able to identify the existing service accounts with the OIDC/LDAP users, so we can go through them and close the ones that are gone. In our case, we lock accounts in LDAP by setting some hash in the password field that doesn't match any password they potential user may type. How would Minio then know whether this account is gone or not?

LDAP has nothing to do with this issue. In case of LDAP we will see the UserDN is missing on the LDAP server this tells us that this user is gone. 
For completeness: this issue was already reported to the security team nearly 1 year ago. The response was that this (the fact that a service account survives the deletion of the IDP account) is not a security issue, but "an administrative issue". The suggested "fix" was adding a step for manual Service Account deletion (`mc admin user svacct rm`) in the user removal procedure.

While I don't agree, I understand that there is no way for MinIO (as "OIDC Service Provider") to be notified for a user deletion from the IDP (AFAIK). Also, there is no way to query for user info/existence from SP to IDP in the OIDC standard. Specific implementations may expose specific non-standard endpoints for querying the user list (e.g., Keycloak as Admin REST APIs).

A possible "mitigation" is add an option for disabling service account altogether, or limit service accounts creation to admins / non IDP-users.

> For completeness: this issue was already reported to the security team nearly 1 year ago. The response was that this (the fact that a service account survives the deletion of the IDP account) is not a security issue, but "an administrative issue". The suggested "fix" was adding a step for manual Service Account deletion (`mc admin user svacct rm`) in the user removal procedure.
> 
> While I don't agree, I understand that there is no way for MinIO (as "OIDC Service Provider") to be notified for a user deletion from the IDP (AFAIK). Also, there is no way to query for user info/existence from SP to IDP in the OIDC standard. Specific implementations may expose specific non-standard endpoints for querying the user list (e.g., Keycloak as Admin REST APIs).
> 
> A possible "mitigation" is add an option for disabling service account altogether, or limit service accounts creation to admins / non IDP-users.
> 

Automatic way is already added, but something seems to be missing and we haven't had time to debug this further.

Will reply back when we have further updates @Enrico204 
Being able to list service account for a specific IDP account would already be a success (as minio admin), since this way we can manually search for existing service accounts of disabled IDP users. Is that possible?
> Being able to list service account for a specific IDP account would already be a success (as minio admin), since this way we can manually search for existing service accounts of disabled IDP users. Is that possible?

When you login to the UI you will see what your service accounts are for the incoming user @titansmc 

Fix patch:
diff --git a/cmd/iam-store.go b/cmd/iam-store.go
index 245922ce435ae..63b94d018cdf0 100644
--- a/cmd/iam-store.go
+++ b/cmd/iam-store.go
@@ -1457,7 +1457,20 @@ func (store *IAMStoreSys) GetAllParentUsers() []string {
 	res := set.NewStringSet()
 	for _, cred := range cache.iamUsersMap {
 		if cred.IsServiceAccount() || cred.IsTemp() {
-			res.Add(cred.ParentUser)
+			parentUser := cred.ParentUser
+			if cred.SessionToken != "" {
+				claims, err := getClaimsFromToken(cred.SessionToken)
+				if err != nil {
+					continue
+				}
+				if v, ok := claims[subClaim]; ok {
+					subFromToken, ok := v.(string)
+					if ok {
+						parentUser = subFromToken
+					}
+				}
+			}
+			res.Add(parentUser)
 		}
 	}
 
diff --git a/cmd/iam.go b/cmd/iam.go
index 5172decd9aae2..6041d2f1dd6f2 100644
--- a/cmd/iam.go
+++ b/cmd/iam.go
@@ -1006,11 +1006,7 @@ func (sys *IAMSys) purgeExpiredCredentialsForExternalSSO(ctx context.Context) {
 	parentUsers := sys.store.GetAllParentUsers()
 	var expiredUsers []string
 	for _, parentUser := range parentUsers {
-		userid, err := parseOpenIDParentUser(parentUser)
-		if err == errSkipFile {
-			continue
-		}
-		u, err := globalOpenIDConfig.LookupUser(userid)
+		u, err := globalOpenIDConfig.LookupUser(parentUser)
 		if err != nil {
 			logger.LogIf(GlobalContext, err)
 			continue
diff --git a/cmd/sts-handlers.go b/cmd/sts-handlers.go
index 95cba38804fa0..7743a320b7dcf 100644
--- a/cmd/sts-handlers.go
+++ b/cmd/sts-handlers.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2015-2021 MinIO, Inc.
+/// Copyright (c) 2015-2021 MinIO, Inc.
 //
 // This file is part of MinIO Object Storage stack
 //
@@ -80,16 +80,6 @@ const (
 	roleArnClaim = "roleArn"
 )
 
-func parseOpenIDParentUser(parentUser string) (userID string, err error) {
-	if strings.HasPrefix(parentUser, "openid:") {
-		tokens := strings.SplitN(strings.TrimPrefix(parentUser, "openid:"), ":", 2)
-		if len(tokens) == 2 {
-			return tokens[0], nil
-		}
-	}
-	return "", errSkipFile
-}
-
 // stsAPIHandlers implements and provides http handlers for AWS STS API.
 type stsAPIHandlers struct{}
 


Test patch: