github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/ias/client.go (about) 1 package ias 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net/http" 10 "strings" 11 12 kebError "github.com/kyma-project/kyma-environment-broker/internal/error" 13 ) 14 15 const ( 16 PathServiceProviders = "/service/sps" 17 PathCompanyGlobal = "/service/company/global" 18 PathAccess = "/service/sps/%s/rba" 19 PathIdentityProviders = "/service/idp" 20 PathDelete = "/service/sps/delete" 21 PathDeleteSecret = "/service/sps/clientSecret" 22 ) 23 24 type ( 25 ClientConfig struct { 26 URL string 27 ID string 28 Secret string 29 } 30 31 Client struct { 32 config ClientConfig 33 httpClient *http.Client 34 closeBodyError error 35 } 36 37 Request struct { 38 Method string 39 Path string 40 Body io.Reader 41 Headers map[string]string 42 Delete bool 43 } 44 ) 45 46 func NewClient(cli *http.Client, cfg ClientConfig) *Client { 47 return &Client{ 48 config: cfg, 49 httpClient: cli, 50 } 51 } 52 53 func (c *Client) SetOIDCConfiguration(spID string, payload OIDCType) error { 54 return c.call(c.serviceProviderPath(spID), payload) 55 } 56 57 func (c *Client) SetSAMLConfiguration(spID string, payload SAMLType) error { 58 return c.call(c.serviceProviderPath(spID), payload) 59 } 60 61 func (c *Client) SetAssertionAttribute(spID string, payload PostAssertionAttributes) error { 62 return c.call(c.serviceProviderPath(spID), payload) 63 } 64 65 func (c *Client) SetSubjectNameIdentifier(spID string, payload SubjectNameIdentifier) error { 66 return c.call(c.serviceProviderPath(spID), payload) 67 } 68 69 func (c *Client) SetAuthenticationAndAccess(spID string, payload AuthenticationAndAccess) error { 70 pathAccess := fmt.Sprintf(PathAccess, spID) 71 72 return c.call(pathAccess, payload) 73 } 74 75 func (c *Client) SetDefaultAuthenticatingIDP(payload DefaultAuthIDPConfig) error { 76 return c.call(PathServiceProviders, payload) 77 } 78 79 func (c *Client) GetCompany() (_ *Company, err error) { 80 company := &Company{} 81 request := &Request{Method: http.MethodGet, Path: PathCompanyGlobal} 82 83 response, err := c.do(request) 84 defer func() { 85 if closeErr := c.closeResponseBody(response); closeErr != nil { 86 err = kebError.AsTemporaryError(closeErr, "while closing response body with company data") 87 } 88 }() 89 if err != nil { 90 return company, fmt.Errorf("while making request to ias platform about company: %w", err) 91 } 92 93 err = json.NewDecoder(response.Body).Decode(company) 94 if err != nil { 95 return company, fmt.Errorf("while decoding response body with company data: %w", err) 96 } 97 98 return company, nil 99 } 100 101 func (c *Client) CreateServiceProvider(serviceName, companyID string) (err error) { 102 payload := fmt.Sprintf("sp_name=%s&company_id=%s", serviceName, companyID) 103 request := &Request{ 104 Method: http.MethodPost, 105 Path: PathServiceProviders, 106 Body: strings.NewReader(payload), 107 Headers: map[string]string{"content-type": "application/x-www-form-urlencoded"}, 108 } 109 110 response, err := c.do(request) 111 defer func() { 112 if closeErr := c.closeResponseBody(response); closeErr != nil { 113 err = kebError.AsTemporaryError(closeErr, "while closing response body for ServiceProvider creation") 114 } 115 }() 116 if err != nil { 117 return fmt.Errorf("while making request with ServiceProvider creation: %w", err) 118 } 119 120 return nil 121 } 122 123 func (c *Client) DeleteServiceProvider(spID string) (err error) { 124 request := &Request{ 125 Method: http.MethodPut, 126 Path: fmt.Sprintf("%s?sp_id=%s", PathDelete, spID), 127 Delete: true, 128 } 129 response, err := c.do(request) 130 defer func() { 131 if closeErr := c.closeResponseBody(response); closeErr != nil { 132 err = kebError.AsTemporaryError(closeErr, "while closing response body for ServiceProvider deletion") 133 } 134 }() 135 if err != nil { 136 return fmt.Errorf("while making request to delete ServiceProvider: %w", err) 137 } 138 139 return nil 140 } 141 142 func (c *Client) DeleteSecret(payload SecretsRef) (err error) { 143 request, err := c.jsonRequest(PathDeleteSecret, http.MethodDelete, payload) 144 if err != nil { 145 return fmt.Errorf("while creating json request for path %s: %w", PathDeleteSecret, err) 146 } 147 request.Delete = true 148 149 response, err := c.do(request) 150 defer func() { 151 if closeErr := c.closeResponseBody(response); closeErr != nil { 152 err = kebError.AsTemporaryError(closeErr, "while closing response body for Secret deletion") 153 } 154 }() 155 if err != nil { 156 return fmt.Errorf("while making request to delete ServiceProvider secrets: %w", err) 157 } 158 159 return nil 160 } 161 162 func (c *Client) GenerateServiceProviderSecret(secretCfg SecretConfiguration) (_ *ServiceProviderSecret, err error) { 163 secretResponse := &ServiceProviderSecret{} 164 request, err := c.jsonRequest(PathServiceProviders, http.MethodPut, secretCfg) 165 if err != nil { 166 return secretResponse, fmt.Errorf("while creating request for secret provider: %w", err) 167 } 168 169 response, err := c.do(request) 170 defer func() { 171 if closeErr := c.closeResponseBody(response); closeErr != nil { 172 err = kebError.AsTemporaryError(closeErr, "while closing response body for ServiceProviderSecret generating") 173 } 174 }() 175 if err != nil { 176 return secretResponse, fmt.Errorf("while making request to generate ServiceProvider secret: %w", err) 177 } 178 179 err = json.NewDecoder(response.Body).Decode(secretResponse) 180 if err != nil { 181 return secretResponse, fmt.Errorf("while decoding response with secret provider: %w", err) 182 } 183 184 return secretResponse, nil 185 } 186 187 func (c Client) AuthenticationURL(id ProviderID) string { 188 return fmt.Sprintf("%s%s/%s", c.config.URL, PathIdentityProviders, id) 189 } 190 191 func (c *Client) serviceProviderPath(spID string) string { 192 return fmt.Sprintf("%s/%s", PathServiceProviders, spID) 193 } 194 195 func (c *Client) call(path string, payload interface{}) (err error) { 196 request, err := c.jsonRequest(path, http.MethodPut, payload) 197 if err != nil { 198 return fmt.Errorf("while creating json request for path %s: %w", path, err) 199 } 200 201 response, err := c.do(request) 202 defer func() { 203 if closeErr := c.closeResponseBody(response); closeErr != nil { 204 err = kebError.AsTemporaryError(closeErr, "while closing response body for call method") 205 } 206 }() 207 if err != nil { 208 return fmt.Errorf("while making request for path %s: %w", path, err) 209 } 210 211 return nil 212 } 213 214 func (c *Client) jsonRequest(path string, method string, payload interface{}) (*Request, error) { 215 buffer := &bytes.Buffer{} 216 encoder := json.NewEncoder(buffer) 217 err := encoder.Encode(payload) 218 if err != nil { 219 return &Request{}, err 220 } 221 222 return &Request{ 223 Method: method, 224 Path: path, 225 Body: buffer, 226 Headers: map[string]string{"content-type": "application/json"}, 227 }, nil 228 } 229 230 func (c *Client) do(sciReq *Request) (*http.Response, error) { 231 url := fmt.Sprintf("%s%s", c.config.URL, sciReq.Path) 232 req, err := http.NewRequest(sciReq.Method, url, sciReq.Body) 233 if err != nil { 234 return nil, err 235 } 236 237 req.Close = true 238 req.SetBasicAuth(c.config.ID, c.config.Secret) 239 for h, v := range sciReq.Headers { 240 req.Header.Set(h, v) 241 } 242 243 response, err := c.httpClient.Do(req) 244 if err != nil { 245 return &http.Response{}, kebError.AsTemporaryError(err, "while making request") 246 } 247 248 switch response.StatusCode { 249 case http.StatusOK, http.StatusCreated, http.StatusNoContent: 250 return response, nil 251 case http.StatusNotFound: 252 if sciReq.Delete { 253 return response, nil 254 } 255 case http.StatusRequestTimeout: 256 return response, kebError.NewTemporaryError(c.responseErrorMessage(response)) 257 } 258 259 if response.StatusCode >= http.StatusInternalServerError { 260 return response, kebError.NewTemporaryError(c.responseErrorMessage(response)) 261 } 262 return response, fmt.Errorf("while sending request to IAS: %s", c.responseErrorMessage(response)) 263 } 264 265 func (c *Client) closeResponseBody(response *http.Response) error { 266 if response == nil { 267 return nil 268 } 269 if response.Body == nil { 270 return nil 271 } 272 return response.Body.Close() 273 } 274 275 func (c *Client) responseErrorMessage(response *http.Response) string { 276 body, err := ioutil.ReadAll(response.Body) 277 if err != nil { 278 return fmt.Sprintf("unexpected status code %d cannot read body response: %s", response.StatusCode, err) 279 } 280 return fmt.Sprintf("unexpected status code %d with body: %s", response.StatusCode, string(body)) 281 }