Browse Source

[CLOUDTRUST-1261] Error handling

master
Sonia 6 years ago
committed by GitHub
parent
commit
112a4a5211
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      Gopkg.lock
  2. 4
      clients.go
  3. 21
      errormessages.go
  4. 13
      integration/integration.go
  5. 72
      keycloak_client.go
  6. 2
      oidc_verifier.go
  7. 10
      users.go

14
Gopkg.lock

@ -115,7 +115,7 @@
[[projects]]
branch = "master"
digest = "1:a530f8e0c0ee8a3b440f9f0b0e9f4e5d5e47cfe3a581086ce32cd8ba114ddf4f"
digest = "1:086760278d762dbb0e9a26e09b57f04c89178c86467d8d94fae47d64c222f328"
name = "golang.org/x/crypto"
packages = [
"ed25519",
@ -123,11 +123,11 @@
"pbkdf2",
]
pruneopts = ""
revision = "9756ffdc24725223350eb3266ffb92590d28f278"
revision = "4def268fd1a49955bfb3dda92fe3db4f924f2285"
[[projects]]
branch = "master"
digest = "1:87c06c289123bf8be0a776c57ca40ce075f6c598a905ff2ff8ba40fba0d5d17c"
digest = "1:31cd6e3c114e17c5f0c9e8b0bcaa3025ab3c221ce36323c7ce1acaa753d0d0aa"
name = "golang.org/x/net"
packages = [
"context",
@ -136,7 +136,7 @@
"publicsuffix",
]
pruneopts = ""
revision = "ba9fcec4b297b415637633c5a6e8fa592e4a16c3"
revision = "da137c7871d730100384dbcf36e6f8fa493aef5b"
[[projects]]
branch = "master"
@ -175,7 +175,7 @@
version = "v0.3.2"
[[projects]]
digest = "1:0568e577f790e9bd0420521cff50580f9b38165a38f217ce68f55c4bbaa97066"
digest = "1:47f391ee443f578f01168347818cb234ed819521e49e4d2c8dd2fb80d48ee41a"
name = "google.golang.org/appengine"
packages = [
"internal",
@ -187,8 +187,8 @@
"urlfetch",
]
pruneopts = ""
revision = "5f2a59506353b8d5ba8cbbcd9f3c1f41f1eaf079"
version = "v1.6.2"
revision = "b2f4a3cf3c67576a2ee09e1fe62656a5086ce880"
version = "v1.6.1"
[[projects]]
digest = "1:e3250d192192f02fbb143d50de437cbe967d6be7bd9fad671600942a33269d08"

4
clients.go

