Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add security fields support to protoc-gen-swagger #547

Merged
merged 3 commits into from
Feb 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 125 additions & 111 deletions examples/examplepb/a_bit_of_everything.pb.go

Large diffs are not rendered by default.

77 changes: 77 additions & 0 deletions examples/examplepb/a_bit_of_everything.proto
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,68 @@ option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
consumes: "application/x-foo-mime";
produces: "application/json";
produces: "application/x-foo-mime";
security_definitions: {
security: {
key: "BasicAuth";
value: {
type: TYPE_BASIC;
}
}
security: {
key: "ApiKeyAuth";
value: {
type: TYPE_API_KEY;
in: IN_HEADER;
name: "X-API-Key";
}
}
security: {
key: "OAuth2";
value: {
type: TYPE_OAUTH2;
flow: FLOW_ACCESS_CODE;
authorization_url: "https://example.com/oauth/authorize";
token_url: "https://example.com/oauth/token";
scopes: {
scope: {
key: "read";
value: "Grants read access";
}
scope: {
key: "write";
value: "Grants write access";
}
scope: {
key: "admin";
value: "Grants read and write access to administrative information";
}
}
}
}
}
security: {
security_requirement: {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see an entry for bearer (in authorization header). Which one would that map to? Reading the docs I guess an apiKey with the header "authorization"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that's how it could be done? But Authorization: Bearer isn't mentioned in v2: https://github.com/OAI/OpenAPI-Specification/blob/fb059ca461bd17b10a9e3e59879f04485886d356/versions/2.0.md

It would also look amusing, with every token prefixed with Bearer .

However, Authorization: Bearer would presumably be used with OAuth2 anyway; given v2 doesn't seem to address Bearer in other use cases, why not just do that? :)

key: "BasicAuth";
value: {};
}
security_requirement: {
key: "ApiKeyAuth";
value: {};
}
}
security: {
security_requirement: {
key: "OAuth2";
value: {
scope: "read";
scope: "write";
}
}
security_requirement: {
key: "ApiKeyAuth";
value: {};
}
}
};


Expand Down Expand Up @@ -154,6 +216,21 @@ service ABitOfEverythingService {
option (google.api.http) = {
delete: "/v1/example/a_bit_of_everything/{uuid}"
};
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = {
security: {
security_requirement: {
key: "ApiKeyAuth";
value: {}
}
security_requirement: {
key: "OAuth2";
value: {
scope: "read";
scope: "write";
}
}
}
};
}
rpc GetQuery(ABitOfEverything) returns (google.protobuf.Empty) {
option (google.api.http) = {
Expand Down
43 changes: 43 additions & 0 deletions examples/examplepb/a_bit_of_everything.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,15 @@
],
"tags": [
"ABitOfEverythingService"
],
"security": [
{
"ApiKeyAuth": [],
"OAuth2": [
"read",
"write"
]
}
]
},
"put": {
Expand Down Expand Up @@ -867,6 +876,40 @@
}
}
},
"securityDefinitions": {
"ApiKeyAuth": {
"type": "apiKey",
"name": "X-API-Key",
"in": "header"
},
"BasicAuth": {
"type": "basic"
},
"OAuth2": {
"type": "oauth2",
"flow": "accessCode",
"authorizationUrl": "https://example.com/oauth/authorize",
"tokenUrl": "https://example.com/oauth/token",
"scopes": {
"admin": "Grants read and write access to administrative information",
"read": "Grants read access",
"write": "Grants write access"
}
}
},
"security": [
{
"ApiKeyAuth": [],
"BasicAuth": []
},
{
"ApiKeyAuth": [],
"OAuth2": [
"read",
"write"
]
}
],
"externalDocs": {
"description": "More about gRPC-Gateway",
"url": "https://github.com/grpc-ecosystem/grpc-gateway"
Expand Down
100 changes: 100 additions & 0 deletions protoc-gen-swagger/genswagger/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,24 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re
operationObject.Tags = make([]string, len(opts.Tags))
copy(operationObject.Tags, opts.Tags)
}
if opts.Security != nil {
newSecurity := []swaggerSecurityRequirementObject{}
if operationObject.Security == nil {
newSecurity = []swaggerSecurityRequirementObject{}
} else {
newSecurity = operationObject.Security
}
for _, secReq := range opts.Security {
newSecReq := swaggerSecurityRequirementObject{}
for secReqKey, secReqValue := range secReq.SecurityRequirement {
newSecReqValue := make([]string, len(secReqValue.Scope))
copy(newSecReqValue, secReqValue.Scope)
newSecReq[secReqKey] = newSecReqValue
}
newSecurity = append(newSecurity, newSecReq)
}
operationObject.Security = newSecurity
}

