From 9e1db628f5c0345d4cabebd7714feaf5988448ea Mon Sep 17 00:00:00 2001 From: Alec Cohan Date: Fri, 9 Jun 2023 10:15:06 -0400 Subject: [PATCH 01/11] Update keycloak-port env var --- internal/config/config.go | 2 +- internal/models/user.go | 4 ++-- internal/service/keycloak/keycloak.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index f8f5882..d635ed1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -98,7 +98,7 @@ func Get() *MbopConfig { Debug: debug, KeyCloakHost: fetchWithDefault("KEYCLOAK_HOST", "localhost"), - KeyCloakPort: fetchWithDefault("KEYCLOAK_PORT", "8000"), + KeyCloakPort: fetchWithDefault("KEYCLOAK_PORT", ":8000"), KeyCloakScheme: fetchWithDefault("KEYCLOAK_SCHEME", "http"), KeyCloakTimeout: keyCloakTimeout, KeyCloakTokenUsername: fetchWithDefault("KEYCLOAK_TOKEN_USERNAME", "admin"), diff --git a/internal/models/user.go b/internal/models/user.go index bc39edc..2db6665 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -50,8 +50,8 @@ type KeycloakResponse struct { Type string `json:"type"` Username string `json:"username"` UserID string `json:"user_id"` - FirstName string `json:"firstName"` - LastName string `json:"lastName"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` ID string `json:"id"` } diff --git a/internal/service/keycloak/keycloak.go b/internal/service/keycloak/keycloak.go index 5751596..2601a17 100644 --- a/internal/service/keycloak/keycloak.go +++ b/internal/service/keycloak/keycloak.go @@ -139,7 +139,7 @@ func createEncodedTokenBody() *strings.Reader { } func createTokenURL() (*url.URL, error) { - url, err := url.Parse(fmt.Sprintf("%s://%s:%s/token", config.Get().KeyCloakScheme, config.Get().KeyCloakHost, config.Get().KeyCloakPort)) + url, err := url.Parse(fmt.Sprintf("%s://%s%s/token", config.Get().KeyCloakScheme, config.Get().KeyCloakHost, config.Get().KeyCloakPort)) if err != nil { return nil, fmt.Errorf("error creating keycloak token url: %s", err) } @@ -149,7 +149,7 @@ func createTokenURL() (*url.URL, error) { // MAKE response to users function to massage data back to regular format func createV1RequestURL(usernames models.UserBody, q models.UserV1Query) (*url.URL, error) { - url, err := url.Parse(fmt.Sprintf("%s://%s:%s/users?limit=100", config.Get().KeyCloakScheme, config.Get().KeyCloakHost, config.Get().KeyCloakPort)) + url, err := url.Parse(fmt.Sprintf("%s://%s%s/users?limit=100", config.Get().KeyCloakScheme, config.Get().KeyCloakHost, config.Get().KeyCloakPort)) if err != nil { return nil, fmt.Errorf("error creating (keycloak) /users url: %s", err) } From 1efa1eebef7391c1a863bdb1202cfc7b120eff9d Mon Sep 17 00:00:00 2001 From: Alec Cohan Date: Fri, 9 Jun 2023 12:49:56 -0400 Subject: [PATCH 02/11] Seperate keycloak from user-service --- internal/config/config.go | 39 ++-- .../handlers/accounts_v3_usersBy_handler.go | 22 ++- .../handlers/accounts_v3_users_handler.go | 22 ++- internal/handlers/users_v1_handler.go | 22 ++- .../keycloak-user-service/user_service.go | 179 ++++++++++++++++++ .../user_service_interface.go} | 13 +- internal/service/keycloak/keycloak.go | 164 +--------------- .../service/keycloak/keycloak_integration.go | 12 ++ 8 files changed, 270 insertions(+), 203 deletions(-) create mode 100644 internal/service/keycloak-user-service/user_service.go rename internal/service/{keycloak/keycloak_interface.go => keycloak-user-service/user_service_interface.go} (71%) create mode 100644 internal/service/keycloak/keycloak_integration.go diff --git a/internal/config/config.go b/internal/config/config.go index d635ed1..ae6910f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -27,14 +27,17 @@ type MbopConfig struct { IsInternalLabel string Debug bool - KeyCloakScheme string - KeyCloakHost string - KeyCloakPort string - KeyCloakTimeout int64 - KeyCloakTokenUsername string - KeyCloakTokenPassword string - KeyCloakTokenGrantType string - KeyCloakTokenClientID string + KeyCloakUserServiceScheme string + KeyCloakUserServiceHost string + KeyCloakUserServicePort string + KeyCloakUserServiceTimeout int64 + KeyCloakTimeout int64 + KeyCloakTokenURL string + KeyCloakTokenPath string + KeyCloakTokenUsername string + KeyCloakTokenPassword string + KeyCloakTokenGrantType string + KeyCloakTokenClientID string StoreBackend string DatabaseHost string @@ -60,6 +63,7 @@ func Get() *MbopConfig { debug, _ := strconv.ParseBool(fetchWithDefault("DEBUG", "false")) certDir := fetchWithDefault("CERT_DIR", "/certs") keyCloakTimeout, _ := strconv.ParseInt(fetchWithDefault("KEYCLOAK_TIMEOUT", "60"), 0, 64) + userServiceTimeout, _ := strconv.ParseInt(fetchWithDefault("KEYCLOAK_USER_SERVICE_TIMEOUT", "60"), 0, 64) var tls bool _, err := os.Stat(certDir + "/tls.crt") @@ -97,14 +101,17 @@ func Get() *MbopConfig { IsInternalLabel: fetchWithDefault("IS_INTERNAL_LABEL", ""), Debug: debug, - KeyCloakHost: fetchWithDefault("KEYCLOAK_HOST", "localhost"), - KeyCloakPort: fetchWithDefault("KEYCLOAK_PORT", ":8000"), - KeyCloakScheme: fetchWithDefault("KEYCLOAK_SCHEME", "http"), - KeyCloakTimeout: keyCloakTimeout, - KeyCloakTokenUsername: fetchWithDefault("KEYCLOAK_TOKEN_USERNAME", "admin"), - KeyCloakTokenPassword: fetchWithDefault("KEYCLOAK_TOKEN_PASSWORD", "admin"), - KeyCloakTokenGrantType: fetchWithDefault("KEYCLOAK_TOKEN_GRANT_TYPE", "password"), - KeyCloakTokenClientID: fetchWithDefault("KEYCLOAK_TOKEN_CLIENT_ID", "admin-cli"), + KeyCloakUserServiceHost: fetchWithDefault("KEYCLOAK_USER_SERVICE_HOST", "localhost"), + KeyCloakUserServicePort: fetchWithDefault("KEYCLOAK_USER_SERVICE_PORT", "8000"), + KeyCloakUserServiceScheme: fetchWithDefault("KEYCLOAK_USER_SERVICE_SCHEME", "http"), + KeyCloakUserServiceTimeout: userServiceTimeout, + KeyCloakTimeout: keyCloakTimeout, + KeyCloakTokenURL: fetchWithDefault("KEYCLOAK_TOKEN_URL", "http://localhost:8080/"), + KeyCloakTokenPath: fetchWithDefault("KEYCLOAK_TOKEN_PATH", "realms/master/protocol/openid-connect/token"), + KeyCloakTokenUsername: fetchWithDefault("KEYCLOAK_TOKEN_USERNAME", "admin"), + KeyCloakTokenPassword: fetchWithDefault("KEYCLOAK_TOKEN_PASSWORD", "admin"), + KeyCloakTokenGrantType: fetchWithDefault("KEYCLOAK_TOKEN_GRANT_TYPE", "password"), + KeyCloakTokenClientID: fetchWithDefault("KEYCLOAK_TOKEN_CLIENT_ID", "admin-cli"), Port: fetchWithDefault("PORT", "8090"), TLSPort: fetchWithDefault("TLS_PORT", "8890"), diff --git a/internal/handlers/accounts_v3_usersBy_handler.go b/internal/handlers/accounts_v3_usersBy_handler.go index 0d143a1..10e8c82 100644 --- a/internal/handlers/accounts_v3_usersBy_handler.go +++ b/internal/handlers/accounts_v3_usersBy_handler.go @@ -6,6 +6,7 @@ import ( "github.com/redhatinsights/mbop/internal/config" "github.com/redhatinsights/mbop/internal/models" "github.com/redhatinsights/mbop/internal/service/keycloak" + keycloak_user_service "github.com/redhatinsights/mbop/internal/service/keycloak-user-service" "github.com/redhatinsights/mbop/internal/service/ocm" ) @@ -104,25 +105,32 @@ func AccountsV3UsersByHandler(w http.ResponseWriter, r *http.Request) { return } - client, err := keycloak.NewKeyCloakClient() + keycloakClient := keycloak.NewKeyCloakClient() + err = keycloakClient.InitKeycloakConnection() if err != nil { - do400(w, err.Error()) + do500(w, "Can't build keycloak connection: "+err.Error()) return } - err = client.InitKeycloakConnection() + token, err := keycloakClient.GetAccessToken() if err != nil { - do500(w, "Can't build keycloak connection: "+err.Error()) + do500(w, "Can't fetch keycloak token: "+err.Error()) return } - token, err := client.GetAccessToken() + userServiceClient, err := keycloak_user_service.NewKeyCloakUserServiceClient() if err != nil { - do500(w, "Can't fetch keycloak token: "+err.Error()) + do500(w, "Can't build keycloak user service client: "+err.Error()) + return + } + + err = userServiceClient.InitKeycloakUserServiceConnection() + if err != nil { + do500(w, "Can't build keycloak user service connection: "+err.Error()) return } - u, err := client.GetAccountV3Users(orgID, token, q) + u, err := userServiceClient.GetAccountV3Users(orgID, token, q) if err != nil { do500(w, "Cant Retrieve Keycloak Accounts: "+err.Error()) return diff --git a/internal/handlers/accounts_v3_users_handler.go b/internal/handlers/accounts_v3_users_handler.go index 968e40d..04f8ed3 100644 --- a/internal/handlers/accounts_v3_users_handler.go +++ b/internal/handlers/accounts_v3_users_handler.go @@ -5,6 +5,7 @@ import ( "github.com/redhatinsights/mbop/internal/config" "github.com/redhatinsights/mbop/internal/service/keycloak" + keycloak_user_service "github.com/redhatinsights/mbop/internal/service/keycloak-user-service" "github.com/redhatinsights/mbop/internal/service/ocm" ) @@ -81,25 +82,32 @@ func AccountsV3UsersHandler(w http.ResponseWriter, r *http.Request) { return } - client, err := keycloak.NewKeyCloakClient() + keycloakClient := keycloak.NewKeyCloakClient() + err = keycloakClient.InitKeycloakConnection() if err != nil { - do400(w, err.Error()) + do500(w, "Can't build keycloak connection: "+err.Error()) return } - err = client.InitKeycloakConnection() + token, err := keycloakClient.GetAccessToken() if err != nil { - do500(w, "Can't build keycloak connection: "+err.Error()) + do500(w, "Can't fetch keycloak token: "+err.Error()) return } - token, err := client.GetAccessToken() + userServiceClient, err := keycloak_user_service.NewKeyCloakUserServiceClient() if err != nil { - do500(w, "Can't fetch keycloak token: "+err.Error()) + do500(w, "Can't build keycloak user service client: "+err.Error()) + return + } + + err = userServiceClient.InitKeycloakUserServiceConnection() + if err != nil { + do500(w, "Can't build keycloak user service connection: "+err.Error()) return } - u, err := client.GetAccountV3Users(orgID, token, q) + u, err := userServiceClient.GetAccountV3Users(orgID, token, q) if err != nil { do500(w, "Cant Retrieve Keycloak Accounts: "+err.Error()) return diff --git a/internal/handlers/users_v1_handler.go b/internal/handlers/users_v1_handler.go index 44f7a1c..2bb3636 100644 --- a/internal/handlers/users_v1_handler.go +++ b/internal/handlers/users_v1_handler.go @@ -5,6 +5,7 @@ import ( "github.com/redhatinsights/mbop/internal/config" "github.com/redhatinsights/mbop/internal/service/keycloak" + keycloak_user_service "github.com/redhatinsights/mbop/internal/service/keycloak-user-service" "github.com/redhatinsights/mbop/internal/service/ocm" ) @@ -75,25 +76,32 @@ func UsersV1Handler(w http.ResponseWriter, r *http.Request) { return } - client, err := keycloak.NewKeyCloakClient() + keycloakClient := keycloak.NewKeyCloakClient() + err = keycloakClient.InitKeycloakConnection() if err != nil { - do400(w, err.Error()) + do500(w, "Can't build keycloak connection: "+err.Error()) return } - err = client.InitKeycloakConnection() + token, err := keycloakClient.GetAccessToken() if err != nil { - do500(w, "Can't build keycloak connection: "+err.Error()) + do500(w, "Can't fetch keycloak token: "+err.Error()) return } - token, err := client.GetAccessToken() + userServiceClient, err := keycloak_user_service.NewKeyCloakUserServiceClient() if err != nil { - do500(w, "Can't fetch keycloak token: "+err.Error()) + do500(w, "Can't build keycloak user service client: "+err.Error()) + return + } + + err = userServiceClient.InitKeycloakUserServiceConnection() + if err != nil { + do500(w, "Can't build keycloak user service connection: "+err.Error()) return } - u, err := client.GetUsers(token, usernames, q) + u, err := userServiceClient.GetUsers(token, usernames, q) if err != nil { do500(w, "Cant Retrieve Keycloak Accounts: "+err.Error()) return diff --git a/internal/service/keycloak-user-service/user_service.go b/internal/service/keycloak-user-service/user_service.go new file mode 100644 index 0000000..46798d5 --- /dev/null +++ b/internal/service/keycloak-user-service/user_service.go @@ -0,0 +1,179 @@ +package keycloak_user_service + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/redhatinsights/mbop/internal/config" + l "github.com/redhatinsights/mbop/internal/logger" + "github.com/redhatinsights/mbop/internal/models" +) + +type UserServiceClient struct { + client *http.Client +} + +func (userService *UserServiceClient) InitKeycloakUserServiceConnection() error { + userService.client = &http.Client{ + Timeout: time.Duration(config.Get().KeyCloakUserServiceTimeout * int64(time.Second)), + } + + return nil +} + +func (userService *UserServiceClient) GetUsers(token string, u models.UserBody, q models.UserV1Query) (models.Users, error) { + users := models.Users{Users: []models.User{}} + url, err := createV1RequestURL(u, q) + if err != nil { + return users, err + } + + body, err := userService.sendKeycloakGetRequest(url, token) + if err != nil { + l.Log.Error(err, "/v3/users error sending request") + return users, err + } + + unmarshaledResponse := models.KeycloakResponses{} + err = json.Unmarshal(body, &unmarshaledResponse) + if err != nil { + return users, err + } + + return keycloakResponseToUsers(unmarshaledResponse.Users), err +} + +func (userService *UserServiceClient) GetAccountV3Users(orgID string, token string, q models.UserV3Query) (models.Users, error) { + users := models.Users{Users: []models.User{}} + url, err := createV3UsersRequestURL(orgID, q) + if err != nil { + return users, err + } + + body, err := userService.sendKeycloakGetRequest(url, token) + if err != nil { + l.Log.Error(err, "/v3/users error sending request") + return users, err + } + + unmarshaledResponse := models.KeycloakResponses{} + err = json.Unmarshal(body, &unmarshaledResponse) + if err != nil { + return users, err + } + + return keycloakResponseToUsers(unmarshaledResponse.Users), nil +} + +func (userService *UserServiceClient) sendKeycloakGetRequest(url *url.URL, token string) ([]byte, error) { + var responseBody []byte + + req, err := http.NewRequest(http.MethodGet, url.String(), nil) + if err != nil { + return responseBody, err + } + + req.Header.Set("Authorization", "Bearer "+token) + + resp, err := userService.client.Do(req) + if err != nil { + l.Log.Error(err, "error fetching keycloak response") + return responseBody, err + } + + responseBody, err = ioutil.ReadAll(resp.Body) + if err != nil { + l.Log.Error(err, "error reading keycloak response body") + return responseBody, err + } + + // Close response body + defer resp.Body.Close() + + return responseBody, nil +} + +// MAKE response to users function to massage data back to regular format +func createV1RequestURL(usernames models.UserBody, q models.UserV1Query) (*url.URL, error) { + url, err := url.Parse(fmt.Sprintf("%s://%s:%s/users?limit=100", config.Get().KeyCloakUserServiceScheme, config.Get().KeyCloakUserServiceHost, config.Get().KeyCloakUserServicePort)) + if err != nil { + return nil, fmt.Errorf("error creating (keycloak) /users url: %s", err) + } + + queryParams := url.Query() + + if q.QueryBy != "" { + queryParams.Add("order", q.QueryBy) + } + + if q.SortOrder != "" { + queryParams.Add("direction", q.SortOrder) + } + + queryParams.Add("usernames", createUsernamesQuery(usernames.Users)) + + url.RawQuery = queryParams.Encode() + return url, err +} + +func createV3UsersRequestURL(orgID string, q models.UserV3Query) (*url.URL, error) { + url, err := url.Parse(fmt.Sprintf("%s://%s:%s/users", config.Get().KeyCloakUserServiceScheme, config.Get().KeyCloakUserServiceHost, config.Get().KeyCloakUserServicePort)) + if err != nil { + return nil, fmt.Errorf("error creating (keycloak) /v3/users url: %s", err) + } + queryParams := url.Query() + + if q.SortOrder != "" { + queryParams.Add("direction", q.SortOrder) + } + + queryParams.Add("org_id", orgID) + queryParams.Add("limit", strconv.Itoa(q.Limit)) + queryParams.Add("offset", strconv.Itoa(q.Offset)) + + url.RawQuery = queryParams.Encode() + + return url, err +} + +func createUsernamesQuery(usernames []string) string { + usernameQuery := "" + + for _, username := range usernames { + if usernameQuery == "" { + usernameQuery += username + } else { + usernameQuery += fmt.Sprintf(",%s", username) + } + } + + return usernameQuery +} + +func keycloakResponseToUsers(r []models.KeycloakResponse) models.Users { + users := models.Users{Users: []models.User{}} + + for _, response := range r { + users.AddUser(models.User{ + Username: response.Username, + ID: response.ID, + Email: response.Email, + FirstName: response.FirstName, + LastName: response.LastName, + AddressString: "", + IsActive: true, + IsInternal: response.IsInternal, + Locale: "en_US", + OrgID: response.OrgID, + DisplayName: response.UserID, + Type: response.Type, + }) + } + + return users +} diff --git a/internal/service/keycloak/keycloak_interface.go b/internal/service/keycloak-user-service/user_service_interface.go similarity index 71% rename from internal/service/keycloak/keycloak_interface.go rename to internal/service/keycloak-user-service/user_service_interface.go index 77de7f8..edce4ba 100644 --- a/internal/service/keycloak/keycloak_interface.go +++ b/internal/service/keycloak-user-service/user_service_interface.go @@ -1,4 +1,4 @@ -package keycloak +package keycloak_user_service import ( "fmt" @@ -7,9 +7,8 @@ import ( "github.com/redhatinsights/mbop/internal/models" ) -type KeyCloak interface { - InitKeycloakConnection() error - GetAccessToken() (string, error) +type KeyCloakUserService interface { + InitKeycloakUserServiceConnection() error GetUsers(token string, users models.UserBody, q models.UserV1Query) (models.Users, error) GetAccountV3Users(orgID string, token string, q models.UserV3Query) (models.Users, error) } @@ -17,12 +16,12 @@ type KeyCloak interface { // re-declaring keycloak constant here to avoid circular module importing const keyCloakModule = "keycloak" -func NewKeyCloakClient() (KeyCloak, error) { - var client KeyCloak +func NewKeyCloakUserServiceClient() (KeyCloakUserService, error) { + var client KeyCloakUserService switch config.Get().UsersModule { case keyCloakModule: - client = &Client{} + client = &UserServiceClient{} default: return nil, fmt.Errorf("unsupported users module %q", config.Get().UsersModule) } diff --git a/internal/service/keycloak/keycloak.go b/internal/service/keycloak/keycloak.go index 2601a17..9a01bcd 100644 --- a/internal/service/keycloak/keycloak.go +++ b/internal/service/keycloak/keycloak.go @@ -6,72 +6,26 @@ import ( "io/ioutil" "net/http" "net/url" - "strconv" "strings" "time" "github.com/redhatinsights/mbop/internal/config" - l "github.com/redhatinsights/mbop/internal/logger" "github.com/redhatinsights/mbop/internal/models" ) -type Client struct { +type KeycloakClient struct { client *http.Client } -func (keyCloak *Client) InitKeycloakConnection() error { - keyCloak.client = &http.Client{ +func (keycloak *KeycloakClient) InitKeycloakConnection() error { + keycloak.client = &http.Client{ Timeout: time.Duration(config.Get().KeyCloakTimeout * int64(time.Second)), } return nil } -func (keyCloak *Client) GetUsers(token string, u models.UserBody, q models.UserV1Query) (models.Users, error) { - users := models.Users{Users: []models.User{}} - url, err := createV1RequestURL(u, q) - if err != nil { - return users, err - } - - body, err := keyCloak.sendKeycloakGetRequest(url, token) - if err != nil { - l.Log.Error(err, "/v3/users error sending request") - return users, err - } - - unmarshaledResponse := models.KeycloakResponses{} - err = json.Unmarshal(body, &unmarshaledResponse) - if err != nil { - return users, err - } - - return keycloakResponseToUsers(unmarshaledResponse.Users), err -} - -func (keyCloak *Client) GetAccountV3Users(orgID string, token string, q models.UserV3Query) (models.Users, error) { - users := models.Users{Users: []models.User{}} - url, err := createV3UsersRequestURL(orgID, q) - if err != nil { - return users, err - } - - body, err := keyCloak.sendKeycloakGetRequest(url, token) - if err != nil { - l.Log.Error(err, "/v3/users error sending request") - return users, err - } - - unmarshaledResponse := models.KeycloakResponses{} - err = json.Unmarshal(body, &unmarshaledResponse) - if err != nil { - return users, err - } - - return keycloakResponseToUsers(unmarshaledResponse.Users), nil -} - -func (keyCloak *Client) GetAccessToken() (string, error) { +func (keycloak *KeycloakClient) GetAccessToken() (string, error) { token := models.KeycloakTokenObject{} url, err := createTokenURL() if err != nil { @@ -100,34 +54,6 @@ func (keyCloak *Client) GetAccessToken() (string, error) { return token.AccessToken, nil } -func (keyCloak *Client) sendKeycloakGetRequest(url *url.URL, token string) ([]byte, error) { - var responseBody []byte - - req, err := http.NewRequest(http.MethodGet, url.String(), nil) - if err != nil { - return responseBody, err - } - - req.Header.Set("Authorization", "Bearer "+token) - - resp, err := keyCloak.client.Do(req) - if err != nil { - l.Log.Error(err, "error fetching keycloak response") - return responseBody, err - } - - responseBody, err = ioutil.ReadAll(resp.Body) - if err != nil { - l.Log.Error(err, "error reading keycloak response body") - return responseBody, err - } - - // Close response body - defer resp.Body.Close() - - return responseBody, nil -} - func createEncodedTokenBody() *strings.Reader { data := url.Values{} data.Set("username", config.Get().KeyCloakTokenUsername) @@ -139,90 +65,10 @@ func createEncodedTokenBody() *strings.Reader { } func createTokenURL() (*url.URL, error) { - url, err := url.Parse(fmt.Sprintf("%s://%s%s/token", config.Get().KeyCloakScheme, config.Get().KeyCloakHost, config.Get().KeyCloakPort)) + url, err := url.Parse(fmt.Sprintf("%s%s", config.Get().KeyCloakTokenURL, config.Get().KeyCloakTokenPath)) if err != nil { return nil, fmt.Errorf("error creating keycloak token url: %s", err) } return url, err } - -// MAKE response to users function to massage data back to regular format -func createV1RequestURL(usernames models.UserBody, q models.UserV1Query) (*url.URL, error) { - url, err := url.Parse(fmt.Sprintf("%s://%s%s/users?limit=100", config.Get().KeyCloakScheme, config.Get().KeyCloakHost, config.Get().KeyCloakPort)) - if err != nil { - return nil, fmt.Errorf("error creating (keycloak) /users url: %s", err) - } - - queryParams := url.Query() - - if q.QueryBy != "" { - queryParams.Add("order", q.QueryBy) - } - - if q.SortOrder != "" { - queryParams.Add("direction", q.SortOrder) - } - - queryParams.Add("usernames", createUsernamesQuery(usernames.Users)) - - url.RawQuery = queryParams.Encode() - return url, err -} - -func createV3UsersRequestURL(orgID string, q models.UserV3Query) (*url.URL, error) { - url, err := url.Parse(fmt.Sprintf("%s://%s:%s/users", config.Get().KeyCloakScheme, config.Get().KeyCloakHost, config.Get().KeyCloakPort)) - if err != nil { - return nil, fmt.Errorf("error creating (keycloak) /v3/users url: %s", err) - } - queryParams := url.Query() - - if q.SortOrder != "" { - queryParams.Add("direction", q.SortOrder) - } - - queryParams.Add("org_id", orgID) - queryParams.Add("limit", strconv.Itoa(q.Limit)) - queryParams.Add("offset", strconv.Itoa(q.Offset)) - - url.RawQuery = queryParams.Encode() - - return url, err -} - -func createUsernamesQuery(usernames []string) string { - usernameQuery := "" - - for _, username := range usernames { - if usernameQuery == "" { - usernameQuery += username - } else { - usernameQuery += fmt.Sprintf(",%s", username) - } - } - - return usernameQuery -} - -func keycloakResponseToUsers(r []models.KeycloakResponse) models.Users { - users := models.Users{Users: []models.User{}} - - for _, response := range r { - users.AddUser(models.User{ - Username: response.Username, - ID: response.ID, - Email: response.Email, - FirstName: response.FirstName, - LastName: response.LastName, - AddressString: "", - IsActive: true, - IsInternal: response.IsInternal, - Locale: "en_US", - OrgID: response.OrgID, - DisplayName: response.UserID, - Type: response.Type, - }) - } - - return users -} diff --git a/internal/service/keycloak/keycloak_integration.go b/internal/service/keycloak/keycloak_integration.go new file mode 100644 index 0000000..543a20c --- /dev/null +++ b/internal/service/keycloak/keycloak_integration.go @@ -0,0 +1,12 @@ +package keycloak + +type KeyCloak interface { + InitKeycloakConnection() error + GetAccessToken() (string, error) +} + +func NewKeyCloakClient() KeyCloak { + client := &KeycloakClient{} + + return client +} From 7abdcca13c92eb3ae0cc58d51466843552049043 Mon Sep 17 00:00:00 2001 From: Alec Cohan Date: Fri, 9 Jun 2023 12:58:32 -0400 Subject: [PATCH 03/11] Fix linting errors --- internal/service/keycloak-user-service/user_service.go | 2 +- .../service/keycloak-user-service/user_service_interface.go | 2 +- internal/service/keycloak/keycloak.go | 6 +++--- internal/service/keycloak/keycloak_integration.go | 2 +- users.json | 3 +++ 5 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 users.json diff --git a/internal/service/keycloak-user-service/user_service.go b/internal/service/keycloak-user-service/user_service.go index 46798d5..2d392e7 100644 --- a/internal/service/keycloak-user-service/user_service.go +++ b/internal/service/keycloak-user-service/user_service.go @@ -1,4 +1,4 @@ -package keycloak_user_service +package keycloakUserService import ( "encoding/json" diff --git a/internal/service/keycloak-user-service/user_service_interface.go b/internal/service/keycloak-user-service/user_service_interface.go index edce4ba..76bd9c2 100644 --- a/internal/service/keycloak-user-service/user_service_interface.go +++ b/internal/service/keycloak-user-service/user_service_interface.go @@ -1,4 +1,4 @@ -package keycloak_user_service +package keycloakUserService import ( "fmt" diff --git a/internal/service/keycloak/keycloak.go b/internal/service/keycloak/keycloak.go index 9a01bcd..5d7c663 100644 --- a/internal/service/keycloak/keycloak.go +++ b/internal/service/keycloak/keycloak.go @@ -13,11 +13,11 @@ import ( "github.com/redhatinsights/mbop/internal/models" ) -type KeycloakClient struct { +type Client struct { client *http.Client } -func (keycloak *KeycloakClient) InitKeycloakConnection() error { +func (keycloak *Client) InitKeycloakConnection() error { keycloak.client = &http.Client{ Timeout: time.Duration(config.Get().KeyCloakTimeout * int64(time.Second)), } @@ -25,7 +25,7 @@ func (keycloak *KeycloakClient) InitKeycloakConnection() error { return nil } -func (keycloak *KeycloakClient) GetAccessToken() (string, error) { +func (keycloak *Client) GetAccessToken() (string, error) { token := models.KeycloakTokenObject{} url, err := createTokenURL() if err != nil { diff --git a/internal/service/keycloak/keycloak_integration.go b/internal/service/keycloak/keycloak_integration.go index 543a20c..a78223c 100644 --- a/internal/service/keycloak/keycloak_integration.go +++ b/internal/service/keycloak/keycloak_integration.go @@ -6,7 +6,7 @@ type KeyCloak interface { } func NewKeyCloakClient() KeyCloak { - client := &KeycloakClient{} + client := &Client{} return client } diff --git a/users.json b/users.json new file mode 100644 index 0000000..6c4cdc8 --- /dev/null +++ b/users.json @@ -0,0 +1,3 @@ +{ + "users": [] +} \ No newline at end of file From 42933d6b137fc3da80b25c351ab8fd08fb67e610 Mon Sep 17 00:00:00 2001 From: Alec Cohan Date: Fri, 9 Jun 2023 13:16:42 -0400 Subject: [PATCH 04/11] Additional linting fixes --- internal/handlers/accounts_v3_usersBy_handler.go | 4 ++-- internal/handlers/accounts_v3_users_handler.go | 4 ++-- internal/handlers/users_v1_handler.go | 5 +++-- internal/models/user.go | 1 + internal/service/keycloak-user-service/user_service.go | 4 ++-- .../service/keycloak-user-service/user_service_interface.go | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/internal/handlers/accounts_v3_usersBy_handler.go b/internal/handlers/accounts_v3_usersBy_handler.go index 10e8c82..55dd5cd 100644 --- a/internal/handlers/accounts_v3_usersBy_handler.go +++ b/internal/handlers/accounts_v3_usersBy_handler.go @@ -6,7 +6,7 @@ import ( "github.com/redhatinsights/mbop/internal/config" "github.com/redhatinsights/mbop/internal/models" "github.com/redhatinsights/mbop/internal/service/keycloak" - keycloak_user_service "github.com/redhatinsights/mbop/internal/service/keycloak-user-service" + keycloakuserservice "github.com/redhatinsights/mbop/internal/service/keycloak-user-service" "github.com/redhatinsights/mbop/internal/service/ocm" ) @@ -118,7 +118,7 @@ func AccountsV3UsersByHandler(w http.ResponseWriter, r *http.Request) { return } - userServiceClient, err := keycloak_user_service.NewKeyCloakUserServiceClient() + userServiceClient, err := keycloakuserservice.NewKeyCloakUserServiceClient() if err != nil { do500(w, "Can't build keycloak user service client: "+err.Error()) return diff --git a/internal/handlers/accounts_v3_users_handler.go b/internal/handlers/accounts_v3_users_handler.go index 04f8ed3..0e901a3 100644 --- a/internal/handlers/accounts_v3_users_handler.go +++ b/internal/handlers/accounts_v3_users_handler.go @@ -5,7 +5,7 @@ import ( "github.com/redhatinsights/mbop/internal/config" "github.com/redhatinsights/mbop/internal/service/keycloak" - keycloak_user_service "github.com/redhatinsights/mbop/internal/service/keycloak-user-service" + keycloakuserservice "github.com/redhatinsights/mbop/internal/service/keycloak-user-service" "github.com/redhatinsights/mbop/internal/service/ocm" ) @@ -95,7 +95,7 @@ func AccountsV3UsersHandler(w http.ResponseWriter, r *http.Request) { return } - userServiceClient, err := keycloak_user_service.NewKeyCloakUserServiceClient() + userServiceClient, err := keycloakuserservice.NewKeyCloakUserServiceClient() if err != nil { do500(w, "Can't build keycloak user service client: "+err.Error()) return diff --git a/internal/handlers/users_v1_handler.go b/internal/handlers/users_v1_handler.go index 2bb3636..a47aea0 100644 --- a/internal/handlers/users_v1_handler.go +++ b/internal/handlers/users_v1_handler.go @@ -5,7 +5,8 @@ import ( "github.com/redhatinsights/mbop/internal/config" "github.com/redhatinsights/mbop/internal/service/keycloak" - keycloak_user_service "github.com/redhatinsights/mbop/internal/service/keycloak-user-service" + keycloakuserservice "github.com/redhatinsights/mbop/internal/service/keycloak-user-service" + "github.com/redhatinsights/mbop/internal/service/ocm" ) @@ -89,7 +90,7 @@ func UsersV1Handler(w http.ResponseWriter, r *http.Request) { return } - userServiceClient, err := keycloak_user_service.NewKeyCloakUserServiceClient() + userServiceClient, err := keycloakuserservice.NewKeyCloakUserServiceClient() if err != nil { do500(w, "Can't build keycloak user service client: "+err.Error()) return diff --git a/internal/models/user.go b/internal/models/user.go index 2db6665..8f1a1bc 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -44,6 +44,7 @@ type KeycloakResponse struct { Created string `json:"created"` Email string `json:"email"` IsInternal bool `json:"is_internal"` + IsActive bool `json:"is_active"` Modified string `json:"modified"` OrgAdmin bool `json:"org_admin"` OrgID string `json:"org_id"` diff --git a/internal/service/keycloak-user-service/user_service.go b/internal/service/keycloak-user-service/user_service.go index 2d392e7..6550409 100644 --- a/internal/service/keycloak-user-service/user_service.go +++ b/internal/service/keycloak-user-service/user_service.go @@ -1,4 +1,4 @@ -package keycloakUserService +package keycloakuserservice import ( "encoding/json" @@ -166,7 +166,7 @@ func keycloakResponseToUsers(r []models.KeycloakResponse) models.Users { FirstName: response.FirstName, LastName: response.LastName, AddressString: "", - IsActive: true, + IsActive: response.IsActive, IsInternal: response.IsInternal, Locale: "en_US", OrgID: response.OrgID, diff --git a/internal/service/keycloak-user-service/user_service_interface.go b/internal/service/keycloak-user-service/user_service_interface.go index 76bd9c2..c0410d9 100644 --- a/internal/service/keycloak-user-service/user_service_interface.go +++ b/internal/service/keycloak-user-service/user_service_interface.go @@ -1,4 +1,4 @@ -package keycloakUserService +package keycloakuserservice import ( "fmt" From 5d818ae218db45ed918ca9347760a8ceb2eccb76 Mon Sep 17 00:00:00 2001 From: Alec Cohan Date: Fri, 9 Jun 2023 13:29:03 -0400 Subject: [PATCH 05/11] Update deployment file with new params --- deployments/deployment.yaml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/deployments/deployment.yaml b/deployments/deployment.yaml index 24e8dce..443a064 100644 --- a/deployments/deployment.yaml +++ b/deployments/deployment.yaml @@ -145,6 +145,18 @@ objects: value: ${KEYCLOAK_TIMEOUT} - name: KEYCLOAK_SCHEME value: ${KEYCLOAK_SCHEME} + - name: KEYCLOAK_USER_SERVICE_HOST + value: ${KEYCLOAK_USER_SERVICE_HOST} + - name: KEYCLOAK_USER_SERVICE_PORT + value: ${KEYCLOAK_USER_SERVICE_PORT} + - name: KEYCLOAK_USER_SERVICE_SCHEME + value: ${KEYCLOAK_USER_SERVICE_SCHEME} + - name: KEYCLOAK_USER_SERVICE_TIMEOUT + value: ${KEYCLOAK_USER_SERVICE_TIMEOUT} + - name: KEYCLOAK_TOKEN_URL + value: ${KEYCLOAK_TOKEN_URL} + - name: KEYCLOAK_TOKEN_PATH + value: ${KEYCLOAK_TOKEN_PATH} - name: KEYCLOAK_TOKEN_GRANT_TYPE value: ${KEYCLOAK_TOKEN_GRANT_TYPE} - name: KEYCLOAK_TOKEN_USERNAME @@ -238,6 +250,24 @@ parameters: - name: KEYCLOAK_TIMEOUT description: keycloak client's timeout value value: "10" +- name: KEYCLOAK_USER_SERVICE_HOST + description: keycloak userservice's host + value: "localhost" +- name: KEYCLOAK_USER_SERVICE_PORT + description: keycloak userservice's post + value: "8000" +- name: KEYCLOAK_USER_SERVICE_SCHEME + description: keycloak userservice's scheme + value: "http" +- name: KEYCLOAK_USER_SERVICE_TIMEOUT + description: keycloak userservice's timeout + value: "60" +- name: KEYCLOAK_TOKEN_URL + description: host for keycloak token request + value: "http://localhost:8080/" +- name: KEYCLOAK_TOKEN_PATH + description: path for keycloak token request + value: "realms/master/protocol/openid-connect/token" - name: KEYCLOAK_TOKEN_GRANT_TYPE description: grant type value for keycloak token request value: password From f6b63eb45308713dd708791bb4863180f7268bbd Mon Sep 17 00:00:00 2001 From: Alec Cohan Date: Fri, 9 Jun 2023 14:18:17 -0400 Subject: [PATCH 06/11] Update userservice port format --- internal/config/config.go | 2 +- internal/service/keycloak-user-service/user_service.go | 4 ++-- users.json | 3 --- 3 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 users.json diff --git a/internal/config/config.go b/internal/config/config.go index ae6910f..4a69a4f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -102,7 +102,7 @@ func Get() *MbopConfig { Debug: debug, KeyCloakUserServiceHost: fetchWithDefault("KEYCLOAK_USER_SERVICE_HOST", "localhost"), - KeyCloakUserServicePort: fetchWithDefault("KEYCLOAK_USER_SERVICE_PORT", "8000"), + KeyCloakUserServicePort: fetchWithDefault("KEYCLOAK_USER_SERVICE_PORT", ":8000"), KeyCloakUserServiceScheme: fetchWithDefault("KEYCLOAK_USER_SERVICE_SCHEME", "http"), KeyCloakUserServiceTimeout: userServiceTimeout, KeyCloakTimeout: keyCloakTimeout, diff --git a/internal/service/keycloak-user-service/user_service.go b/internal/service/keycloak-user-service/user_service.go index 6550409..58b0d81 100644 --- a/internal/service/keycloak-user-service/user_service.go +++ b/internal/service/keycloak-user-service/user_service.go @@ -100,7 +100,7 @@ func (userService *UserServiceClient) sendKeycloakGetRequest(url *url.URL, token // MAKE response to users function to massage data back to regular format func createV1RequestURL(usernames models.UserBody, q models.UserV1Query) (*url.URL, error) { - url, err := url.Parse(fmt.Sprintf("%s://%s:%s/users?limit=100", config.Get().KeyCloakUserServiceScheme, config.Get().KeyCloakUserServiceHost, config.Get().KeyCloakUserServicePort)) + url, err := url.Parse(fmt.Sprintf("%s://%s%s/users?limit=100", config.Get().KeyCloakUserServiceScheme, config.Get().KeyCloakUserServiceHost, config.Get().KeyCloakUserServicePort)) if err != nil { return nil, fmt.Errorf("error creating (keycloak) /users url: %s", err) } @@ -122,7 +122,7 @@ func createV1RequestURL(usernames models.UserBody, q models.UserV1Query) (*url.U } func createV3UsersRequestURL(orgID string, q models.UserV3Query) (*url.URL, error) { - url, err := url.Parse(fmt.Sprintf("%s://%s:%s/users", config.Get().KeyCloakUserServiceScheme, config.Get().KeyCloakUserServiceHost, config.Get().KeyCloakUserServicePort)) + url, err := url.Parse(fmt.Sprintf("%s://%s%s/users", config.Get().KeyCloakUserServiceScheme, config.Get().KeyCloakUserServiceHost, config.Get().KeyCloakUserServicePort)) if err != nil { return nil, fmt.Errorf("error creating (keycloak) /v3/users url: %s", err) } diff --git a/users.json b/users.json deleted file mode 100644 index 6c4cdc8..0000000 --- a/users.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "users": [] -} \ No newline at end of file From 84a99379d965a04021eb67830643ae2d0e3cc28b Mon Sep 17 00:00:00 2001 From: Keith Walsh Date: Sun, 11 Jun 2023 15:54:58 -0400 Subject: [PATCH 07/11] Use client_secret --- internal/service/keycloak/keycloak.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/keycloak/keycloak.go b/internal/service/keycloak/keycloak.go index 5d7c663..6de7d85 100644 --- a/internal/service/keycloak/keycloak.go +++ b/internal/service/keycloak/keycloak.go @@ -57,7 +57,7 @@ func (keycloak *Client) GetAccessToken() (string, error) { func createEncodedTokenBody() *strings.Reader { data := url.Values{} data.Set("username", config.Get().KeyCloakTokenUsername) - data.Set("password", config.Get().KeyCloakTokenPassword) + data.Set("client_secret", config.Get().KeyCloakTokenPassword) data.Set("grant_type", config.Get().KeyCloakTokenGrantType) data.Set("client_id", config.Get().KeyCloakTokenClientID) From 35710e97f659df471599b5bba02da3b722b889a2 Mon Sep 17 00:00:00 2001 From: Keith Walsh Date: Mon, 12 Jun 2023 08:34:11 -0400 Subject: [PATCH 08/11] Tweaks to user service response expectations --- internal/models/user.go | 4 ++-- internal/service/keycloak-user-service/user_service.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/models/user.go b/internal/models/user.go index 8f1a1bc..f5898d5 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -51,8 +51,8 @@ type KeycloakResponse struct { Type string `json:"type"` Username string `json:"username"` UserID string `json:"user_id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` + FirstName string `json:"firstName"` + LastName string `json:"lastName"` ID string `json:"id"` } diff --git a/internal/service/keycloak-user-service/user_service.go b/internal/service/keycloak-user-service/user_service.go index 58b0d81..962485e 100644 --- a/internal/service/keycloak-user-service/user_service.go +++ b/internal/service/keycloak-user-service/user_service.go @@ -172,6 +172,7 @@ func keycloakResponseToUsers(r []models.KeycloakResponse) models.Users { OrgID: response.OrgID, DisplayName: response.UserID, Type: response.Type, + IsOrgAdmin: response.OrgAdmin, }) } From 847ef73ab7125c0a3451d88d31e90a4b6d5eed54 Mon Sep 17 00:00:00 2001 From: Keith Walsh Date: Tue, 13 Jun 2023 14:47:28 -0400 Subject: [PATCH 09/11] Revert "Tweaks to user service response expectations" This reverts commit 35710e97f659df471599b5bba02da3b722b889a2. --- internal/models/user.go | 4 ++-- internal/service/keycloak-user-service/user_service.go | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/models/user.go b/internal/models/user.go index f5898d5..8f1a1bc 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -51,8 +51,8 @@ type KeycloakResponse struct { Type string `json:"type"` Username string `json:"username"` UserID string `json:"user_id"` - FirstName string `json:"firstName"` - LastName string `json:"lastName"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` ID string `json:"id"` } diff --git a/internal/service/keycloak-user-service/user_service.go b/internal/service/keycloak-user-service/user_service.go index 962485e..58b0d81 100644 --- a/internal/service/keycloak-user-service/user_service.go +++ b/internal/service/keycloak-user-service/user_service.go @@ -172,7 +172,6 @@ func keycloakResponseToUsers(r []models.KeycloakResponse) models.Users { OrgID: response.OrgID, DisplayName: response.UserID, Type: response.Type, - IsOrgAdmin: response.OrgAdmin, }) } From 3f1b3e2cd06106c9501c2a92cd70a38b31c8851d Mon Sep 17 00:00:00 2001 From: Keith Walsh Date: Tue, 13 Jun 2023 14:48:44 -0400 Subject: [PATCH 10/11] Add the org admin flag back in --- internal/service/keycloak-user-service/user_service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/keycloak-user-service/user_service.go b/internal/service/keycloak-user-service/user_service.go index 58b0d81..962485e 100644 --- a/internal/service/keycloak-user-service/user_service.go +++ b/internal/service/keycloak-user-service/user_service.go @@ -172,6 +172,7 @@ func keycloakResponseToUsers(r []models.KeycloakResponse) models.Users { OrgID: response.OrgID, DisplayName: response.UserID, Type: response.Type, + IsOrgAdmin: response.OrgAdmin, }) } From d31438a1cb632c9100e88248ce376d4d30fe2693 Mon Sep 17 00:00:00 2001 From: Keith Walsh Date: Tue, 20 Jun 2023 13:15:05 -0400 Subject: [PATCH 11/11] Keycloak adjusted to use --- internal/models/user.go | 2 +- internal/service/keycloak-user-service/user_service.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/models/user.go b/internal/models/user.go index 8f1a1bc..652afa6 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -46,7 +46,7 @@ type KeycloakResponse struct { IsInternal bool `json:"is_internal"` IsActive bool `json:"is_active"` Modified string `json:"modified"` - OrgAdmin bool `json:"org_admin"` + IsOrgAdmin bool `json:"is_org_admin"` OrgID string `json:"org_id"` Type string `json:"type"` Username string `json:"username"` diff --git a/internal/service/keycloak-user-service/user_service.go b/internal/service/keycloak-user-service/user_service.go index 962485e..2920bdb 100644 --- a/internal/service/keycloak-user-service/user_service.go +++ b/internal/service/keycloak-user-service/user_service.go @@ -172,7 +172,7 @@ func keycloakResponseToUsers(r []models.KeycloakResponse) models.Users { OrgID: response.OrgID, DisplayName: response.UserID, Type: response.Type, - IsOrgAdmin: response.OrgAdmin, + IsOrgAdmin: response.IsOrgAdmin, }) }