diff --git a/client/client.go b/client/client.go index 92d12de..44b205e 100644 --- a/client/client.go +++ b/client/client.go @@ -132,6 +132,15 @@ func (c *ThreeScaleClient) buildUpdateJSONReq(ep string, body io.Reader) (*http. return req, err } +// Request builder for PATCH request to the provided endpoint with json content type +func (c *ThreeScaleClient) buildPatchJSONReq(ep string, body io.Reader) (*http.Request, error) { + req, err := http.NewRequest("PATCH", c.adminPortal.rawURL+ep, body) + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+basicAuth("", c.credential)) + return req, err +} + // Request builder for DELETE request to the provided endpoint func (c *ThreeScaleClient) buildDeleteReq(ep string, body io.Reader) (*http.Request, error) { req, err := http.NewRequest("DELETE", c.adminPortal.rawURL+ep, body) diff --git a/client/oidc.go b/client/oidc.go new file mode 100644 index 0000000..ea50e62 --- /dev/null +++ b/client/oidc.go @@ -0,0 +1,56 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" +) + +const ( + oidcResourceEndpoint = "/admin/api/services/%d/proxy/oidc_configuration.json" +) + +// OIDCConfiguration fetches 3scale product oidc configuration +func (c *ThreeScaleClient) OIDCConfiguration(productID int64) (*OIDCConfiguration, error) { + endpoint := fmt.Sprintf(oidcResourceEndpoint, productID) + req, err := c.buildGetReq(endpoint) + if err != nil { + return nil, err + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + oidcConf := &OIDCConfiguration{} + err = handleJsonResp(resp, http.StatusOK, oidcConf) + return oidcConf, err +} + +// UpdateOIDCConfiguration Update 3scale product oidc configuration +func (c *ThreeScaleClient) UpdateOIDCConfiguration(productID int64, oidcConf *OIDCConfiguration) (*OIDCConfiguration, error) { + endpoint := fmt.Sprintf(oidcResourceEndpoint, productID) + + bodyArr, err := json.Marshal(oidcConf) + if err != nil { + return nil, err + } + body := bytes.NewReader(bodyArr) + req, err := c.buildPatchJSONReq(endpoint, body) + if err != nil { + return nil, err + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + newConf := &OIDCConfiguration{} + err = handleJsonResp(resp, http.StatusOK, newConf) + return newConf, err +} diff --git a/client/oidc_test.go b/client/oidc_test.go new file mode 100644 index 0000000..89841a5 --- /dev/null +++ b/client/oidc_test.go @@ -0,0 +1,112 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "testing" +) + +func TestOIDCConfiguration(t *testing.T) { + var ( + productID int64 = 97 + endpoint = fmt.Sprintf(oidcResourceEndpoint, productID) + oidcConf = &OIDCConfiguration{ + Element: OIDCConfigurationItem{ + StandardFlowEnabled: false, + ImplicitFlowEnabled: true, + ServiceAccountsEnabled: false, + DirectAccessGrantsEnabled: true, + }, + } + ) + + httpClient := NewTestClient(func(req *http.Request) *http.Response { + if req.URL.Path != endpoint { + t.Fatalf("Path does not match. Expected [%s]; got [%s]", endpoint, req.URL.Path) + } + + if req.Method != http.MethodGet { + t.Fatalf("Method does not match. Expected [%s]; got [%s]", http.MethodGet, req.Method) + } + + responseBodyBytes, err := json.Marshal(oidcConf) + if err != nil { + t.Fatal(err) + } + + return &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewBuffer(responseBodyBytes)), + Header: make(http.Header), + } + }) + + credential := "someAccessToken" + c := NewThreeScale(NewTestAdminPortal(t), credential, httpClient) + obj, err := c.OIDCConfiguration(productID) + if err != nil { + t.Fatal(err) + } + + if obj == nil { + t.Fatal("response was nil") + } + + if *obj != *oidcConf { + t.Fatalf("Expected %v; got %v", *oidcConf, *obj) + } +} + +func TestUpdateOIDCConfiguration(t *testing.T) { + var ( + productID int64 = 98765 + endpoint = fmt.Sprintf(oidcResourceEndpoint, productID) + oidcConf = &OIDCConfiguration{ + Element: OIDCConfigurationItem{ + StandardFlowEnabled: false, + ImplicitFlowEnabled: true, + ServiceAccountsEnabled: false, + DirectAccessGrantsEnabled: true, + }, + } + ) + + httpClient := NewTestClient(func(req *http.Request) *http.Response { + if req.URL.Path != endpoint { + t.Fatalf("Path does not match. Expected [%s]; got [%s]", endpoint, req.URL.Path) + } + + if req.Method != http.MethodPatch { + t.Fatalf("Method does not match. Expected [%s]; got [%s]", http.MethodPatch, req.Method) + } + + responseBodyBytes, err := json.Marshal(*oidcConf) + if err != nil { + t.Fatal(err) + } + + return &http.Response{ + StatusCode: http.StatusOK, + Body: ioutil.NopCloser(bytes.NewBuffer(responseBodyBytes)), + Header: make(http.Header), + } + }) + + credential := "someAccessToken" + c := NewThreeScale(NewTestAdminPortal(t), credential, httpClient) + obj, err := c.UpdateOIDCConfiguration(productID, oidcConf) + if err != nil { + t.Fatal(err) + } + + if obj == nil { + t.Fatal("resp returned nil") + } + + if *obj != *oidcConf { + t.Fatalf("Expected %v; got %v", *oidcConf, *obj) + } +} diff --git a/client/policies.go b/client/policies.go index fb39300..5da65ec 100644 --- a/client/policies.go +++ b/client/policies.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "net/url" ) const ( @@ -20,9 +19,6 @@ func (c *ThreeScaleClient) Policies(productID int64) (*PoliciesConfigList, error return nil, err } - urlValues := url.Values{} - req.URL.RawQuery = urlValues.Encode() - resp, err := c.httpClient.Do(req) if err != nil { return nil, err diff --git a/client/types.go b/client/types.go index 436ee3b..5cbb282 100644 --- a/client/types.go +++ b/client/types.go @@ -635,3 +635,17 @@ type PolicyConfig struct { type PoliciesConfigList struct { Policies []PolicyConfig `json:"policies_config"` } + +// OIDCConfigurationItem - Holds an OIDC configuration item object +type OIDCConfigurationItem struct { + ID int64 `json:"id,omitempty"` + StandardFlowEnabled bool `json:"standard_flow_enabled"` + ImplicitFlowEnabled bool `json:"implicit_flow_enabled"` + ServiceAccountsEnabled bool `json:"service_accounts_enabled"` + DirectAccessGrantsEnabled bool `json:"direct_access_grants_enabled"` +} + +// OIDCConfiguration - Holds an OIDC configuration object +type OIDCConfiguration struct { + Element OIDCConfigurationItem `json:"oidc_configuration"` +}