// TODO(ivucica): add remaining fields of operation object
}
Expand Down Expand Up @@ -775,6 +793,88 @@ func applyTemplate(p param) (string, error) {
s.Produces = make([]string, len(spb.Produces))
copy(s.Produces, spb.Produces)
}
if spb.SecurityDefinitions != nil && spb.SecurityDefinitions.Security != nil {
if s.SecurityDefinitions == nil {
s.SecurityDefinitions = swaggerSecurityDefinitionsObject{}
}
for secDefKey, secDefValue := range spb.SecurityDefinitions.Security {
var newSecDefValue swaggerSecuritySchemeObject
if oldSecDefValue, ok := s.SecurityDefinitions[secDefKey]; !ok {
newSecDefValue = swaggerSecuritySchemeObject{}
} else {
newSecDefValue = oldSecDefValue
}
if secDefValue.Type != swagger_options.SecurityScheme_TYPE_INVALID {
switch secDefValue.Type {
case swagger_options.SecurityScheme_TYPE_BASIC:
newSecDefValue.Type = "basic"
case swagger_options.SecurityScheme_TYPE_API_KEY:
newSecDefValue.Type = "apiKey"
case swagger_options.SecurityScheme_TYPE_OAUTH2:
newSecDefValue.Type = "oauth2"
}
}
if secDefValue.Description != "" {
newSecDefValue.Description = secDefValue.Description
}
if secDefValue.Name != "" {
newSecDefValue.Name = secDefValue.Name
}
if secDefValue.In != swagger_options.SecurityScheme_IN_INVALID {
switch secDefValue.In {
case swagger_options.SecurityScheme_IN_QUERY:
newSecDefValue.In = "query"
case swagger_options.SecurityScheme_IN_HEADER:
newSecDefValue.In = "header"
}
}
if secDefValue.Flow != swagger_options.SecurityScheme_FLOW_INVALID {
switch secDefValue.Flow {
case swagger_options.SecurityScheme_FLOW_IMPLICIT:
newSecDefValue.Flow = "implicit"
case swagger_options.SecurityScheme_FLOW_PASSWORD:
newSecDefValue.Flow = "password"
case swagger_options.SecurityScheme_FLOW_APPLICATION:
newSecDefValue.Flow = "application"
case swagger_options.SecurityScheme_FLOW_ACCESS_CODE:
newSecDefValue.Flow = "accessCode"
}
}
if secDefValue.AuthorizationUrl != "" {
newSecDefValue.AuthorizationURL = secDefValue.AuthorizationUrl
}
if secDefValue.TokenUrl != "" {
newSecDefValue.TokenURL = secDefValue.TokenUrl
}
if secDefValue.Scopes != nil {
if newSecDefValue.Scopes == nil {
newSecDefValue.Scopes = swaggerScopesObject{}
}
for scopeKey, scopeDesc := range secDefValue.Scopes.Scope {
newSecDefValue.Scopes[scopeKey] = scopeDesc
}
}
s.SecurityDefinitions[secDefKey] = newSecDefValue
}
}
if spb.Security != nil {
newSecurity := []swaggerSecurityRequirementObject{}
if s.Security == nil {
newSecurity = []swaggerSecurityRequirementObject{}
} else {
newSecurity = s.Security
}
for _, secReq := range spb.Security {
newSecReq := swaggerSecurityRequirementObject{}
for secReqKey, secReqValue := range secReq.SecurityRequirement {
newSecReqValue := make([]string, len(secReqValue.Scope))
copy(newSecReqValue, secReqValue.Scope)
newSecReq[secReqKey] = newSecReqValue
}
newSecurity = append(newSecurity, newSecReq)
}
s.Security = newSecurity
}
if spb.ExternalDocs != nil {
if s.ExternalDocs == nil {
s.ExternalDocs = &swaggerExternalDocumentationObject{}
Expand Down
46 changes: 35 additions & 11 deletions protoc-gen-swagger/genswagger/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,40 @@ type swaggerExternalDocumentationObject struct {

// http://swagger.io/specification/#swaggerObject
type swaggerObject struct {
Swagger string `json:"swagger"`
Info swaggerInfoObject `json:"info"`
Host string `json:"host,omitempty"`
BasePath string `json:"basePath,omitempty"`
Schemes []string `json:"schemes"`
Consumes []string `json:"consumes"`
Produces []string `json:"produces"`
Paths swaggerPathsObject `json:"paths"`
Definitions swaggerDefinitionsObject `json:"definitions"`
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
}
Swagger string `json:"swagger"`
Info swaggerInfoObject `json:"info"`
Host string `json:"host,omitempty"`
BasePath string `json:"basePath,omitempty"`
Schemes []string `json:"schemes"`
Consumes []string `json:"consumes"`
Produces []string `json:"produces"`
Paths swaggerPathsObject `json:"paths"`
Definitions swaggerDefinitionsObject `json:"definitions"`
SecurityDefinitions swaggerSecurityDefinitionsObject `json:"securityDefinitions,omitempty"`
Security []swaggerSecurityRequirementObject `json:"security,omitempty"`
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
}

// http://swagger.io/specification/#securityDefinitionsObject
type swaggerSecurityDefinitionsObject map[string]swaggerSecuritySchemeObject

// http://swagger.io/specification/#securitySchemeObject
type swaggerSecuritySchemeObject struct {
Type string `json:"type"`
Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"`
In string `json:"in,omitempty"`
Flow string `json:"flow,omitempty"`
AuthorizationURL string `json:"authorizationUrl,omitempty"`
TokenURL string `json:"tokenUrl,omitempty"`
Scopes swaggerScopesObject `json:"scopes,omitempty"`
}

// http://swagger.io/specification/#scopesObject
type swaggerScopesObject map[string]string

// http://swagger.io/specification/#securityRequirementObject
type swaggerSecurityRequirementObject map[string][]string

// http://swagger.io/specification/#pathsObject
type swaggerPathsObject map[string]swaggerPathItemObject
Expand All @@ -82,6 +105,7 @@ type swaggerOperationObject struct {
Tags []string `json:"tags,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`

Security []swaggerSecurityRequirementObject `json:"security,omitempty"`
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
}

Expand Down
Loading