diff --git a/api/authentication_management.go b/api/authentication_management.go index 44ffe98..5f76908 100644 --- a/api/authentication_management.go +++ b/api/authentication_management.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/client_attribute_certificate.go b/api/client_attribute_certificate.go index 63d8d44..d74b0ff 100644 --- a/api/client_attribute_certificate.go +++ b/api/client_attribute_certificate.go @@ -3,7 +3,7 @@ package api import ( "bytes" - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/client_initial_access.go b/api/client_initial_access.go index 5528208..8cadecc 100644 --- a/api/client_initial_access.go +++ b/api/client_initial_access.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/client_registration_policy.go b/api/client_registration_policy.go index 9bba300..c819326 100644 --- a/api/client_registration_policy.go +++ b/api/client_registration_policy.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/client_role_mappings.go b/api/client_role_mappings.go index f604785..d98201e 100644 --- a/api/client_role_mappings.go +++ b/api/client_role_mappings.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/clients.go b/api/clients.go index f698417..3c70e0e 100644 --- a/api/clients.go +++ b/api/clients.go @@ -3,7 +3,7 @@ package api import ( "errors" - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/components.go b/api/components.go index 57442e3..6c2408a 100644 --- a/api/components.go +++ b/api/components.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/credentials.go b/api/credentials.go index ba02e4d..904fe91 100644 --- a/api/credentials.go +++ b/api/credentials.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/headers" "gopkg.in/h2non/gentleman.v2/plugins/url" diff --git a/api/groups.go b/api/groups.go index 9f38ea8..4dbc02b 100644 --- a/api/groups.go +++ b/api/groups.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/identity_providers.go b/api/identity_providers.go index 7a1283c..e10ac1f 100644 --- a/api/identity_providers.go +++ b/api/identity_providers.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/keycloak_client.go b/api/keycloak_client.go index 64193c7..d86e932 100644 --- a/api/keycloak_client.go +++ b/api/keycloak_client.go @@ -1,22 +1,15 @@ package api import ( - "encoding/json" - "regexp" - "fmt" - "net/http" "net/url" - commonhttp "github.com/cloudtrust/common-service/errors" - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "github.com/pkg/errors" "gopkg.in/h2non/gentleman.v2" "gopkg.in/h2non/gentleman.v2/plugin" "gopkg.in/h2non/gentleman.v2/plugins/query" "gopkg.in/h2non/gentleman.v2/plugins/timeout" - - jwt "github.com/gbrlsnchs/jwt/v2" ) // Client is the keycloak client. @@ -90,9 +83,6 @@ func (c *Client) GetToken(realm string, username string, password string) (strin } } - // fmt.Printf("%s", accessToken.(string)) - // fmt.Println() - return accessToken.(string), nil } @@ -101,7 +91,7 @@ func (c *Client) get(accessToken string, data interface{}, plugins ...plugin.Plu var err error var req = c.httpClient.Get() req = applyPlugins(req, plugins...) - req, err = setAuthorisationAndHostHeaders(req, accessToken) + req = setAuthorisationHeader(req, accessToken) if err != nil { return err @@ -115,23 +105,21 @@ func (c *Client) get(accessToken string, data interface{}, plugins ...plugin.Plu return errors.Wrap(err, keycloak.MsgErrCannotObtain+"."+keycloak.Response) } - switch { - case resp.StatusCode == http.StatusUnauthorized: - return keycloak.ClientDetailedError{HTTPStatus: http.StatusUnauthorized, Message: string(resp.Bytes())} - case resp.StatusCode >= 400: - return treatErrorStatus(resp) - case resp.StatusCode >= 200: - switch resp.Header.Get("Content-Type") { - case "application/json": - return resp.JSON(data) - case "application/octet-stream": - data = resp.Bytes() - return nil - default: - return fmt.Errorf("%s.%v", keycloak.MsgErrUnkownHTTPContentType, resp.Header.Get("Content-Type")) + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + return keycloak.HTTPError{ + HTTPStatus: resp.StatusCode, + Message: string(resp.Bytes()), } + } + + switch resp.Header.Get("Content-Type") { + case "application/json": + return resp.JSON(data) + case "application/octet-stream": + _ = resp.Bytes() + return nil default: - return fmt.Errorf("%s.%v", keycloak.MsgErrUnknownResponseStatusCode, resp.StatusCode) + return fmt.Errorf("%s.%v", keycloak.MsgErrUnkownHTTPContentType, resp.Header.Get("Content-Type")) } } } @@ -140,7 +128,7 @@ func (c *Client) post(accessToken string, data interface{}, plugins ...plugin.Pl var err error var req = c.httpClient.Post() req = applyPlugins(req, plugins...) - req, err = setAuthorisationAndHostHeaders(req, accessToken) + req = setAuthorisationHeader(req, accessToken) if err != nil { return "", err @@ -154,25 +142,23 @@ func (c *Client) post(accessToken string, data interface{}, plugins ...plugin.Pl return "", errors.Wrap(err, keycloak.MsgErrCannotObtain+"."+keycloak.Response) } - switch { - case resp.StatusCode == http.StatusUnauthorized: - return "", keycloak.ClientDetailedError{HTTPStatus: http.StatusUnauthorized, Message: string(resp.Bytes())} - case resp.StatusCode >= 400: - return "", treatErrorStatus(resp) - case resp.StatusCode >= 200: - var location = resp.Header.Get("Location") - - switch resp.Header.Get("Content-Type") { - case "application/json": - return location, resp.JSON(data) - case "application/octet-stream": - data = resp.Bytes() - return location, nil - default: - return location, nil + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + return "", keycloak.HTTPError{ + HTTPStatus: resp.StatusCode, + Message: string(resp.Bytes()), } + } + + var location = resp.Header.Get("Location") + + switch resp.Header.Get("Content-Type") { + case "application/json": + return location, resp.JSON(data) + case "application/octet-stream": + data = resp.Bytes() + return location, nil default: - return "", fmt.Errorf("%s.%v", keycloak.MsgErrUnknownResponseStatusCode, resp.StatusCode) + return location, nil } } } @@ -181,7 +167,7 @@ func (c *Client) delete(accessToken string, plugins ...plugin.Plugin) error { var err error var req = c.httpClient.Delete() req = applyPlugins(req, plugins...) - req, err = setAuthorisationAndHostHeaders(req, accessToken) + req = setAuthorisationHeader(req, accessToken) if err != nil { return err @@ -195,19 +181,14 @@ func (c *Client) delete(accessToken string, plugins ...plugin.Plugin) error { return errors.Wrap(err, keycloak.MsgErrCannotObtain+"."+keycloak.Response) } - switch { - case resp.StatusCode == http.StatusUnauthorized: - return keycloak.ClientDetailedError{HTTPStatus: http.StatusUnauthorized, Message: string(resp.Bytes())} - case resp.StatusCode >= 400: - return treatErrorStatus(resp) - case resp.StatusCode >= 200: - return nil - default: + if resp.StatusCode < 200 || resp.StatusCode >= 400 { return keycloak.HTTPError{ HTTPStatus: resp.StatusCode, Message: string(resp.Bytes()), } } + + return nil } } @@ -215,7 +196,7 @@ func (c *Client) put(accessToken string, plugins ...plugin.Plugin) error { var err error var req = c.httpClient.Put() req = applyPlugins(req, plugins...) - req, err = setAuthorisationAndHostHeaders(req, accessToken) + req = setAuthorisationHeader(req, accessToken) if err != nil { return err @@ -229,35 +210,21 @@ func (c *Client) put(accessToken string, plugins ...plugin.Plugin) error { return errors.Wrap(err, keycloak.MsgErrCannotObtain+"."+keycloak.Response) } - switch { - case resp.StatusCode == http.StatusUnauthorized: - return keycloak.ClientDetailedError{HTTPStatus: http.StatusUnauthorized, Message: string(resp.Bytes())} - case resp.StatusCode >= 400: - return treatErrorStatus(resp) - case resp.StatusCode >= 200: - return nil - default: + if resp.StatusCode < 200 || resp.StatusCode >= 400 { return keycloak.HTTPError{ HTTPStatus: resp.StatusCode, Message: string(resp.Bytes()), } } - } -} -func setAuthorisationAndHostHeaders(req *gentleman.Request, accessToken string) (*gentleman.Request, error) { - host, err := extractHostFromToken(accessToken) - - if err != nil { - return req, err + return nil } +} +func setAuthorisationHeader(req *gentleman.Request, accessToken string) *gentleman.Request { var r = req.SetHeader("Authorization", fmt.Sprintf("Bearer %s", accessToken)) r = r.SetHeader("X-Forwarded-Proto", "https") - - r.Context.Request.Host = host - - return r, nil + return r } // applyPlugins apply all the plugins to the request req. @@ -269,41 +236,6 @@ func applyPlugins(req *gentleman.Request, plugins ...plugin.Plugin) *gentleman.R return r } -func extractHostFromToken(token string) (string, error) { - issuer, err := extractIssuerFromToken(token) - - if err != nil { - return "", err - } - - var u *url.URL - { - var err error - u, err = url.Parse(issuer) - if err != nil { - return "", errors.Wrap(err, keycloak.MsgErrCannotParse+"."+keycloak.TokenProviderURL) - } - } - - return u.Host, nil -} - -func extractIssuerFromToken(token string) (string, error) { - payload, _, err := jwt.Parse(token) - - if err != nil { - return "", errors.Wrap(err, keycloak.MsgErrCannotParse+"."+keycloak.TokenMsg) - } - - var jot Token - - if err = jwt.Unmarshal(payload, &jot); err != nil { - return "", errors.Wrap(err, keycloak.MsgErrCannotUnmarshal+"."+keycloak.TokenMsg) - } - - return jot.Issuer, nil -} - // createQueryPlugins create query parameters with the key values paramKV. func createQueryPlugins(paramKV ...string) []plugin.Plugin { var plugins = []plugin.Plugin{} @@ -314,70 +246,3 @@ func createQueryPlugins(paramKV ...string) []plugin.Plugin { } return plugins } - -func treatErrorStatus(resp *gentleman.Response) error { - var response map[string]interface{} - err := json.Unmarshal(resp.Bytes(), &response) - if message, ok := response["errorMessage"]; ok && err == nil { - return whitelistErrors(resp.StatusCode, message.(string)) - } - return keycloak.HTTPError{ - HTTPStatus: resp.StatusCode, - Message: string(resp.Bytes()), - } -} - -func whitelistErrors(statusCode int, message string) error { - // whitelist errors from Keycloak - reg := regexp.MustCompile("invalidPassword[a-zA-Z]*Message") - errorMessages := map[string]string{ - "User exists with same username or email": keycloak.MsgErrExistingValue + "." + keycloak.UserOrEmail, - "usernameExistsMessage": keycloak.MsgErrExistingValue + "." + keycloak.UserOrEmail, - "emailExistsMessage": keycloak.MsgErrExistingValue + "." + keycloak.UserOrEmail, - "User exists with same username": keycloak.MsgErrExistingValue + "." + keycloak.Username, - "User exists with same email": keycloak.MsgErrExistingValue + "." + keycloak.Email, - "readOnlyUsernameMessage": keycloak.MsgErrReadOnly + "." + keycloak.Username, - } - - switch { - //POST account/credentials/password with error message related to invalid value for the password - // of the format invalidPassword{a-zA-Z}*Message, e.g. invalidPasswordMinDigitsMessage - case reg.MatchString(message): - return commonhttp.Error{ - Status: statusCode, - Message: "keycloak." + message, - } - // update account in back-office or self-service - case errorMessages[message] != "": - return commonhttp.Error{ - Status: statusCode, - Message: "keycloak." + errorMessages[message], - } - default: - return keycloak.HTTPError{ - HTTPStatus: statusCode, - Message: message, - } - } -} - -// Token is JWT token. -// We need to define our own structure as the library define aud as a string but it can also be a string array. -// To fix this issue, we remove aud as we do not use it here. -type Token struct { - hdr *header - Issuer string `json:"iss,omitempty"` - Subject string `json:"sub,omitempty"` - ExpirationTime int64 `json:"exp,omitempty"` - NotBefore int64 `json:"nbf,omitempty"` - IssuedAt int64 `json:"iat,omitempty"` - ID string `json:"jti,omitempty"` - Username string `json:"preferred_username,omitempty"` -} - -type header struct { - Algorithm string `json:"alg,omitempty"` - KeyID string `json:"kid,omitempty"` - Type string `json:"typ,omitempty"` - ContentType string `json:"cty,omitempty"` -} diff --git a/api/realm.go b/api/realm.go index 2d4d3eb..1e0ce6b 100644 --- a/api/realm.go +++ b/api/realm.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/recovery_code.go b/api/recovery_code.go index fe3a161..dd9eab2 100644 --- a/api/recovery_code.go +++ b/api/recovery_code.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/query" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/roles.go b/api/roles.go index 13cace0..834e451 100644 --- a/api/roles.go +++ b/api/roles.go @@ -1,7 +1,7 @@ package api import ( - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/api/statistics.go b/api/statistics.go deleted file mode 100644 index 72ae543..0000000 --- a/api/statistics.go +++ /dev/null @@ -1,26 +0,0 @@ -package api - -import ( - "github.com/cloudtrust/keycloak-client/v3" - "gopkg.in/h2non/gentleman.v2/plugins/url" -) - -const ( - statisticsPath = "/auth/realms/master/api/admin/realms/:realm/statistics" - statisticsUsers = statisticsPath + "/users" - statisticsCredentials = statisticsPath + "/credentials" -) - -// GetStatisticsUsers returns statisctics on the total number of users and on their status -func (c *Client) GetStatisticsUsers(accessToken string, realmName string) (keycloak.StatisticsUsersRepresentation, error) { - var resp = keycloak.StatisticsUsersRepresentation{} - var err = c.get(accessToken, &resp, url.Path(statisticsUsers), url.Param("realm", realmName)) - return resp, err -} - -// GetStatisticsAuthenticators returns statistics on the authenticators used by the users on a certain realm -func (c *Client) GetStatisticsAuthenticators(accessToken string, realmName string) (map[string]int64, error) { - var resp = make(map[string]int64) - var err = c.get(accessToken, &resp, url.Path(statisticsCredentials), url.Param("realm", realmName)) - return resp, err -} diff --git a/api/users.go b/api/users.go index d5d39a3..8565935 100644 --- a/api/users.go +++ b/api/users.go @@ -3,7 +3,7 @@ package api import ( "errors" - "github.com/cloudtrust/keycloak-client/v3" + "github.com/nmasse-itix/keycloak-client" "gopkg.in/h2non/gentleman.v2/plugins/body" "gopkg.in/h2non/gentleman.v2/plugins/url" ) diff --git a/errormessages.go b/errormessages.go index bcf0189..f67eafc 100644 --- a/errormessages.go +++ b/errormessages.go @@ -24,10 +24,6 @@ const ( TokenMsg = "token" Response = "response" AccessToken = "accessToken" - OIDCProvider = "OIDCProvider" - UserOrEmail = "UsernameOrEmail" - Username = "username" - Email = "email" ) // HTTPError is returned when an error occured while contacting the keycloak instance. diff --git a/go.mod b/go.mod index 7a7cce9..274d7cb 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,8 @@ -module github.com/cloudtrust/keycloak-client/v3 +module github.com/nmasse-itix/keycloak-client go 1.15 require ( - github.com/cloudtrust/common-service v2.3.2+incompatible github.com/davecgh/go-spew v1.1.1 // indirect github.com/gbrlsnchs/jwt/v2 v2.0.0 github.com/kr/pretty v0.1.0 // indirect diff --git a/go.sum b/go.sum index 66fa392..b17600b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/cloudtrust/common-service v2.3.2+incompatible h1:UpnaO5LjwsNjL3W8fQbZ03W5UrJZ8bT1mr91i0G7iKw= -github.com/cloudtrust/common-service v2.3.2+incompatible/go.mod h1:xvzyeNyD7L0x0xHn4Uqbpx/q9Z4tUT3gicALOAN40X4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/integration/integration-tests.go b/integration/integration-tests.go index 304c6f3..2c7a54b 100644 --- a/integration/integration-tests.go +++ b/integration/integration-tests.go @@ -8,8 +8,8 @@ import ( "strings" "time" - "github.com/cloudtrust/keycloak-client/v3" - api "github.com/cloudtrust/keycloak-client/v3/api" + "github.com/nmasse-itix/keycloak-client" + api "github.com/nmasse-itix/keycloak-client/api" "github.com/spf13/pflag" )