Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 1e3b357
Author: Martin Rode <martin.rode@programmfabrik.de>
Date:   Thu Jul 1 14:23:16 2021 +0200

    body_type: fixed "urlencoded"

    urlencoded was not working if the provided body did not unmarshal to map[string][string] but to map[string]interface{}, which must have always happened. So, I suspect this never worked and is untested in the unit tests.

commit 8458072
Author: Martin Rode <martin.rode@programmfabrik.de>
Date:   Thu Jul 1 14:22:07 2021 +0200

    oauth2: added "oauth2_basic_auth" + improved template functions

    Renamed oauth2.Key to oauth2.Client

commit 8dc7114
Author: Unai Garcia <unai.garcia@programmfabrik.de>
Date:   Thu Jul 1 12:02:54 2021 +0200

    returning only string on template functions

commit 929b37f
Author: Unai Garcia <unai.garcia@programmfabrik.de>
Date:   Thu Jul 1 11:06:16 2021 +0200

    added template functions, tests and doc

commit ca1a69b
Merge: ee59ffb f4c6a99
Author: Unai Garcia <unai.garcia@programmfabrik.de>
Date:   Thu Jul 1 09:52:22 2021 +0200

    Merge branch 'master' into enhancement/oauth2-flows

commit ee59ffb
Author: Martin Rode <martin.rode@programmfabrik.de>
Date:   Thu Jul 1 09:47:20 2021 +0200

    oauth2: changed error management

commit c21f01e
Author: Martin Rode <martin.rode@programmfabrik.de>
Date:   Wed Jun 30 17:14:22 2021 +0200

    oauth2: renamed oauth2_token, fixed README typos

commit f361cab
Author: Unai Garcia <unai.garcia@programmfabrik.de>
Date:   Fri Jun 25 16:10:40 2021 +0200

    added some doc about oauth functionality

commit 385747e
Author: Unai Garcia <unai.garcia@programmfabrik.de>
Date:   Fri Jun 25 15:44:36 2021 +0200

    - Added support for oauth different flows
    - Added template functions for each
    - Added unit and api tests
  • Loading branch information
