Repository: Nerzal/gocloak
Pull request #123 (2019-12-20 18:35:10)

Request permission
Depends-on https://github.com/Nerzal/gocloak/pull/122

Removing `RequestPermission` as it's intended functionality is covered by `GetRequestingPartyToken`. Adding some coverage to `Permissions` in `RetrospecTokenResult`.



Patch:
diff --git a/client.go b/client.go
index 4493679..fc53470 100644
--- a/client.go
+++ b/client.go
@@ -370,18 +370,6 @@ func (client *gocloak) LogoutPublicClient(clientID, realm, accessToken, refreshT
 	return checkForError(resp, err)
 }
 
-// RequestPermission request a permission
-func (client *gocloak) RequestPermission(clientID, clientSecret, realm, username, password string, permission string) (*JWT, error) {
-	return client.GetToken(realm, TokenOptions{
-		ClientID:     &clientID,
-		ClientSecret: &clientSecret,
-		GrantType:    StringP("password"),
-		Username:     &username,
-		Password:     &password,
-		Permission:   &permission,
-	})
-}
-
 // ExecuteActionsEmail executes an actions email
 func (client *gocloak) ExecuteActionsEmail(token, realm string, params ExecuteActionsEmail) error {
 	queryParams, err := GetQueryParams(params)
diff --git a/gocloak.go b/gocloak.go
index c7dc443..aed3417 100644
--- a/gocloak.go
+++ b/gocloak.go
@@ -26,8 +26,6 @@ type GoCloak interface {
 	LoginClient(clientID, clientSecret, realm string) (*JWT, error)
 	// LoginAdmin login as admin
 	LoginAdmin(username, password, realm string) (*JWT, error)
-	// RequestPermission sends a request to the token endpoint with permission parameter
-	RequestPermission(clientID, clientSecret, realm, username, password, permission string) (*JWT, error)
 	// RefreshToken used to refresh the token
 	RefreshToken(refreshToken string, clientID, clientSecret, realm string) (*JWT, error)
 	// DecodeAccessToken decodes the accessToken
diff --git a/models.go b/models.go
index 25dcc07..5d5f8ba 100644
--- a/models.go
+++ b/models.go
@@ -89,17 +89,26 @@ type IssuerResponse struct {
 	TokensNotBefore *int    `json:"tokens-not-before,omitempty"`
 }
 
+// ResourcePermission represents a permission granted to a resource
+type ResourcePermission struct {
+	RSID           *string  `json:"rsid,omitempty"`
+	ResourceID     *string  `json:"resource_id,omitempty"`
+	RSName         *string  `json:"rsname,omitempty"`
+	Scopes         []string `json:"scopes,omitempty"`
+	ResourceScopes []string `json:"resource_scopes,omitempty"`
+}
+
 // RetrospecTokenResult is returned when a token was checked
 type RetrospecTokenResult struct {
-	Permissions map[string]string `json:"permissions,omitempty"`
-	Exp         *int              `json:"exp,omitempty"`
-	Nbf         *int              `json:"nbf,omitempty"`
-	Iat         *int              `json:"iat,omitempty"`
-	Aud         *StringOrArray    `json:"aud,omitempty"`
-	Active      *bool             `json:"active"`
-	AuthTime    *int              `json:"auth_time,omitempty"`
-	Jti         *string           `json:"jti,omitempty"`
-	Type        *string           `json:"typ,omitempty"`
+	Permissions []*ResourcePermission `json:"permissions,omitempty"`
+	Exp         *int                  `json:"exp,omitempty"`
+	Nbf         *int                  `json:"nbf,omitempty"`
+	Iat         *int                  `json:"iat,omitempty"`
+	Aud         *StringOrArray        `json:"aud,omitempty"`
+	Active      *bool                 `json:"active"`
+	AuthTime    *int                  `json:"auth_time,omitempty"`
+	Jti         *string               `json:"jti,omitempty"`
+	Type        *string               `json:"typ,omitempty"`
 }
 
 // User represents the Keycloak User Structure


Test patch:
diff --git a/client_test.go b/client_test.go
index 93ec1b8..e06af73 100644
--- a/client_test.go
+++ b/client_test.go
@@ -522,23 +522,40 @@ func TestGocloak_RequestPermission(t *testing.T) {
 	cfg := GetConfig(t)
 	client := NewClientWithDebug(t)
 	SetUpTestUser(t, client)
-	token, err := client.RequestPermission(
+	token, err := client.Login(
 		cfg.GoCloak.ClientID,
 		cfg.GoCloak.ClientSecret,
 		cfg.GoCloak.Realm,
 		cfg.GoCloak.UserName,
-		cfg.GoCloak.Password,
-		"Permission foo # 3")
-	FailIfErr(t, err, "login failed")
+		cfg.GoCloak.Password)
+	assert.NoError(t, err, "login failed")
+
+	rpt, err := client.GetRequestingPartyToken(token.AccessToken, cfg.GoCloak.Realm, RequestingPartyTokenOptions{
+		Audience: StringP(cfg.GoCloak.ClientID),
+		Permissions: []string{
+			"Fake Resource",
+		},
+	})
+	assert.Error(t, err, "GetRequestingPartyToken failed")
+
+	rpt, err = client.GetRequestingPartyToken(token.AccessToken, cfg.GoCloak.Realm, RequestingPartyTokenOptions{
+		Audience: StringP(cfg.GoCloak.ClientID),
+		Permissions: []string{
+			"Default Resource",
+		},
+	})
+	assert.NoError(t, err, "GetRequestingPartyToken failed")
 
 	rptResult, err := client.RetrospectToken(
-		token.AccessToken,
+		rpt.AccessToken,
 		cfg.GoCloak.ClientID,
 		cfg.GoCloak.ClientSecret,
 		cfg.GoCloak.Realm)
 	t.Log(rptResult)
-	FailIfErr(t, err, "inspection failed")
-	FailIf(t, !PBool(rptResult.Active), "Inactive Token oO")
+	assert.NoError(t, err, "inspection failed")
+	assert.True(t, PBool(rptResult.Active), "Inactive Token oO")
+	assert.Equal(t, 1, len(rptResult.Permissions), "GetRequestingPartyToken failed")
+	assert.Equal(t, "Default Resource", *(rptResult.Permissions[0].RSName), "GetRequestingPartyToken failed")
 }
 
 func TestGocloak_GetCerts(t *testing.T) {
@@ -742,6 +759,9 @@ func TestGocloak_GetRequestingPartyToken(t *testing.T) {
 	)
 	assert.NoError(t, err, "Get requesting party token failed")
 	t.Logf("New RPT: %+v", *rpt)
+
+	_, err = client.RetrospectToken(rpt.AccessToken, cfg.GoCloak.ClientID, cfg.GoCloak.ClientSecret, cfg.GoCloak.Realm)
+	assert.NoError(t, err, "RetrospectToken failed")
 }
 
 func TestGocloak_LoginClient(t *testing.T) {