From c934bdfb2737cfab5c6960d91111c893d4d6e625 Mon Sep 17 00:00:00 2001 From: Oleg Kashin Date: Wed, 5 Jul 2023 13:23:00 +0300 Subject: [PATCH 1/2] global security --- operation.go | 5 +++ parser.go | 31 ++++++++++++++++++ parser_test.go | 15 +++++++++ testdata/global_security/api/api.go | 22 +++++++++++++ testdata/global_security/expected.json | 44 ++++++++++++++++++++++++++ testdata/global_security/main.go | 20 ++++++++++++ 6 files changed, 137 insertions(+) create mode 100644 testdata/global_security/api/api.go create mode 100644 testdata/global_security/expected.json create mode 100644 testdata/global_security/main.go diff --git a/operation.go b/operation.go index 3485f51f4..9f5bcc990 100644 --- a/operation.go +++ b/operation.go @@ -715,6 +715,11 @@ func (operation *Operation) ParseRouterComment(commentLine string) error { // ParseSecurityComment parses comment for given `security` comment string. func (operation *Operation) ParseSecurityComment(commentLine string) error { + if len(commentLine) == 0 { + operation.Security = []map[string][]string{} + return nil + } + var ( securityMap = make(map[string][]string) securitySource = commentLine[strings.Index(commentLine, "@Security")+1:] diff --git a/parser.go b/parser.go index 2e453960a..0398c76b2 100644 --- a/parser.go +++ b/parser.go @@ -569,6 +569,9 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { parser.swagger.SecurityDefinitions[value] = scheme + case securityAttr: + parser.swagger.Security = append(parser.swagger.Security, parseSecurity(value)) + case "@query.collection.format": parser.collectionFormatInQuery = TransToValidCollectionFormat(value) @@ -768,6 +771,34 @@ func parseSecAttributes(context string, lines []string, index *int) (*spec.Secur return scheme, nil } +func parseSecurity(commentLine string) map[string][]string { + securityMap := make(map[string][]string) + + for _, securityOption := range strings.Split(commentLine, "||") { + securityOption = strings.TrimSpace(securityOption) + + left, right := strings.Index(securityOption, "["), strings.Index(securityOption, "]") + + if !(left == -1 && right == -1) { + scopes := securityOption[left+1 : right] + + var options []string + + for _, scope := range strings.Split(scopes, ",") { + options = append(options, strings.TrimSpace(scope)) + } + + securityKey := securityOption[0:left] + securityMap[securityKey] = append(securityMap[securityKey], options...) + } else { + securityKey := strings.TrimSpace(securityOption) + securityMap[securityKey] = []string{} + } + } + + return securityMap +} + func initIfEmpty(license *spec.License) *spec.License { if license == nil { return new(spec.License) diff --git a/parser_test.go b/parser_test.go index 71dc90da4..6a9ebd2ac 100644 --- a/parser_test.go +++ b/parser_test.go @@ -2157,6 +2157,21 @@ func TestParseTypeOverrides(t *testing.T) { assert.Equal(t, string(expected), string(b)) } +func TestGlobalSecurity(t *testing.T) { + t.Parallel() + + searchDir := "testdata/global_security" + p := New() + err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) + assert.NoError(t, err) + + expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json")) + assert.NoError(t, err) + + b, _ := json.MarshalIndent(p.swagger, "", " ") + assert.Equal(t, string(expected), string(b)) +} + func TestParseNested(t *testing.T) { t.Parallel() diff --git a/testdata/global_security/api/api.go b/testdata/global_security/api/api.go new file mode 100644 index 000000000..9581f52d6 --- /dev/null +++ b/testdata/global_security/api/api.go @@ -0,0 +1,22 @@ +package api + +import ( + "net/http" +) + +// @Summary Get application +// @Description test get application +// @Success 200 +// @Router /testapi/application [get] +func GetApplication(w http.ResponseWriter, r *http.Request) { + //write your code +} + +// Summary Get no security +// @Description override global security +// @Security +// @Success 200 +// @Router /testapi/nosec [get] +func GetNoSec(w http.ResponseWriter, r *http.Request) { + //write your code +} diff --git a/testdata/global_security/expected.json b/testdata/global_security/expected.json new file mode 100644 index 000000000..2f612ad12 --- /dev/null +++ b/testdata/global_security/expected.json @@ -0,0 +1,44 @@ +{ + "swagger": "2.0", + "info": { + "title": "Swagger Example API", + "contact": {}, + "version": "1.0" + }, + "paths": { + "/testapi/application": { + "get": { + "description": "test get application", + "summary": "Get application", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/testapi/nosec": { + "get": { + "security": [], + "description": "override global security", + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "securityDefinitions": { + "APIKeyAuth": { + "type": "apiKey", + "name": "Authorization", + "in": "header" + } + }, + "security": [ + { + "APIKeyAuth": [] + } + ] +} \ No newline at end of file diff --git a/testdata/global_security/main.go b/testdata/global_security/main.go new file mode 100644 index 000000000..ee38ee3a7 --- /dev/null +++ b/testdata/global_security/main.go @@ -0,0 +1,20 @@ +package global_security + +import ( + "net/http" + + "github.com/swaggo/swag/testdata/global_security/api" +) + +// @title Swagger Example API +// @version 1.0 + +// @securityDefinitions.apikey APIKeyAuth +// @in header +// @name Authorization + +// @security APIKeyAuth +func main() { + http.HandleFunc("/testapi/application", api.GetApplication) + http.ListenAndServe(":8080", nil) +} From f28882cb67adef9273030ffe5024043e9b53e2a0 Mon Sep 17 00:00:00 2001 From: Oleg Kashin Date: Wed, 5 Jul 2023 13:56:09 +0300 Subject: [PATCH 2/2] improve test --- testdata/global_security/api/api.go | 32 ++++++++---- testdata/global_security/expected.json | 69 ++++++++++++++++++++++++-- testdata/global_security/main.go | 20 ++++---- 3 files changed, 96 insertions(+), 25 deletions(-) diff --git a/testdata/global_security/api/api.go b/testdata/global_security/api/api.go index 9581f52d6..8b8741c57 100644 --- a/testdata/global_security/api/api.go +++ b/testdata/global_security/api/api.go @@ -4,19 +4,31 @@ import ( "net/http" ) -// @Summary Get application -// @Description test get application +// @Summary default security // @Success 200 // @Router /testapi/application [get] -func GetApplication(w http.ResponseWriter, r *http.Request) { - //write your code -} +func GetApplication(w http.ResponseWriter, r *http.Request) {} -// Summary Get no security -// @Description override global security +// @Summary no security // @Security // @Success 200 // @Router /testapi/nosec [get] -func GetNoSec(w http.ResponseWriter, r *http.Request) { - //write your code -} +func GetNoSec(w http.ResponseWriter, r *http.Request) {} + +// @Summary basic security +// @Security BasicAuth +// @Success 200 +// @Router /testapi/basic [get] +func GetBasic(w http.ResponseWriter, r *http.Request) {} + +// @Summary oauth2 write +// @Security OAuth2Application[write] +// @Success 200 +// @Router /testapi/oauth/write [get] +func GetOAuthWrite(w http.ResponseWriter, r *http.Request) {} + +// @Summary oauth2 admin +// @Security OAuth2Application[admin] +// @Success 200 +// @Router /testapi/oauth/admin [get] +func GetOAuthAdmin(w http.ResponseWriter, r *http.Request) {} diff --git a/testdata/global_security/expected.json b/testdata/global_security/expected.json index 2f612ad12..7505df733 100644 --- a/testdata/global_security/expected.json +++ b/testdata/global_security/expected.json @@ -8,8 +8,22 @@ "paths": { "/testapi/application": { "get": { - "description": "test get application", - "summary": "Get application", + "summary": "default security", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/testapi/basic": { + "get": { + "security": [ + { + "BasicAuth": [] + } + ], + "summary": "basic security", "responses": { "200": { "description": "OK" @@ -20,7 +34,41 @@ "/testapi/nosec": { "get": { "security": [], - "description": "override global security", + "summary": "no security", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/testapi/oauth/admin": { + "get": { + "security": [ + { + "OAuth2Application": [ + "admin" + ] + } + ], + "summary": "oauth2 admin", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/testapi/oauth/write": { + "get": { + "security": [ + { + "OAuth2Application": [ + "write" + ] + } + ], + "summary": "oauth2 write", "responses": { "200": { "description": "OK" @@ -34,11 +82,24 @@ "type": "apiKey", "name": "Authorization", "in": "header" + }, + "BasicAuth": { + "type": "basic" + }, + "OAuth2Application": { + "type": "oauth2", + "flow": "application", + "tokenUrl": "https://example.com/oauth/token", + "scopes": { + "admin": " Grants read and write access to administrative information", + "write": " Grants write access" + } } }, "security": [ { - "APIKeyAuth": [] + "APIKeyAuth": [], + "OAuth2Application": [] } ] } \ No newline at end of file diff --git a/testdata/global_security/main.go b/testdata/global_security/main.go index ee38ee3a7..83484bac8 100644 --- a/testdata/global_security/main.go +++ b/testdata/global_security/main.go @@ -1,11 +1,5 @@ package global_security -import ( - "net/http" - - "github.com/swaggo/swag/testdata/global_security/api" -) - // @title Swagger Example API // @version 1.0 @@ -13,8 +7,12 @@ import ( // @in header // @name Authorization -// @security APIKeyAuth -func main() { - http.HandleFunc("/testapi/application", api.GetApplication) - http.ListenAndServe(":8080", nil) -} +// @securityDefinitions.basic BasicAuth + +// @securityDefinitions.oauth2.application OAuth2Application +// @tokenUrl https://example.com/oauth/token +// @scope.write Grants write access +// @scope.admin Grants read and write access to administrative information + +// @security APIKeyAuth || OAuth2Application +func main() {}