github.com/Axway/agent-sdk@v1.1.101/pkg/apic/specsecuritybuilder.go (about) 1 package apic 2 3 import ( 4 "fmt" 5 6 "github.com/Axway/agent-sdk/pkg/util" 7 "github.com/getkin/kin-openapi/openapi2" 8 "github.com/getkin/kin-openapi/openapi3" 9 "golang.org/x/text/cases" 10 "golang.org/x/text/language" 11 ) 12 13 const ( 14 oas2 = 2 15 oas3 = 3 16 httpScheme = "http" 17 cookie = "cookie" 18 header = "header" 19 query = "query" 20 implicit = "implicit" 21 authorizationCode = "authorizationCode" 22 clientCredentials = "clientCredentials" 23 password = "password" 24 accessCode = "accessCode" 25 application = "application" 26 ) 27 28 // used by oas spec parsers to start the builder 29 func newSpecSecurityBuilder(oasMajorVersion int) SecurityBuilder { 30 return &specSecurity{ 31 oasMajorVersion: oasMajorVersion, 32 } 33 } 34 35 // first select the type of security we are building 36 type SecurityBuilder interface { 37 HTTPBasic() HTTPBasicSecurityBuilder 38 APIKey() APIKeySecurityBuilder 39 OAuth() OAuthSecurityBuilder 40 Bearer() BearerSecurityBuilder 41 OpenID() OpenIDSecurityBuilder 42 } 43 44 type specSecurity struct { 45 oasMajorVersion int 46 } 47 48 func (s *specSecurity) HTTPBasic() HTTPBasicSecurityBuilder { 49 return &httpBasicSecurity{ 50 specSecurity: s, 51 } 52 } 53 54 type HTTPBasicSecurityBuilder interface { 55 Build() map[string]interface{} 56 } 57 58 type httpBasicSecurity struct { 59 *specSecurity 60 } 61 62 // create http basic scheme 63 func (s *httpBasicSecurity) Build() map[string]interface{} { 64 const ( 65 name = "basicAuth" 66 basic = "basic" 67 ) 68 69 if s.oasMajorVersion == 2 { 70 return map[string]interface{}{ 71 name: &openapi2.SecurityScheme{ 72 Type: basic, 73 }, 74 } 75 } 76 return map[string]interface{}{ 77 name: &openapi3.SecurityScheme{ 78 Type: httpScheme, 79 Scheme: basic, 80 }, 81 } 82 } 83 84 func (s *specSecurity) APIKey() APIKeySecurityBuilder { 85 return &apiKeySecurity{ 86 specSecurity: s, 87 locations: []string{}, 88 } 89 } 90 91 type APIKeySecurityBuilder interface { 92 Build() map[string]interface{} 93 InCookie() APIKeySecurityBuilder // quests are the same for cookie vs api key in query or header 94 InHeader() APIKeySecurityBuilder 95 InQueryParam() APIKeySecurityBuilder 96 SetArgumentName(argName string) APIKeySecurityBuilder 97 } 98 99 type apiKeySecurity struct { 100 *specSecurity 101 locations []string 102 argName string 103 } 104 105 func (s *apiKeySecurity) InCookie() APIKeySecurityBuilder { 106 s.locations = append(s.locations, cookie) 107 return s 108 } 109 110 func (s *apiKeySecurity) InHeader() APIKeySecurityBuilder { 111 s.locations = append(s.locations, header) 112 return s 113 } 114 115 func (s *apiKeySecurity) InQueryParam() APIKeySecurityBuilder { 116 s.locations = append(s.locations, query) 117 return s 118 } 119 120 func (s *apiKeySecurity) SetArgumentName(argName string) APIKeySecurityBuilder { 121 s.argName = argName 122 return s 123 } 124 125 // create api key security type 126 func (s *apiKeySecurity) Build() map[string]interface{} { 127 const apiKey = "apiKey" 128 129 output := map[string]interface{}{} 130 131 for _, location := range s.locations { 132 name := fmt.Sprintf("%v%v", apiKey, cases.Title(language.English).String(location)) 133 134 if s.oasMajorVersion == 2 { 135 if location == cookie { 136 // only supported on oas3, return empty for oas2 137 continue 138 } 139 140 output[name] = &openapi2.SecurityScheme{ 141 Name: s.argName, 142 In: location, 143 Type: apiKey, 144 } 145 continue 146 } 147 148 output[name] = &openapi3.SecurityScheme{ 149 Name: s.argName, 150 In: location, 151 Type: apiKey, 152 } 153 } 154 155 return output 156 } 157 158 type oAuthFlow struct { 159 flow string 160 authURL string 161 tokenURL string 162 refreshURL string 163 scopes map[string]string 164 } 165 166 func NewOAuthFlowBuilder() OAuthFlowBuilder { 167 return &oAuthFlow{} 168 } 169 170 // oauth flow options, setting flow type should be last, not all other methods are required 171 type OAuthFlowBuilder interface { 172 SetScopes(map[string]string) OAuthFlowBuilder 173 AddScope(scope, description string) OAuthFlowBuilder 174 SetAuthorizationURL(url string) OAuthFlowBuilder 175 SetRefreshURL(url string) OAuthFlowBuilder 176 SetTokenURL(url string) OAuthFlowBuilder 177 Implicit() *oAuthFlow 178 Password() *oAuthFlow 179 AuthorizationCode() *oAuthFlow 180 ClientCredentials() *oAuthFlow 181 } 182 183 func (s *oAuthFlow) SetScopes(scopes map[string]string) OAuthFlowBuilder { 184 s.scopes = scopes 185 return s 186 } 187 188 func (s *oAuthFlow) AddScope(scope, description string) OAuthFlowBuilder { 189 s.scopes[scope] = description 190 return s 191 } 192 193 func (s *oAuthFlow) SetTokenURL(url string) OAuthFlowBuilder { 194 s.tokenURL = url 195 return s 196 } 197 198 func (s *oAuthFlow) SetAuthorizationURL(url string) OAuthFlowBuilder { 199 s.authURL = url 200 return s 201 } 202 203 func (s *oAuthFlow) SetRefreshURL(url string) OAuthFlowBuilder { 204 s.refreshURL = url 205 return s 206 } 207 208 func (s *oAuthFlow) Implicit() *oAuthFlow { 209 s.flow = implicit 210 return s 211 } 212 213 func (s *oAuthFlow) Password() *oAuthFlow { 214 s.flow = password 215 return s 216 } 217 218 func (s *oAuthFlow) AuthorizationCode() *oAuthFlow { 219 s.flow = authorizationCode 220 return s 221 } 222 223 func (s *oAuthFlow) ClientCredentials() *oAuthFlow { 224 s.flow = clientCredentials 225 return s 226 } 227 228 type OAuthSecurityBuilder interface { 229 AddFlow(flow *oAuthFlow) OAuthSecurityBuilder 230 Build() map[string]interface{} 231 } 232 233 type oAuthSecurity struct { 234 *specSecurity 235 flows []*oAuthFlow 236 } 237 238 func (s *specSecurity) OAuth() OAuthSecurityBuilder { 239 return &oAuthSecurity{ 240 specSecurity: s, 241 flows: []*oAuthFlow{}, 242 } 243 } 244 245 func (s *oAuthSecurity) AddFlow(flow *oAuthFlow) OAuthSecurityBuilder { 246 s.flows = append(s.flows, flow) 247 return s 248 } 249 250 func (s *oAuthSecurity) Build() map[string]interface{} { 251 const oauth2 = "oauth2" 252 253 if s.oasMajorVersion == 2 { 254 // create separate scheme for each flow type 255 oauthFlows := map[string]interface{}{} 256 for _, f := range s.flows { 257 // adjust the name of the flow for oas2 support 258 if f.flow == authorizationCode { 259 f.flow = accessCode 260 } else if f.flow == clientCredentials { 261 f.flow = application 262 } 263 264 fName := fmt.Sprintf("%v%v", oauth2, cases.Title(language.English).String(f.flow)) 265 oauthFlows[fName] = &openapi2.SecurityScheme{ 266 Type: oauth2, 267 Flow: f.flow, 268 Scopes: util.OrderStringsInMap(f.scopes), 269 AuthorizationURL: f.authURL, 270 TokenURL: f.tokenURL, 271 } 272 } 273 return util.OrderStringsInMap(oauthFlows) 274 } 275 276 // create single scheme with all flows 277 oauthFlows := &openapi3.OAuthFlows{} 278 for _, f := range s.flows { 279 oFlow := &openapi3.OAuthFlow{ 280 AuthorizationURL: f.authURL, 281 RefreshURL: f.refreshURL, 282 TokenURL: f.tokenURL, 283 Scopes: util.OrderStringsInMap(f.scopes), 284 } 285 switch f.flow { 286 case authorizationCode: 287 oauthFlows.AuthorizationCode = oFlow 288 case password: 289 oauthFlows.Password = oFlow 290 case clientCredentials: 291 oauthFlows.ClientCredentials = oFlow 292 case implicit: 293 oauthFlows.Implicit = oFlow 294 } 295 } 296 return map[string]interface{}{ 297 oauth2: &openapi3.SecurityScheme{ 298 Type: oauth2, 299 Flows: oauthFlows, 300 }, 301 } 302 } 303 304 func (s *specSecurity) Bearer() BearerSecurityBuilder { 305 return &bearerSecurity{ 306 specSecurity: s, 307 } 308 } 309 310 type BearerSecurityBuilder interface { 311 Build() map[string]interface{} 312 SetFormat(format string) BearerSecurityBuilder 313 } 314 315 type bearerSecurity struct { 316 *specSecurity 317 format string 318 } 319 320 func (s *bearerSecurity) SetFormat(format string) BearerSecurityBuilder { 321 s.format = format 322 return s 323 } 324 325 func (s *bearerSecurity) Build() map[string]interface{} { 326 const ( 327 name = "bearerAuth" 328 bearer = "bearer" 329 ) 330 331 if s.oasMajorVersion == 2 { 332 // only supported on oas3, return empty for oas2 333 return map[string]interface{}{} 334 } 335 return map[string]interface{}{ 336 name: &openapi3.SecurityScheme{ 337 Type: httpScheme, 338 Scheme: bearer, 339 BearerFormat: s.format, 340 }, 341 } 342 } 343 344 func (s *specSecurity) OpenID() OpenIDSecurityBuilder { 345 return &openIDSecurity{ 346 specSecurity: s, 347 } 348 } 349 350 type OpenIDSecurityBuilder interface { 351 Build() map[string]interface{} 352 SetURL(url string) OpenIDSecurityBuilder 353 } 354 355 type openIDSecurity struct { 356 *specSecurity 357 url string 358 } 359 360 func (s *openIDSecurity) SetURL(url string) OpenIDSecurityBuilder { 361 s.url = url 362 return s 363 } 364 365 func (s *openIDSecurity) Build() map[string]interface{} { 366 const ( 367 name = "openId" 368 openIdConnect = "openIdConnect" 369 ) 370 371 if s.oasMajorVersion == 2 { 372 // only supported on oas3, return empty for oas2 373 return map[string]interface{}{} 374 } 375 return map[string]interface{}{ 376 name: &openapi3.SecurityScheme{ 377 Type: openIdConnect, 378 OpenIdConnectUrl: s.url, 379 }, 380 } 381 }