Skip to content

Commit acb8264

Browse files
committed
change: make swagger docs compatible with access/authorization code flow
Note: It is not yet compatible with authorization code + PKCE. If that becomes necessary see open PR of related package: swaggo/echo-swagger#100
1 parent 4ad574d commit acb8264

File tree

2 files changed

+43
-14
lines changed

2 files changed

+43
-14
lines changed

pkg/swagger/README.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ To use the latest swagger-ui (by using github.com/swaggo/files/v2) we needed to
1212
## Sources used
1313
To compile the new package we took the gin-echo package (https://github.com/swaggo/echo-swagger/blob/master/swagger.go) as a reference and added the gin based functionality.
1414

15+
*Note:* It is not yet compatible with `Authorization Code + PKCE` login flow. If that becomes necessary see open PR of related package: https://github.com/swaggo/echo-swagger/pull/100 as a start.
1516

1617
## Usage
1718

@@ -50,18 +51,25 @@ import "github.com/greenbone/opensight-golang-libraries/pkg/swagger"
5051

5152
## Index
5253

53-
- [Variables](<#variables>)
54-
- [func DeepLinking\(deepLinking bool\) func\(\*Config\)](<#DeepLinking>)
55-
- [func DocExpansion\(docExpansion string\) func\(\*Config\)](<#DocExpansion>)
56-
- [func DomID\(domID string\) func\(\*Config\)](<#DomID>)
57-
- [func GinWrapHandler\(options ...func\(\*Config\)\) gin.HandlerFunc](<#GinWrapHandler>)
58-
- [func InstanceName\(instanceName string\) func\(\*Config\)](<#InstanceName>)
59-
- [func OAuth\(config \*OAuthConfig\) func\(\*Config\)](<#OAuth>)
60-
- [func PersistAuthorization\(persistAuthorization bool\) func\(\*Config\)](<#PersistAuthorization>)
61-
- [func SyntaxHighlight\(syntaxHighlight bool\) func\(\*Config\)](<#SyntaxHighlight>)
62-
- [func URL\(url string\) func\(\*Config\)](<#URL>)
63-
- [type Config](<#Config>)
64-
- [type OAuthConfig](<#OAuthConfig>)
54+
- [ginSwagger](#ginswagger)
55+
- [Reason for implementation](#reason-for-implementation)
56+
- [Sources used](#sources-used)
57+
- [Usage](#usage)
58+
- [License](#license)
59+
- [ginSwagger](#ginswagger-1)
60+
- [Index](#index)
61+
- [Variables](#variables)
62+
- [func DeepLinking](#func-deeplinking)
63+
- [func DocExpansion](#func-docexpansion)
64+
- [func DomID](#func-domid)
65+
- [func GinWrapHandler](#func-ginwraphandler)
66+
- [func InstanceName](#func-instancename)
67+
- [func OAuth](#func-oauth)
68+
- [func PersistAuthorization](#func-persistauthorization)
69+
- [func SyntaxHighlight](#func-syntaxhighlight)
70+
- [func URL](#func-url)
71+
- [type Config](#type-config)
72+
- [type OAuthConfig](#type-oauthconfig)
6573

6674

6775
## Variables

pkg/swagger/ginSwagger.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010
"path/filepath"
1111
"regexp"
12+
"strings"
1213

1314
"github.com/gin-gonic/gin"
1415
swaggerFiles "github.com/swaggo/files/v2"
@@ -26,6 +27,7 @@ type Config struct {
2627
PersistAuthorization bool
2728
SyntaxHighlight bool
2829
OAuth *OAuthConfig
30+
CSPConnectSrc []string
2931
}
3032

3133
type OAuthConfig struct {
@@ -82,6 +84,15 @@ func OAuth(config *OAuthConfig) func(*Config) {
8284
}
8385
}
8486

87+
// CSPConnectSrc adds URLs to the Content Security Policy's connect-src directive.
88+
// This is necessary to allow the Swagger UI to communicate with Keycloak for authentication.
89+
// Note: The connect-src directive expects only scheme://host:port without paths
90+
func CSPConnectSrc(urls ...string) func(*Config) {
91+
return func(c *Config) {
92+
c.CSPConnectSrc = append(c.CSPConnectSrc, urls...)
93+
}
94+
}
95+
8596
func newConfig(configFns ...func(*Config)) *Config {
8697
config := Config{
8798
URLs: []string{"doc.json", "doc.yaml"},
@@ -106,13 +117,23 @@ var WrapHandler = GinWrapHandler()
106117
func GinWrapHandler(options ...func(*Config)) gin.HandlerFunc {
107118
config := newConfig(options...)
108119
index, _ := template.New("swagger_index.html").Parse(indexTemplate)
109-
re := regexp.MustCompile(`^(.*/)([^?].*)?[?|.]*$`)
120+
re := regexp.MustCompile(`^(.*/)([^?]*)?(\?.*)?$`) // e.g. /api/users?sort=name => [ "api/", "users", "?sort=name" ]
110121

111122
return func(c *gin.Context) {
112123
// Set security headers to protect against ClickJacking and XSS
113124
c.Header("X-Frame-Options", "DENY")
114125
c.Header("X-XSS-Protection", "1; mode=block")
115-
c.Header("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; frame-ancestors 'none'")
126+
csp := []string{
127+
"default-src 'self'",
128+
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net",
129+
"frame-ancestors 'none'",
130+
}
131+
if len(config.CSPConnectSrc) > 0 {
132+
sources := append(config.CSPConnectSrc, "'self'")
133+
connectSrc := "connect-src " + strings.Join(sources, " ")
134+
csp = append(csp, connectSrc)
135+
}
136+
c.Header("Content-Security-Policy", strings.Join(csp, "; "))
116137
c.Header("X-Content-Type-Options", "nosniff")
117138

118139
if c.Request.Method != http.MethodGet {

0 commit comments

Comments
 (0)