Skip to content

Commit

Permalink
Merge pull request #6 from go-swagno/security
Browse files Browse the repository at this point in the history
#3 Add Security Definations
  • Loading branch information
anilsenay authored Sep 4, 2022
2 parents a52a03b + dd6aa44 commit ce6fb30
Show file tree
Hide file tree
Showing 7 changed files with 388 additions and 82 deletions.
211 changes: 190 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ This project inspired by [Swaggo](https://github.com/swaggo/swag). Swaggo, uses
- [General Swagger Info](#general-swagger-info)
- [Adding Contact and License info (optional)](#adding-contact-and-license-info-optional)
- [Adding Tags (optional)](#adding-tags-optional)
- [Security](#security)
- [Basic Auth](#basic-auth)
- [API Key Auth](#api-key-auth)
- [OAuth2](#oauth2-auth)
- [Endpoints (API)](#endpoints-api)
- [Arguments](#arguments)
- [Contribution](#contribution)

## Getting started
Expand All @@ -45,9 +50,9 @@ You can import without explicit period (.) like this: `import "github.com/go-swa

```go
endpoints := []Endpoint{
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products"),
EndPoint(GET, "/product", "product", Params(IntParam("id", true, "")), nil, models.Product{}, models.ErrorResponse{}, ""),
EndPoint(POST, "/product", "product", Params(), models.ProductPost{}, models.Product{}, models.ErrorResponse{}, ""),
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products", nil),
EndPoint(GET, "/product", "product", Params(IntParam("id", true, "")), nil, models.Product{}, models.ErrorResponse{}, "", nil),
EndPoint(POST, "/product", "product", Params(), models.ProductPost{}, models.Product{}, models.ErrorResponse{}, "", nil),
}
```

Expand Down Expand Up @@ -98,7 +103,7 @@ Example:

1. Get swagno-fiber

```go
```sh
go get github.com/go-swagno/swagno-fiber
```

Expand Down Expand Up @@ -127,7 +132,7 @@ Example:

1. Get swagno-gin

```go
```sh
go get github.com/go-swagno/swagno-gin
```

Expand Down Expand Up @@ -161,9 +166,10 @@ As purpose of this section, you can compare **swagno** status with **swaggo**
- [x] Describing Request Body
- [x] Describing Responses
- [x] MIME Types -> need to improve
- [ ] Authentication
- [ ] Basic Authentication
- [ ] API Keys
- [x] Authentication
- [x] Basic Authentication
- [x] API Keys
- [x] OAuth2
- [ ] Adding Examples
- [x] File Upload -> need to improve
- [x] Enums
Expand Down Expand Up @@ -208,29 +214,94 @@ sw.AddTags(SwaggerTag{Name: "WithStruct", Description: "WithStruct operations"})
sw.Tags = append(sw.Tags, SwaggerTag{Name: "headerparams", Description: "headerparams operations"})
```

## Security

If you want to add security to your swagger, you can use `SetBasicAuth`, `SetApiKeyAuth`, `SetOAuth2Auth` functions.

```go
sw.SetBasicAuth()
sw.SetApiKeyAuth("api_key", "header")
sw.SetOAuth2Auth("oauth2_name", "password", "http://localhost:8080/oauth2/token", "http://localhost:8080/oauth2/authorize", Scopes(Scope("read:pets", "read your pets"), Scope("write:pets", "modify pets in your account")))
```

#### Basic Auth

If you have a basic auth with username and password, you can use `SetBasicAuth` function. It has default name as "basicAuth". You can add description as argument:

```go
sw.SetBasicAuth()
// with description
sw.SetBasicAuth("Description")
```

#### Api Key Auth

If you have an api key auth, you can use `SetApiKeyAuth` function.

Parameters:

- `name` -> name of the api key
- `in` -> location of the api key. It can be `header` or `query`
- `description` (optional) -> you can also add description as argument

```go
sw.SetApiKeyAuth("api_key", "header")
// with description
sw.SetApiKeyAuth("api_key", "header", "Description")
```

#### OAuth2 Auth

If you have an oauth2 auth, you can use `SetOAuth2Auth` function. You can also add description as argument:

Parameters:

- `name` -> name of the oauth2
- `flow` -> flow type of the oauth2. It can be `implicit`, `password`, `application`, `accessCode`
- `authorizationUrl` -> authorization url of the oauth2 (set this if flow is `impilicit` or `accessCode`, else you can set empty string)
- `tokenUrl` -> token url of the oauth2 (set this if flow is `password`, `application` or `accessCode`, else you can set empty string)
- `scopes` -> scopes of the oauth2
- `description` (optional) -> you can also add description as argument

```go
sw.SetOAuth2Auth("oauth2_name", "password", "", "http://localhost:8080/oauth2/token", Scopes(Scope("read:pets", "read your pets"), Scope("write:pets", "modify pets in your account")))
```

For scopes, you can use `Scopes` function. It takes `Scope` as variadic parameter.
Parameters of `Scope`:

- `name` -> name of the scope
- `description` -> description of the scope

## Endpoints (API)

Defination:

```go
EndPoint(method MethodType, path string, tags string, params []Parameter, body interface{}, ret interface{}, err interface{}, des string, secuirty []map[string][]string, args ...string)
```

You need to create an Endpoint array []Endpoint and add your endpoints in this array. Example:

```go
endpoints := []Endpoint{
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products"),
EndPoint(GET, "/product", "product", Params(IntParam("id", true, "")), nil, models.Product{}, models.ErrorResponse{}, ""),
EndPoint(POST, "/product", "product", Params(), models.ProductPost{}, models.Product{}, models.ErrorResponse{}, ""),
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products", nil),
EndPoint(GET, "/product", "product", Params(IntParam("id", true, "")), nil, models.Product{}, models.ErrorResponse{}, "", nil),
EndPoint(POST, "/product", "product", Params(), models.ProductPost{}, models.Product{}, models.ErrorResponse{}, "", nil),
}
// add endpoints array to Swagno
AddEndpoints(endpoints)
```

- Arguments: (Method, Path, Tag, Params, Body, Response, Error Response, Description)
- Arguments: (Method, Path, Tag, Params, Body, Response, Error Response, Description, Security)

**NOTE: If you not imported with explicit period (.), you need to get from swagno package:**

```go
endpoints := []swagno.Endpoint{
swagno.EndPoint(swagno.GET, "/product", "product", swagno.Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products"),
swagno.EndPoint(swagno.GET, "/product", "product", swagno.Params(swagno.IntParam("id", true, "")), nil, models.Product{}, models.ErrorResponse{}, ""),
swagno.EndPoint(swagno.POST, "/product", "product", swagno.Params(), models.ProductPost{}, models.Product{}, models.ErrorResponse{}, ""),
swagno.EndPoint(swagno.GET, "/product", "product", swagno.Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products", nil),
swagno.EndPoint(swagno.GET, "/product", "product", swagno.Params(swagno.IntParam("id", true, "")), nil, models.Product{}, models.ErrorResponse{}, "", nil),
swagno.EndPoint(swagno.POST, "/product", "product", swagno.Params(), models.ProductPost{}, models.Product{}, models.ErrorResponse{}, "", nil),
}
// add endpoints array to Swagno
swagno.AddEndpoints(endpoints)
Expand All @@ -245,10 +316,25 @@ endpoints := []Endpoint{
```

**Don't forget to add your endpoints array to Swagno**

```go
AddEndpoints(endpoints)
```

### Arguments:

- [Method](#method)
- [Path](#path)
- [Tags](#tags)
- [Params](#params)
- [Body](#body)
- [Return](#responsereturn)
- [Error](#error-response)
- [Description](#description)
- [Security](#security)
- [Consumes](#consumes-optional) (optional / extra argument)
- [Produces](#produces-optional) (optional / extra argument)

### Method

Options: GET, POST, PUT, DELETE, OPTION, PATCH, HEAD
Expand Down Expand Up @@ -325,12 +411,12 @@ Or you can use []Parameter array:
| MultipleOf | see: https://datatracker.ietf.org/doc/html/draft-fge-json-schema-validation-00#section-5.1.1 |
| CollenctionFormat | if type is "array", checkout the table above: |

| CollenctionFormat | Description |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
| csv | comma separated values foo,bar. |
| ssv | space separated values foo bar. |
| tsv | tab separated values foo\tbar. |
| pipes | pipe separated values foo | bar. |
| CollenctionFormat | Description |
| ----------------- | ----------------- |
| csv | comma separated values foo,bar.|
| ssv | space separated values foo bar.|
| tsv | tab separated values foo\tbar.|
| pipes | pipe separated values foo \| bar. |
| multi | corresponds to multiple parameter instances instead of multiple values for a single instance foo=bar&foo=baz. This is valid only for parameters in "query" or "formData".  |

### Body
Expand All @@ -349,6 +435,89 @@ use a struct model instance like `models.ErrorResponse` or nil

Endpoint description as string

### Security

Before using this function, you need to define your security definitions in Swagno struct. For example:

```go
sw.SetBasicAuth()
sw.SetApiKeyAuth("api_key", "query")
sw.SetOAuth2Auth("oauth2_name", "password", "http://localhost:8080/oauth2/token", "http://localhost:8080/oauth2/authorize", Scopes(Scope("read:pets", "read your pets"), Scope("write:pets", "modify pets in your account")))
```

If you want to add security to your endpoint, you can use one of `BasicAuth()`, `ApiKeyAuth()`, `OAuth()` functions:

```go
BasicAuth()
```

```go
ApiKeyAuth("api_key")
```

```go
OAuth("oauth2_name", "read:pets")
// you can add more scope name as argument
OAuth("oauth2_name", "read:pets", "write:pets", "...")
```

And use in `EndPoint` function:

```go
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.Error{}, "description", ApiKeyAuth("api_key", "header"))
```

You can add more than one security to your endpoint with `Security()` function:

```go
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products", Security(ApiKeyAuth("api_key", "header"), BasicAuth()))
```

#### BasicAuth

If you want to use basic auth to an endpoint, you can use `BasicAuth()` function.

```go
BasicAuth("Basic Auth Description")
```

#### ApiKeyAuth

If you want to use api key auth to an endpoint, you can use `ApiKeyAuth()` function. It needs name as argument. This name must match with one of your Swagno security definations.

```go
ApiKeyAuth("api_key")
```

#### OAuth2Auth

If you want to use oauth2 auth to an endpoint, you can use `OAuth2Auth()` function. It needs name as argument. This name must match with one of your Swagno security definations. Then you can add scopes as arguments:

```go
OAuth2Auth("oauth2_name", "read:pets", "write:pets")
```

### Consumes (optional)

For default there is only one consumes type: "application/json", you don't need to add it. If you want to add more consumes types, you can add them as string as seperated by commas to EndPoint function's extra option:

```go
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products", nil, "application/xml,text/plain"),
```

**NOTE: If you used FileParam() in your endpoint, you don't need to add "multipart/form-data" to consumes. It will add automatically.**

### Produces (optional)

For default there are two produces types: "application/json" and "application/xml", you don't need to add them. If you want to add more produces types, you can add them as string as seperated by commas to EndPoint function's extra option:

```go
// without extra consumes -> nil as consumes
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products", nil, nil, "application/xml,text/plain"),
// with extra consumes
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products", nil, "application/xml,text/plain", "text/plain,text/html"),
```

# Contribution

We are welcome to any contribution. Swagno still has some missing features. Also we want to enrich handler implementations for other web frameworks.
58 changes: 47 additions & 11 deletions endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,21 @@ type Fields struct {
}

type Endpoint struct {
Method string `json:"method"`
Path string `json:"path"`
Params []Parameter `json:"params"`
Tags []string `json:"tags"`
Return interface{} `json:"return"`
Error interface{} `json:"error"`
Body interface{} `json:"body"`
Description string `json:"description"`
Consume []string `json:"consume"`
Produce []string `json:"produce"`
Method string `json:"method"`
Path string `json:"path"`
Params []Parameter `json:"params"`
Tags []string `json:"tags"`
Return interface{} `json:"return"`
Error interface{} `json:"error"`
Body interface{} `json:"body"`
Description string `json:"description"`
Consume []string `json:"consume"`
Produce []string `json:"produce"`
Security []map[string][]string `json:"security"`
}

// args: method, path, tags, params, body, return, error, description, consume, produce
func EndPoint(method MethodType, path string, tags string, params []Parameter, body interface{}, ret interface{}, err interface{}, des string, args ...string) Endpoint {
func EndPoint(method MethodType, path string, tags string, params []Parameter, body interface{}, ret interface{}, err interface{}, des string, secuirty []map[string][]string, args ...string) Endpoint {
removedSpace := strings.ReplaceAll(tags, " ", "")
endpoint := Endpoint{
Method: string(method),
Expand All @@ -96,6 +97,7 @@ func EndPoint(method MethodType, path string, tags string, params []Parameter, b
Body: body,
Error: err,
Description: des,
Security: secuirty,
}
if len(args) > 0 && len(args[0]) > 0 {
endpoint.Consume = strings.Split(args[0], ",")
Expand Down Expand Up @@ -367,3 +369,37 @@ func fillItemParams(param *Parameter) {
param.Items.Pattern = param.Pattern
param.Items.UniqueItems = param.UniqueItems
}

// Security

func BasicAuth() []map[string][]string {
return []map[string][]string{
{
"basicAuth": []string{},
},
}
}

func ApiKeyAuth(name string) []map[string][]string {
return []map[string][]string{
{
name: []string{},
},
}
}

func OAuth(name string, scopes ...string) []map[string][]string {
return []map[string][]string{
{
name: scopes,
},
}
}

func Security(schemes ...[]map[string][]string) []map[string][]string {
m := make([]map[string][]string, 0)
for _, scheme := range schemes {
m = append(m, scheme...)
}
return m
}
4 changes: 2 additions & 2 deletions example/fiber-multi-array/handlers/merchant_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ func (h MerchantHandler) SetMerchantRoutes(a *fiber.App) {

var MerchantEndpoints = []Endpoint{
// /merchant/{merchantId}?id={id} -> get product of a merchant
EndPoint(GET, "/merchant", "merchant", Params(StrParam("merchant", true, ""), IntQuery("id", true, "product id")), nil, models.Product{}, models.ErrorResponse{}, ""),
EndPoint(GET, "/merchant", "merchant", Params(StrQuery("merchant", true, "")), nil, models.Product{}, models.ErrorResponse{}, ""),
EndPoint(GET, "/merchant", "merchant", Params(StrParam("merchant", true, ""), IntQuery("id", true, "product id")), nil, models.Product{}, models.ErrorResponse{}, "", nil),
EndPoint(GET, "/merchant", "merchant", Params(StrQuery("merchant", true, "")), nil, models.Product{}, models.ErrorResponse{}, "", nil),
}
8 changes: 4 additions & 4 deletions example/fiber-multi-array/handlers/product_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ func (h ProductHandler) SetProductRoutes(a *fiber.App) {
}

var ProductEndpoints = []Endpoint{
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products"),
EndPoint(GET, "/product", "product", Params(IntParam("id", true, "")), nil, models.Product{}, models.ErrorResponse{}, ""),
EndPoint(POST, "/product", "product", Params(), models.ProductPost{}, models.Product{}, models.ErrorResponse{}, ""),
EndPoint(GET, "/product", "product", Params(), nil, []models.Product{}, models.ErrorResponse{}, "Get all products", nil),
EndPoint(GET, "/product", "product", Params(IntParam("id", true, "")), nil, models.Product{}, models.ErrorResponse{}, "", nil),
EndPoint(POST, "/product", "product", Params(), models.ProductPost{}, models.Product{}, models.ErrorResponse{}, "", nil),

// /merchant/{merchantId}?id={id} -> get product of a merchant
EndPoint(GET, "/merchant", "merchant", Params(StrParam("merchant", true, ""), IntQuery("id", true, "product id")), nil, models.Product{}, models.ErrorResponse{}, ""),
EndPoint(GET, "/merchant", "merchant", Params(StrParam("merchant", true, ""), IntQuery("id", true, "product id")), nil, models.Product{}, models.ErrorResponse{}, "", nil),
}
Loading

0 comments on commit ce6fb30

Please sign in to comment.