@ -1,7 +1,7 @@
package keycloak
import (
"fmt"
"errors"
"gopkg.in/h2non/gentleman.v2/plugins/url"
)
@ -17,7 +17,7 @@ const (
// viewableOnly (filter clients that cannot be viewed in full by admin, default="false")
func (c *Client) GetClients(accessToken string, realmName string, paramKV ...string) ([]ClientRepresentation, error) {
if len(paramKV)%2 != 0 {
return nil, fmt.Errorf("the number of key/val parameters should be even")
return nil, errors.New(MsgErrInvalidParam + "." + EvenParams)
}
var resp = []ClientRepresentation{}

21
errormessages.go

@ -0,0 +1,21 @@
package keycloak
const (
MsgErrMissingParam = "missingParameter"
MsgErrInvalidParam = "invalidParameter"
MsgErrCannotObtain = "cannotObtain"
MsgErrCannotMarshal = "cannotMarshal"
MsgErrCannotUnmarshal = "cannotUnmarshal"
MsgErrCannotParse = "cannotParse"
MsgErrCannotCreate = "cannotCreate"
MsgErrUnkownHTTPContentType = "unkownHTTPContentType"
MsgErrUnknownResponseStatusCode = "unknownResponseStatusCode"
EvenParams = "key/valParametersShouldBeEven"
TokenProviderURL = "tokenProviderURL"
APIURL = "APIURL"
TokenMsg = "token"
Response = "response"
AccessToken = "accessToken"
OIDCProvider = "OIDCProvider"
)

13
integration/integration.go

@ -16,19 +16,6 @@ const (
user = "version"
)
// This should be converted into
// GetClient(accessToken string, realmName, idClient string) (kc.ClientRepresentation, error)
// GetClientRoleMappings(accessToken string, realmName, userID, clientID string) ([]kc.RoleRepresentation, error)
// AddClientRolesToUserRoleMapping(accessToken string, realmName, userID, clientID string, roles []kc.RoleRepresentation) error
// GetRealmLevelRoleMappings(accessToken string, realmName, userID string) ([]kc.RoleRepresentation, error)
// ResetPassword(accessToken string, realmName string, userID string) error
// SendVerifyEmail(accessToken string, realmName string, userID string) error
// GetRoles(accessToken string, realmName string) ([]kc.RoleRepresentation, error)
// GetRole(accessToken string, realmName string, roleID string) (kc.RoleRepresentation, error)
// GetClientRoles(accessToken string, realmName, idClient string) ([]kc.RoleRepresentation, error)
// CreateClientRole(accessToken string, realmName, clientID string, role kc.RoleRepresentation) (string, error)
func main() {
var conf = getKeycloakConfig()
var client, err = keycloak.New(*conf)

72
keycloak_client.go

@ -2,11 +2,13 @@ package keycloak
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
commonhttp "github.com/cloudtrust/common-service/errors"
"github.com/pkg/errors"
"gopkg.in/h2non/gentleman.v2"
"gopkg.in/h2non/gentleman.v2/plugin"
@ -49,7 +51,7 @@ func New(config Config) (*Client, error) {
var err error
uToken, err = url.Parse(config.AddrTokenProvider)
if err != nil {
return nil, errors.Wrap(err, "could not parse Token Provider URL")
return nil, errors.Wrap(err, MsgErrCannotParse+"."+TokenProviderURL)
}
}
@ -58,7 +60,7 @@ func New(config Config) (*Client, error) {
var err error
uAPI, err = url.Parse(config.AddrAPI)
if err != nil {
return nil, errors.Wrap(err, "could not parse API URL")
return nil, errors.Wrap(err, MsgErrCannotParse+"."+APIURL)
}
}
@ -87,7 +89,7 @@ func New(config Config) (*Client, error) {
return client, nil
}
// GetToken returns a valid token from keycloak.
// GetToken returns a valid token from keycloak
func (c *Client) GetToken(realm string, username string, password string) (string, error) {
var req *gentleman.Request
{
@ -104,7 +106,7 @@ func (c *Client) GetToken(realm string, username string, password string) (strin
var err error
resp, err = req.Do()
if err != nil {
return "", errors.Wrap(err, "could not get token")
return "", errors.Wrap(err, MsgErrCannotObtain+"."+TokenMsg)
}
}
defer resp.Close()
@ -114,7 +116,7 @@ func (c *Client) GetToken(realm string, username string, password string) (strin
var err error
err = resp.JSON(&unmarshalledBody)
if err != nil {
return "", errors.Wrap(err, "could not unmarshal response")
return "", errors.Wrap(err, MsgErrCannotUnmarshal+"."+Response)
}
}
@ -123,7 +125,7 @@ func (c *Client) GetToken(realm string, username string, password string) (strin
var ok bool
accessToken, ok = unmarshalledBody["access_token"]
if !ok {
return "", fmt.Errorf("could not find access token in response body")
return "", fmt.Errorf(MsgErrMissingParam + "." + AccessToken)
}
}
@ -158,7 +160,7 @@ func (c *Client) get(accessToken string, data interface{}, plugins ...plugin.Plu
var err error
resp, err = req.Do()
if err != nil {
return errors.Wrap(err, "could not get response")
return errors.Wrap(err, MsgErrCannotObtain+"."+Response)
}
switch {
@ -171,10 +173,7 @@ func (c *Client) get(accessToken string, data interface{}, plugins ...plugin.Plu
var response map[string]string
err := json.Unmarshal(resp.Bytes(), &response)
if message, ok := response["errorMessage"]; ok && err == nil {
return HTTPError{
HTTPStatus: resp.StatusCode,
Message: message,
}
return whitelistErrors(resp.StatusCode, message)
}
return HTTPError{
HTTPStatus: resp.StatusCode,
@ -188,10 +187,10 @@ func (c *Client) get(accessToken string, data interface{}, plugins ...plugin.Plu
data = resp.Bytes()
return nil
default:
return fmt.Errorf("unkown http content-type: %v", resp.Header.Get("Content-Type"))
return fmt.Errorf("%s.%v", MsgErrUnkownHTTPContentType, resp.Header.Get("Content-Type"))
}
default:
return fmt.Errorf("unknown response status code: %v", resp.StatusCode)
return fmt.Errorf("%s.%v", MsgErrUnknownResponseStatusCode, resp.StatusCode)
}
}
}
@ -211,7 +210,7 @@ func (c *Client) post(accessToken string, data interface{}, plugins ...plugin.Pl
var err error
resp, err = req.Do()
if err != nil {
return "", errors.Wrap(err, "could not get response")
return "", errors.Wrap(err, MsgErrCannotObtain+"."+Response)
}
switch {
@ -224,10 +223,7 @@ func (c *Client) post(accessToken string, data interface{}, plugins ...plugin.Pl
var response map[string]string
err := json.Unmarshal(resp.Bytes(), &response)
if message, ok := response["errorMessage"]; ok && err == nil {
return "", HTTPError{
HTTPStatus: resp.StatusCode,
Message: message,
}
return "", whitelistErrors(resp.StatusCode, message)
}
return "", HTTPError{
HTTPStatus: resp.StatusCode,
@ -246,7 +242,7 @@ func (c *Client) post(accessToken string, data interface{}, plugins ...plugin.Pl
return location, nil
}
default:
return "", fmt.Errorf("unknown response status code: %v", resp.StatusCode)
return "", fmt.Errorf("%s.%v", MsgErrUnknownResponseStatusCode, resp.StatusCode)
}
}
}
@ -266,7 +262,7 @@ func (c *Client) delete(accessToken string, plugins ...plugin.Plugin) error {
var err error
resp, err = req.Do()
if err != nil {
return errors.Wrap(err, "could not get response")
return errors.Wrap(err, MsgErrCannotObtain+"."+Response)
}
switch {
@ -279,10 +275,7 @@ func (c *Client) delete(accessToken string, plugins ...plugin.Plugin) error {
var response map[string]string
err := json.Unmarshal(resp.Bytes(), &response)
if message, ok := response["errorMessage"]; ok && err == nil {
return HTTPError{
HTTPStatus: resp.StatusCode,
Message: message,
}
return whitelistErrors(resp.StatusCode, message)
}
return HTTPError{
HTTPStatus: resp.StatusCode,
@ -314,7 +307,7 @@ func (c *Client) put(accessToken string, plugins ...plugin.Plugin) error {
var err error
resp, err = req.Do()
if err != nil {
return errors.Wrap(err, "could not get response")
return errors.Wrap(err, MsgErrCannotObtain+"."+Response)
}
switch {
@ -327,10 +320,7 @@ func (c *Client) put(accessToken string, plugins ...plugin.Plugin) error {
var response map[string]string
err := json.Unmarshal(resp.Bytes(), &response)
if message, ok := response["errorMessage"]; ok && err == nil {
return HTTPError{
HTTPStatus: resp.StatusCode,
Message: message,
}
return whitelistErrors(resp.StatusCode, message)
}
return HTTPError{
HTTPStatus: resp.StatusCode,
@ -383,7 +373,7 @@ func extractHostFromToken(token string) (string, error) {
var err error
u, err = url.Parse(issuer)
if err != nil {
return "", errors.Wrap(err, "could not parse Token issuer URL")
return "", errors.Wrap(err, MsgErrCannotParse+"."+TokenProviderURL)
}
}
@ -394,13 +384,13 @@ func extractIssuerFromToken(token string) (string, error) {
payload, _, err := jwt.Parse(token)
if err != nil {
return "", errors.Wrap(err, "could not parse Token")
return "", errors.Wrap(err, MsgErrCannotParse+"."+TokenMsg)
}
var jot Token
if err = jwt.Unmarshal(payload, &jot); err != nil {
return "", errors.Wrap(err, "could not unmarshall token")
return "", errors.Wrap(err, MsgErrCannotUnmarshal+"."+TokenMsg)
}
return jot.Issuer, nil
@ -421,6 +411,24 @@ func str(s string) *string {
return &s
}
func whitelistErrors(statusCode int, message string) error {
// whitelist errors from Keycloak
switch message {
//POST account/credentials/password with error message "invalidPasswordExistingMessage"
case "invalidPasswordExistingMessage":
return commonhttp.Error{
Status: statusCode,
Message: "keycloak." + message,
}
default:
return 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.

2
oidc_verifier.go

@ -55,7 +55,7 @@ func (vc *verifierCache) GetOidcVerifier(realm string) (OidcVerifier, error) {
var issuer = fmt.Sprintf("%s/auth/realms/%s", vc.tokenProviderURL.String(), realm)
oidcProvider, err = oidc.NewProvider(context.Background(), issuer)
if err != nil {
return nil, errors.Wrap(err, "could not create oidc provider")
return nil, errors.Wrap(err, MsgErrCannotCreate+"."+OIDCProvider)
}
}

10
users.go

@ -1,7 +1,7 @@
package keycloak
import (
"fmt"
"errors"
"gopkg.in/h2non/gentleman.v2/plugins/body"
"gopkg.in/h2non/gentleman.v2/plugins/headers"
@ -33,7 +33,7 @@ const (
func (c *Client) GetUsers(accessToken string, reqRealmName, targetRealmName string, paramKV ...string) (UsersPageRepresentation, error) {
var resp UsersPageRepresentation
if len(paramKV)%2 != 0 {
return resp, fmt.Errorf("the number of key/val parameters should be even")
return resp, errors.New(MsgErrInvalidParam + "." + EvenParams)
}
var plugins = append(createQueryPlugins(paramKV...), url.Path(usersAdminExtensionApiPath), url.Param("realmReq", reqRealmName), url.Param("realm", targetRealmName))
@ -85,7 +85,7 @@ func (c *Client) ResetPassword(accessToken string, realmName, userID string, cre
// SendVerifyEmail sends an email-verification email to the user An email contains a link the user can click to verify their email address.
func (c *Client) SendVerifyEmail(accessToken string, realmName string, userID string, paramKV ...string) error {
if len(paramKV)%2 != 0 {
return fmt.Errorf("the number of key/val parameters should be even")
return errors.New(MsgErrInvalidParam + "." + EvenParams)
}
var plugins = append(createQueryPlugins(paramKV...), url.Path(sendVerifyEmailPath), url.Param("realm", realmName), url.Param("id", userID))
@ -96,7 +96,7 @@ func (c *Client) SendVerifyEmail(accessToken string, realmName string, userID st
// ExecuteActionsEmail sends an update account email to the user. An email contains a link the user can click to perform a set of required actions.
func (c *Client) ExecuteActionsEmail(accessToken string, realmName string, userID string, actions []string, paramKV ...string) error {
if len(paramKV)%2 != 0 {
return fmt.Errorf("the number of key/val parameters should be even")
return errors.New(MsgErrInvalidParam + "." + EvenParams)
}
var plugins = append(createQueryPlugins(paramKV...), url.Path(executeActionsEmailPath), url.Param("realm", realmName), url.Param("id", userID), body.JSON(actions))
@ -119,7 +119,7 @@ func (c *Client) SendNewEnrolmentCode(accessToken string, realmName string, user
// SendReminderEmail sends a reminder email to a user
func (c *Client) SendReminderEmail(accessToken string, realmName string, userID string, paramKV ...string) error {
if len(paramKV)%2 != 0 {
return fmt.Errorf("the number of key/val parameters should be even")
return errors.New(MsgErrInvalidParam + "." + EvenParams)
}
var newParamKV = append(paramKV, "userid", userID)

Loading…
Cancel
Save