martinrode committed Jul 1, 2021
1 parent f4c6a99 commit 39c5e73
Show file tree
Hide file tree
Showing 15 changed files with 242 additions and 58 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1704,6 +1704,22 @@ Returns a `string` of the MD5 sum of the file found in `filepath`.
Returns a `string` where all `"` are escaped to `\"`. This is useful in Strings which need to be concatenated.
## `query_escape [string]`
Returns a `string` as the result of escaping input as if it was intended for use in a URL query string.
## `query_unescape [string]`
Returns a `string` as the result of unescaping input as if it was coming from a URL query string.
## `base64_encode [string]`
Returns a `string` as the result of encoding input into base64.
## `base64_decode [string]`
Returns a `string` as the result of decoding input from base64.
## `url_path_escape [string]`
Uses [Url.PathEscape](https://pkg.go.dev/net/url?tab=doc#PathEscape) to escape given `string` to use in `endpoint` or `server_url`. Returns `string`.
Expand Down Expand Up @@ -1816,6 +1832,24 @@ Example:
}
```
## `oauth2_client [client]`
**oauth2_client** returns a configured **oauth client** given its `client_id`. Result is an object which contains several properties.
Example:
```django
{
"store": {
"oauth2_client_config": {{ oauth2_client "my_client" | marshal }}
}
}
```
## `oauth2_basic_auth [client]`
** oauth2_basic_auth** returns the authentication header for basic authentication for the given oauth client.
# HTTP Server
The apitest tool includes an HTTP Server. It can be used to serve files from the local disk temporarily. The HTTP Server can run in test mode. In this mode, the apitest tool does not run any tests, but starts the HTTP Server in the foreground, until CTRL-C in pressed.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ require (
github.com/spf13/viper v1.5.0
github.com/tidwall/gjson v1.3.4
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
)
11 changes: 9 additions & 2 deletions pkg/lib/api/build_policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,15 @@ func buildUrlencoded(request Request) (additionalHeaders map[string]string, body
additionalHeaders = make(map[string]string, 0)
additionalHeaders["Content-Type"] = "application/x-www-form-urlencoded"
formParams := url.Values{}
for key, value := range request.Body.(map[string]string) {
formParams.Add(key, value)
for key, value := range request.Body.(map[string]interface{}) {
switch v := value.(type) {
case string:
formParams.Set(key, v)
case []string:
formParams[key] = v
default:
formParams.Set(key, fmt.Sprintf("%s", v))
}
}
body = strings.NewReader(formParams.Encode())
return additionalHeaders, body, nil
Expand Down
34 changes: 34 additions & 0 deletions pkg/lib/template/oauth2_helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package template

import (
"encoding/json"

"golang.org/x/oauth2"
)

type oAuth2TokenExtended struct {
*oauth2.Token
Error string `json:"error"`
ErrorDescription string `json:"error_description"`
}

// ReadOAuthReturnValue checks the return values from an OAUTH client and
// stores the error in an extended struct of the oAuth token
func readOAuthReturnValue(t *oauth2.Token, err error) (tE oAuth2TokenExtended, err2 error) {
if t == nil {
t = &oauth2.Token{} // Make sure we have "AccessToken" in our struct
}
tE = oAuth2TokenExtended{Token: t}
if err != nil {
switch v := err.(type) {
case *oauth2.RetrieveError:
err = json.Unmarshal(v.Body, &tE)
if err != nil {
tE.Error = err.Error()
tE.ErrorDescription = string(v.Body)
err = nil
}
}
}
return tE, err
}
81 changes: 51 additions & 30 deletions pkg/lib/template/template_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"crypto/md5"
"database/sql"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
Expand All @@ -15,7 +16,6 @@ import (

"github.com/pkg/errors"
"github.com/programmfabrik/apitest/pkg/lib/datastore"
"golang.org/x/oauth2"

"github.com/programmfabrik/apitest/pkg/lib/cjson"
"github.com/programmfabrik/apitest/pkg/lib/csv"
Expand Down Expand Up @@ -352,62 +352,83 @@ func (loader *Loader) Render(
parsedURL.Host = loader.HTTPServerHost
return parsedURL.String(), nil
},
"server_url": func() *url.URL {
return loader.ServerURL
"server_url": func() url.URL {
return *loader.ServerURL
},
"is_zero": func(v interface{}) bool {
if v == nil {
return true
}
return reflect.ValueOf(v).IsZero()
},
"oauth2_password_token": func(client string, login string, password string) (t *oauth2.Token, err error) {
"oauth2_password_token": func(client string, login string, password string) (tE oAuth2TokenExtended, err error) {
oAuthClient, ok := loader.OAuthClient[client]
if !ok {
return nil, errors.Errorf("OAuth client %s not configured", client)
return tE, errors.Errorf("OAuth client %s not configured", client)
}
oAuthClient.Key = client
t, err = oAuthClient.GetPasswordCredentialsAuthToken(login, password)
if err != nil {
t = &oauth2.Token{AccessToken: err.Error()}
oAuthClient.Client = client
return readOAuthReturnValue(oAuthClient.GetPasswordCredentialsAuthToken(login, password))

},
"oauth2_client_token": func(client string) (tE oAuth2TokenExtended, err error) {
oAuthClient, ok := loader.OAuthClient[client]
if !ok {
return tE, errors.Errorf("OAuth client %s not configured", client)
}
return t, nil
oAuthClient.Client = client
return readOAuthReturnValue(oAuthClient.GetClientCredentialsAuthToken())
},
"oauth2_client_token": func(client string) (t *oauth2.Token, err error) {
"oauth2_code_token": func(client string, params ...string) (tE oAuth2TokenExtended, err error) {
oAuthClient, ok := loader.OAuthClient[client]
if !ok {
return nil, errors.Errorf("OAuth client %s not configured", client)
return tE, errors.Errorf("OAuth client %s not configured", client)
}
oAuthClient.Key = client
t, err = oAuthClient.GetClientCredentialsAuthToken()
if err != nil {
t = &oauth2.Token{AccessToken: err.Error()}
oAuthClient.Client = client
return readOAuthReturnValue(oAuthClient.GetCodeAuthToken(params...))
},
"oauth2_implicit_token": func(client string, params ...string) (tE oAuth2TokenExtended, err error) {
oAuthClient, ok := loader.OAuthClient[client]
if !ok {
return tE, errors.Errorf("OAuth client %s not configured", client)
}
return t, nil
oAuthClient.Client = client
return readOAuthReturnValue(oAuthClient.GetAuthToken(params...))
},
"oauth2_code_token": func(client string, params ...string) (t *oauth2.Token, err error) {
"oauth2_client": func(client string) (c *util.OAuthClientConfig, err error) {
oAuthClient, ok := loader.OAuthClient[client]
if !ok {
return nil, errors.Errorf("OAuth client %s not configured", client)
}
oAuthClient.Key = client
t, err = oAuthClient.GetCodeAuthToken(params...)
if err != nil {
t = &oauth2.Token{AccessToken: err.Error()}
}
return t, nil
oAuthClient.Client = client
return &oAuthClient, nil
},
"oauth2_implicit_token": func(client string, params ...string) (t *oauth2.Token, err error) {
"oauth2_basic_auth": func(client string) (string, error) {
oAuthClient, ok := loader.OAuthClient[client]
if !ok {
return nil, errors.Errorf("OAuth client %s not configured", client)
return "", errors.Errorf("OAuth client %s not configured", client)
}
oAuthClient.Client = client
return "Basic " + base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", oAuthClient.Client, oAuthClient.Secret))), nil
},
"query_escape": func(in string) string {
return url.QueryEscape(in)
},
"query_unescape": func(in string) string {
out, err := url.QueryUnescape(in)
if err != nil {
return err.Error()
}
oAuthClient.Key = client
t, err = oAuthClient.GetAuthToken(params...)
return out
},
"base64_encode": func(in string) string {
return base64.StdEncoding.EncodeToString([]byte(in))
},
"base64_decode": func(in string) string {
b, err := base64.StdEncoding.DecodeString(in)
if err != nil {
t = &oauth2.Token{AccessToken: err.Error()}
return err.Error()
}
return t, nil
return string(b)
},
}
tmpl, err := template.New("tmpl").Funcs(funcMap).Parse(string(tmplBytes))
Expand Down
18 changes: 9 additions & 9 deletions pkg/lib/util/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ type OAuthClientsConfig map[string]OAuthClientConfig

// OAuthClientConfig is our config for a single oAuth client
type OAuthClientConfig struct {
Key string
Endpoint OAuthEndpointConfig `mapstructure:"endpoint"`
Secret string `mapstructure:"secret"`
RedirectURL string `mapstructure:"redirect_url"`
Scopes []string `mapstructure:"scopes"`
Client string `json:"client"`
Endpoint OAuthEndpointConfig `mapstructure:"endpoint" json:"endpoint"`
Secret string `mapstructure:"secret" json:"secret"`
RedirectURL string `mapstructure:"redirect_url" json:"redirect_url"`
Scopes []string `mapstructure:"scopes" json:"scopes"`
}

// OAuthEndpointConfig is our config for an oAuth endpoint
type OAuthEndpointConfig struct {
TokenURL string `mapstructure:"token_url"`
AuthURL string `mapstructure:"auth_url"`
TokenURL string `mapstructure:"token_url" json:"token_url"`
AuthURL string `mapstructure:"auth_url" json:"auth_url"`
}

func getOAuthClientConfig(c OAuthClientConfig) oauth2.Config {
return oauth2.Config{
ClientID: c.Key,
ClientID: c.Client,
ClientSecret: c.Secret,
Endpoint: oauth2.Endpoint{
AuthURL: c.Endpoint.AuthURL,
Expand All @@ -44,7 +44,7 @@ func getOAuthClientConfig(c OAuthClientConfig) oauth2.Config {

func getOAuthClientCredentialsConfig(c OAuthClientConfig) clientcredentials.Config {
return clientcredentials.Config{
ClientID: c.Key,
ClientID: c.Client,
ClientSecret: c.Secret,
TokenURL: c.Endpoint.TokenURL,
}
Expand Down
Loading

0 comments on commit 39c5e73

Please sign in to comment.