Skip to content

Commit

Permalink
Allow custom response structures and statuses in security middlewares (
Browse files Browse the repository at this point in the history
  • Loading branch information
vearutop authored Feb 20, 2021
1 parent 1beaa1a commit 88927c7
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 33 deletions.
4 changes: 2 additions & 2 deletions _examples/advanced/_testdata/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
},
"/gzip-pass-through":{
"get":{
"summary":"directGzip","description":"","operationId":"_examples/advanced.directGzip",
"summary":"direct Gzip","description":"","operationId":"_examples/advanced.directGzip",
"parameters":[
{
"name":"plainStruct","in":"query","description":"Output plain structure instead of gzip container.",
Expand All @@ -58,7 +58,7 @@
}
},
"head":{
"summary":"directGzip","description":"","operationId":"_examples/advanced.directGzip",
"summary":"direct Gzip","description":"","operationId":"_examples/advanced.directGzip",
"parameters":[
{
"name":"plainStruct","in":"query","description":"Output plain structure instead of gzip container.",
Expand Down
8 changes: 4 additions & 4 deletions _examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ go 1.13
replace github.com/swaggest/rest => ../

require (
github.com/bool64/dev v0.1.18
github.com/bool64/dev v0.1.19
github.com/bool64/httptestbench v0.1.1
github.com/go-chi/chi v1.5.2
github.com/kelseyhightower/envconfig v1.4.0
github.com/stretchr/testify v1.7.0
github.com/swaggest/assertjson v1.6.3
github.com/swaggest/jsonschema-go v0.3.15
github.com/swaggest/jsonschema-go v0.3.17
github.com/swaggest/openapi-go v0.2.9
github.com/swaggest/rest v0.1.17
github.com/swaggest/swgui v1.2.0
github.com/swaggest/usecase v0.1.2
github.com/swaggest/usecase v0.1.3
github.com/valyala/fasthttp v1.17.0
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d // indirect
)
15 changes: 8 additions & 7 deletions _examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ github.com/bool64/dev v0.1.10/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbs
github.com/bool64/dev v0.1.12/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbscdg=
github.com/bool64/dev v0.1.15/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
github.com/bool64/dev v0.1.17/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
github.com/bool64/dev v0.1.18 h1:uMN5MsHrVWmtRoauefI8wD86b8vbXYnrCZlIhFGyuXI=
github.com/bool64/dev v0.1.18/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
github.com/bool64/dev v0.1.19 h1:SLAOGmJIrbroYjkGuV0MBLtww2NddzPDne1C7ATJdJk=
github.com/bool64/dev v0.1.19/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
github.com/bool64/httptestbench v0.1.1 h1:08112fXc5F1XuTWLdC9lPGB4FSWNACoeUWTeoR0a2qA=
github.com/bool64/httptestbench v0.1.1/go.mod h1:gSmfFacu85Je3u6ZjmLZsFolcv7cwg1fLNUCzq2UUsM=
github.com/bool64/shared v0.1.0/go.mod h1:khzw051JtnOZH6lX21rQ+sqbeC0FFEcU1wfchRS7EyU=
Expand Down Expand Up @@ -126,8 +127,8 @@ github.com/swaggest/jsonschema-go v0.2.1/go.mod h1:QFauBdPTrU1UltwocM5FzOWnVjVVt
github.com/swaggest/jsonschema-go v0.2.4/go.mod h1:m4VV88Gbi7lCrt9ckJzigK1rMlEeFjdZUkJr1o5MnDE=
github.com/swaggest/jsonschema-go v0.3.7/go.mod h1:TrWgbug4p2ZgcxnHDz+CvYvEtJ5KckL/XOV4mSR6FGw=
github.com/swaggest/jsonschema-go v0.3.13/go.mod h1:INO3Dt0DeyNCbQbqMXNpMQ489BcyRtSb/k62OfmHbpY=
github.com/swaggest/jsonschema-go v0.3.15 h1:6D23R4rhU2fVELiMyXtssVxlS/8eq6krRzemv07VvK8=
github.com/swaggest/jsonschema-go v0.3.15/go.mod h1:1JyQxHbtZcK3WqGqRybu022vrDdI02j9Xu9sK/Z58Tg=
github.com/swaggest/jsonschema-go v0.3.17 h1:9BjsqEq3nIipXJbbN8+XNMsAu2lxbUVeYqI+l8DWtAA=
github.com/swaggest/jsonschema-go v0.3.17/go.mod h1:VrFw5hc9rCdtWJPhmfJi8UuSzpDJY+CAPn8QJ0SlxM0=
github.com/swaggest/openapi-go v0.1.3/go.mod h1:Zx4ZgJ7XvlFH9wCOHE7u8RAjLfiHAnCHeaD5kUDujVM=
github.com/swaggest/openapi-go v0.1.13/go.mod h1:fju3Ka5zb8qBKf4789zLNVUqITydWLDjtPloZhhYHL8=
github.com/swaggest/openapi-go v0.2.9 h1:JJQrDSRtKMycIkLju97yjhJbw72mqhsCDJjqFB8HvSs=
Expand All @@ -144,8 +145,8 @@ github.com/swaggest/swgen v0.6.27 h1:bO3CtX4NvuetPuABMChEEp2N/bjsJraegaJpYZ72aoI
github.com/swaggest/swgen v0.6.27/go.mod h1:tQLsoQVLcvtAMzNbm26JbWXDHZM4BBRuI5nemk3gEDw=
github.com/swaggest/swgui v1.2.0 h1:zZWybwcfeWUNgOJOa+/FkLtYi7xI7pvGXnJrPcwh8vY=
github.com/swaggest/swgui v1.2.0/go.mod h1:gn/xO+5S+BDFhtZP05gPiB/P6cXurNPv6kAeDJYgT98=
github.com/swaggest/usecase v0.1.2 h1:Fi3z0nig4gBcX0n0nnEHD8QzCttdV3lAPmaoX3nQowA=
github.com/swaggest/usecase v0.1.2/go.mod h1:BKvpHx94bpg+4RCXbrCv0jx8CpJRr5P8dYK0b4UfUZc=
github.com/swaggest/usecase v0.1.3 h1:rK/t7PR3pt6pDEVQuT9n/aXHbMtKNzulqniQ/GSOiCc=
github.com/swaggest/usecase v0.1.3/go.mod h1:d94PwelVQeMv4RH0VsRMFqRNrk6ferEqg2AetDcjPWo=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.17.0 h1:P8/koH4aSnJ4xbd0cUUFEGQs3jQqIxoDDyRQrUiAkqg=
Expand Down Expand Up @@ -181,8 +182,8 @@ golang.org/x/net v0.0.0-20201016165138-7b1cca2348c0/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/swaggest/rest
go 1.13

require (
github.com/bool64/dev v0.1.18
github.com/bool64/dev v0.1.19
github.com/bool64/shared v0.1.2
github.com/cespare/xxhash/v2 v2.1.1
github.com/go-chi/chi v1.5.2
Expand All @@ -12,8 +12,8 @@ require (
github.com/stretchr/testify v1.7.0
github.com/swaggest/assertjson v1.6.3
github.com/swaggest/form v1.11.1
github.com/swaggest/jsonschema-go v0.3.15
github.com/swaggest/jsonschema-go v0.3.17
github.com/swaggest/openapi-go v0.2.9
github.com/swaggest/refl v0.1.7
github.com/swaggest/usecase v0.1.2
github.com/swaggest/usecase v0.1.3
)
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ github.com/bool64/dev v0.1.10/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbs
github.com/bool64/dev v0.1.12/go.mod h1:pn52JC52uSgpazChx9CeXyG+S3sW2V36HHoLNBbscdg=
github.com/bool64/dev v0.1.15/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
github.com/bool64/dev v0.1.17/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
github.com/bool64/dev v0.1.18 h1:uMN5MsHrVWmtRoauefI8wD86b8vbXYnrCZlIhFGyuXI=
github.com/bool64/dev v0.1.18/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
github.com/bool64/dev v0.1.19 h1:SLAOGmJIrbroYjkGuV0MBLtww2NddzPDne1C7ATJdJk=
github.com/bool64/dev v0.1.19/go.mod h1:cTHiTDNc8EewrQPy3p1obNilpMpdmlUesDkFTF2zRWU=
github.com/bool64/shared v0.1.0/go.mod h1:khzw051JtnOZH6lX21rQ+sqbeC0FFEcU1wfchRS7EyU=
github.com/bool64/shared v0.1.1/go.mod h1:khzw051JtnOZH6lX21rQ+sqbeC0FFEcU1wfchRS7EyU=
github.com/bool64/shared v0.1.2 h1:3W41/TM41O4dMZRZbKErzMcSZQuDG4UnkoWZWaQnTTo=
Expand Down Expand Up @@ -112,8 +112,8 @@ github.com/swaggest/jsonschema-go v0.2.1/go.mod h1:QFauBdPTrU1UltwocM5FzOWnVjVVt
github.com/swaggest/jsonschema-go v0.2.4/go.mod h1:m4VV88Gbi7lCrt9ckJzigK1rMlEeFjdZUkJr1o5MnDE=
github.com/swaggest/jsonschema-go v0.3.7/go.mod h1:TrWgbug4p2ZgcxnHDz+CvYvEtJ5KckL/XOV4mSR6FGw=
github.com/swaggest/jsonschema-go v0.3.13/go.mod h1:INO3Dt0DeyNCbQbqMXNpMQ489BcyRtSb/k62OfmHbpY=
github.com/swaggest/jsonschema-go v0.3.15 h1:6D23R4rhU2fVELiMyXtssVxlS/8eq6krRzemv07VvK8=
github.com/swaggest/jsonschema-go v0.3.15/go.mod h1:1JyQxHbtZcK3WqGqRybu022vrDdI02j9Xu9sK/Z58Tg=
github.com/swaggest/jsonschema-go v0.3.17 h1:9BjsqEq3nIipXJbbN8+XNMsAu2lxbUVeYqI+l8DWtAA=
github.com/swaggest/jsonschema-go v0.3.17/go.mod h1:VrFw5hc9rCdtWJPhmfJi8UuSzpDJY+CAPn8QJ0SlxM0=
github.com/swaggest/openapi-go v0.1.3/go.mod h1:Zx4ZgJ7XvlFH9wCOHE7u8RAjLfiHAnCHeaD5kUDujVM=
github.com/swaggest/openapi-go v0.1.13/go.mod h1:fju3Ka5zb8qBKf4789zLNVUqITydWLDjtPloZhhYHL8=
github.com/swaggest/openapi-go v0.2.9 h1:JJQrDSRtKMycIkLju97yjhJbw72mqhsCDJjqFB8HvSs=
Expand All @@ -128,8 +128,8 @@ github.com/swaggest/swgen v0.6.20/go.mod h1:ipkZNfwztgRfbOWUllLZawfmxXprT8flqNJO
github.com/swaggest/swgen v0.6.23/go.mod h1:gj2yCLONy3kosKjwRtQeT5O9qqlhUvXAiDnbVwBUNFM=
github.com/swaggest/swgen v0.6.27 h1:bO3CtX4NvuetPuABMChEEp2N/bjsJraegaJpYZ72aoI=
github.com/swaggest/swgen v0.6.27/go.mod h1:tQLsoQVLcvtAMzNbm26JbWXDHZM4BBRuI5nemk3gEDw=
github.com/swaggest/usecase v0.1.2 h1:Fi3z0nig4gBcX0n0nnEHD8QzCttdV3lAPmaoX3nQowA=
github.com/swaggest/usecase v0.1.2/go.mod h1:BKvpHx94bpg+4RCXbrCv0jx8CpJRr5P8dYK0b4UfUZc=
github.com/swaggest/usecase v0.1.3 h1:rK/t7PR3pt6pDEVQuT9n/aXHbMtKNzulqniQ/GSOiCc=
github.com/swaggest/usecase v0.1.3/go.mod h1:d94PwelVQeMv4RH0VsRMFqRNrk6ferEqg2AetDcjPWo=
github.com/vearutop/json5 v0.1.2 h1:AD+VTRiN8TZb4/ObzXXyYIY66AVNqdBn0O8ks/WqgH0=
github.com/vearutop/json5 v0.1.2/go.mod h1:0EGfZGmwGu042tElaCn8+Song+vEMQOOgChj+mzD7OQ=
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
Expand Down
4 changes: 4 additions & 0 deletions nethttp/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ var _ http.Handler = &Handler{}

// NewHandler creates use case http handler.
func NewHandler(useCase usecase.Interactor, options ...func(h *Handler)) *Handler {
if useCase == nil {
panic("usecase interactor is nil")
}

h := &Handler{
options: options,
}
Expand Down
55 changes: 49 additions & 6 deletions nethttp/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ func OpenAPIMiddleware(s *openapi.Collector) func(http.Handler) http.Handler {
}

// HTTPBasicSecurityMiddleware creates middleware to expose Basic Security schema.
func HTTPBasicSecurityMiddleware(c *openapi.Collector, name, description string) func(http.Handler) http.Handler {
func HTTPBasicSecurityMiddleware(
c *openapi.Collector,
name, description string,
options ...func(*MiddlewareConfig),
) func(http.Handler) http.Handler {
hss := openapi3.HTTPSecurityScheme{}

hss.WithScheme("basic")
Expand All @@ -54,12 +58,20 @@ func HTTPBasicSecurityMiddleware(c *openapi.Collector, name, description string)
},
)

return securityMiddleware(c, name)
cfg := MiddlewareConfig{}

for _, o := range options {
o(&cfg)
}

return securityMiddleware(c, name, cfg)
}

// HTTPBearerSecurityMiddleware creates middleware to expose HTTP Bearer security schema.
func HTTPBearerSecurityMiddleware(
c *openapi.Collector, name, description, bearerFormat string,
c *openapi.Collector,
name, description, bearerFormat string,
options ...func(*MiddlewareConfig),
) func(http.Handler) http.Handler {
hss := openapi3.HTTPSecurityScheme{}

Expand All @@ -82,7 +94,13 @@ func HTTPBearerSecurityMiddleware(
},
)

return securityMiddleware(c, name)
cfg := MiddlewareConfig{}

for _, o := range options {
o(&cfg)
}

return securityMiddleware(c, name, cfg)
}

// AnnotateOpenAPI applies OpenAPI annotation to relevant handlers.
Expand All @@ -105,10 +123,35 @@ func AnnotateOpenAPI(
}
}

func securityMiddleware(s *openapi.Collector, name string) func(http.Handler) http.Handler {
// SecurityResponse is an security middleware option to customize response structure and status.
func SecurityResponse(structure interface{}, httpStatus int) func(config *MiddlewareConfig) {
return func(config *MiddlewareConfig) {
config.ResponseStructure = structure
config.ResponseStatus = httpStatus
}
}

// MiddlewareConfig defines security middleware options.
type MiddlewareConfig struct {
// ResponseStructure declares structure that is used for unauthorized message, default rest.ErrResponse{}.
ResponseStructure interface{}

// ResponseStatus declares HTTP status code that is used for unauthorized message, default http.StatusUnauthorized.
ResponseStatus int
}

func securityMiddleware(s *openapi.Collector, name string, cfg MiddlewareConfig) func(http.Handler) http.Handler {
return AnnotateOpenAPI(s, func(op *openapi3.Operation) error {
op.Security = append(op.Security, map[string][]string{name: {}})

return s.Reflector().SetJSONResponse(op, rest.ErrResponse{}, http.StatusUnauthorized)
if cfg.ResponseStatus == 0 {
cfg.ResponseStatus = http.StatusUnauthorized
}

if cfg.ResponseStructure == nil {
cfg.ResponseStructure = rest.ErrResponse{}
}

return s.Reflector().SetJSONResponse(op, cfg.ResponseStructure, cfg.ResponseStatus)
})
}
9 changes: 8 additions & 1 deletion nethttp/openapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ func TestOpenAPIMiddleware(t *testing.T) {
_ = nethttp.WrapHandler(uh,
nethttp.OpenAPIMiddleware(&c),
nethttp.HTTPBasicSecurityMiddleware(&c, "admin", "Admin Area."),
nethttp.HTTPBearerSecurityMiddleware(&c, "api", "API Security.", "JWT"),
nethttp.HTTPBearerSecurityMiddleware(&c, "api", "API Security.", "JWT",
nethttp.SecurityResponse(new(struct {
Error string `json:"error"`
}), http.StatusForbidden)),
nethttp.HandlerWithRouteMiddleware(http.MethodGet, "/test"),
)

Expand All @@ -77,6 +80,10 @@ func TestOpenAPIMiddleware(t *testing.T) {
"401":{
"description":"Unauthorized",
"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestErrResponse"}}}
},
"403":{
"description":"Forbidden",
"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}
}
},
"security":[{"api":[]},{"admin":[]}]
Expand Down
12 changes: 8 additions & 4 deletions openapi/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@ func (c *Collector) Collect(
u usecase.Interactor,
h rest.HandlerTrait,
annotations ...func(*openapi3.Operation) error,
) error {
) (err error) {
c.mu.Lock()
defer c.mu.Unlock()

var err error
defer func() {
if err != nil {
err = fmt.Errorf("failed to reflect API schema for %s %s: %w", method, pattern, err)
}
}()

reflector := c.Reflector()

Expand All @@ -70,12 +74,12 @@ func (c *Collector) Collect(

err = c.setupInput(&oc, u, h)
if err != nil {
return err
return fmt.Errorf("failed to setup request: %w", err)
}

err = c.setupOutput(&oc, u)
if err != nil {
return err
return fmt.Errorf("failed to setup response: %w", err)
}

err = c.processUseCase(op, u, h)
Expand Down

0 comments on commit 88927c7

Please sign in to comment.