Skip to content

Commit

Permalink
refactored good parts of the code (#10)
Browse files Browse the repository at this point in the history
* made the init/login/logout flow more robust

* added signup/login timeout

* removed helpers and other odd functions

* created a dedicated app info struct for the config

* moved some utility code the helpers package

* refactored packages config and settings, lot's of name changes to functions etc

* removed unused error definitions from the codebase

* moved examples one level up
  • Loading branch information
mickume authored Aug 28, 2022
1 parent 7cdc7e1 commit 047aaf5
Show file tree
Hide file tree
Showing 34 changed files with 753 additions and 678 deletions.
15 changes: 7 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ all: test

.PHONY: test
test:
cd internal && go test -covermode=atomic
cd internal/auth && go test -covermode=atomic
cd internal/settings && go test -covermode=atomic
cd logger && go test -covermode=atomic
Expand All @@ -21,26 +20,26 @@ test:
.PHONY: test_build
test_build:
go mod verify && go mod tidy
cd examples/auth/api && go build main.go && rm main
cd examples/auth/cli && go build cli.go && rm cli
cd examples/service && go build main.go && rm main
cd examples/cli && go build cli.go && rm cli
cd examples/appengine && go build main.go && rm main


.PHONY: examples
examples: example_cli example_api
examples: example_cli example_service

.PHONY: example_appengine
example_appengine:
cd examples/appengine && gcloud app deploy . --quiet

.PHONY: example_cli
example_cli:
cd examples/auth/cli && go build -o ${EXAMPLE_NAME} cli.go && mv ${EXAMPLE_NAME} ../../../bin/${EXAMPLE_NAME}
cd examples/cli && go build -o ${EXAMPLE_NAME} cli.go && mv ${EXAMPLE_NAME} ../../bin/${EXAMPLE_NAME}
chmod +x bin/${EXAMPLE_NAME}

.PHONY: example_api
example_api:
cd examples/auth/cli && go build -o svc cli.go && mv svc ../../../bin/${EXAMPLE_NAME}svc
.PHONY: example_service
example_service:
cd examples/service && go build -o svc main.go && mv svc ../../bin/${EXAMPLE_NAME}svc
chmod +x bin/${EXAMPLE_NAME}svc

#.PHONY example_api_container
Expand Down
27 changes: 14 additions & 13 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"fmt"
"net/http"

"github.com/txsvc/apikit"

"github.com/txsvc/stdlib/v2"

"github.com/txsvc/apikit/config"
Expand All @@ -27,21 +25,24 @@ const (
var (
// ErrMissingCredentials indicates that a credentials are is missing
ErrMissingCredentials = errors.New("missing credentials")

// ErrApiInvocationError indicates an error in an API call
ErrApiInvocationError = errors.New("api invocation error")
)

// Client - API client encapsulating the http client
type (
Client struct {
httpClient *http.Client
cfg *settings.Settings
cfg *settings.DialSettings
logger logger.Logger
userAgent string
trace string
}
)

func NewClient(cfg *settings.Settings, logger logger.Logger) (*Client, error) {
var _cfg *settings.Settings
func NewClient(cfg *settings.DialSettings, logger logger.Logger) (*Client, error) {
var cfg_ *settings.DialSettings

httpClient, err := NewTransport(logger, http.DefaultTransport)
if err != nil {
Expand All @@ -51,20 +52,20 @@ func NewClient(cfg *settings.Settings, logger logger.Logger) (*Client, error) {
// create or clone the settings
if cfg != nil {
c := cfg.Clone()
_cfg = &c
cfg_ = &c
} else {
_cfg = config.GetSettings()
if _cfg.Credentials == nil {
_cfg.Credentials = &settings.Credentials{} // just provide something to prevent NPEs further down
cfg_ = config.GetConfig().Settings()
if cfg_.Credentials == nil {
cfg_.Credentials = &settings.Credentials{} // just provide something to prevent NPEs further down
}
}

return &Client{
httpClient: httpClient,
cfg: _cfg,
cfg: cfg_,
logger: logger,
userAgent: config.UserAgentString(),
trace: stdlib.GetString("APIKIT_FORCE_TRACE", ""),
userAgent: config.GetConfig().Info().UserAgentString(),
trace: stdlib.GetString(config.ForceTraceEnv, ""),
}, nil
}

Expand Down Expand Up @@ -142,7 +143,7 @@ func (c *Client) roundTrip(req *http.Request, response interface{}) (int, error)
}
return status.Status, fmt.Errorf(status.Message)
}
return resp.StatusCode, apikit.ErrApiError
return resp.StatusCode, ErrApiInvocationError
}

// unmarshal the response if one is expected
Expand Down
60 changes: 40 additions & 20 deletions api/resource_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@ import (
"net/http"

"github.com/labstack/echo/v4"

"github.com/txsvc/stdlib/v2"

"github.com/txsvc/apikit/config"
"github.com/txsvc/apikit/helpers"
"github.com/txsvc/apikit/internal"
"github.com/txsvc/apikit/internal/auth"
"github.com/txsvc/apikit/internal/settings"
"github.com/txsvc/stdlib/v2"
)

const (
// auth routes
InitRoute = "/auth"
LoginRoute = "/auth/:sig/:token"
LogoutRoute = "/auth/:sig"

LoginExpiresAfter = 15
)

func WithAuthEndpoints(e *echo.Echo) *echo.Echo {
Expand All @@ -33,37 +36,44 @@ func WithAuthEndpoints(e *echo.Echo) *echo.Echo {
return e
}

func (c *Client) InitCommand(cfg *settings.Settings) error {
func (c *Client) InitCommand(cfg *settings.DialSettings) error {
_, err := c.POST(fmt.Sprintf("%s%s", NamespacePrefix, InitRoute), cfg, nil)
return err
}

func InitEndpoint(c echo.Context) error {
// get the payload
var cfg *settings.Settings = new(settings.Settings)
if err := c.Bind(cfg); err != nil {
var cfg_ *settings.DialSettings = new(settings.DialSettings)
if err := c.Bind(cfg_); err != nil {
return StandardResponse(c, http.StatusBadRequest, nil)
}

// pre-validate the request
if cfg.Credentials == nil || cfg.APIKey == "" {
if cfg_.Credentials == nil || cfg_.APIKey == "" {
return StandardResponse(c, http.StatusBadRequest, nil)
}
if cfg.Credentials.ProjectID == "" || cfg.Credentials.UserID == "" {
if cfg_.Credentials.ProjectID == "" || cfg_.Credentials.UserID == "" {
return StandardResponse(c, http.StatusBadRequest, nil)
}

// create a brand new instance so that the client can't sneak anything in we don't want
cfg := settings.DialSettings{
Credentials: cfg_.Credentials.Clone(),
DefaultScopes: config.GetConfig().GetScopes(),
}

// prepare the settings for registration
cfg.Credentials.Token = internal.CreateSimpleToken() // ignore anything that was provided
cfg.Credentials.Expires = stdlib.IncT(stdlib.Now(), 15) // FIXME: config, valid for 15min
cfg.Status = -2 // signals init
cfg.Credentials.Token = CreateSimpleToken() // ignore anything that was provided
cfg.Credentials.Expires = stdlib.IncT(stdlib.Now(), LoginExpiresAfter)
cfg.APIKey = cfg_.APIKey
cfg.Status = settings.StateInit // signals init

if err := auth.RegisterAuthorization(cfg); err != nil {
if err := auth.RegisterAuthorization(&cfg); err != nil {
return StandardResponse(c, http.StatusBadRequest, nil) // FIXME: or 409/Conflict ?
}

// all good so far, send the confirmation
err := helpers.MailgunSimpleEmail("[email protected]", cfg.Credentials.UserID, "auth", fmt.Sprintf("the token: %s\n", cfg.Credentials.Token))
err := helpers.MailgunSimpleEmail("[email protected]", cfg.Credentials.UserID, fmt.Sprintf("your api access credentials (%d)", stdlib.Now()), fmt.Sprintf("the token: %s\n", cfg.Credentials.Token))
if err != nil {
return StandardResponse(c, http.StatusBadRequest, nil)
}
Expand Down Expand Up @@ -93,24 +103,29 @@ func LoginEndpoint(c echo.Context) error {
}

// verify the request
_cfg, err := auth.LookupByToken(token)
if _cfg == nil && err != nil {
cfg_, err := auth.LookupByToken(token)
if cfg_ == nil && err != nil {
return ErrorResponse(c, http.StatusBadRequest, ErrInternalError, "token")
}
if _cfg == nil && err == nil {
if cfg_ == nil && err == nil {
return ErrorResponse(c, http.StatusBadRequest, config.ErrInitializingConfiguration, "not found") // simply not there ...
}

// compare provided signature with the expected signature
if sig != signature(_cfg.APIKey, _cfg.Credentials.Token) {
if sig != signature(cfg_.APIKey, cfg_.Credentials.Token) {
return ErrorResponse(c, http.StatusBadRequest, config.ErrInitializingConfiguration, "invalid sig")
}

// check if the token is still valid
if cfg_.Credentials.Expires < stdlib.Now() {
return ErrorResponse(c, http.StatusBadRequest, auth.ErrTokenExpired, "expired")
}

// everything checks out, create/register the real credentials now ...
cfg := _cfg.Clone() // clone, otherwise stupid things happen with pointers !
cfg := cfg_.Clone() // clone, otherwise stupid things happen with pointers !
cfg.Credentials.Expires = 0 // FIXME: really never ?
cfg.Credentials.Token = internal.CreateSimpleToken()
cfg.Status = 1 // FIXME: LOGGED_IN as const
cfg.Credentials.Token = CreateSimpleToken()
cfg.Status = settings.StateAuthorized

// FIXME: what about scopes ?

Expand Down Expand Up @@ -159,7 +174,7 @@ func LogoutEndpoint(c echo.Context) error {
}

// update the cache and store
cfg.Status = -1 // just set to invalid and expired
cfg.Status = settings.StateUndefined // just set to invalid and expired
cfg.Credentials.Expires = stdlib.Now() - 1
if err := auth.UpdateStore(cfg); err != nil {
return ErrorResponse(c, http.StatusBadRequest, err, "update store")
Expand All @@ -172,3 +187,8 @@ func LogoutEndpoint(c echo.Context) error {
func signature(apiKey, token string) string {
return stdlib.Fingerprint(fmt.Sprintf("%s%s", apiKey, token))
}

func CreateSimpleToken() string {
token, _ := stdlib.UUID()
return token
}
2 changes: 1 addition & 1 deletion app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func timeoutShutdown(ctx context.Context, a *App) error {
fmt.Println("blocking ...")
}

return nil
//return nil
}

func TestNewSimple(t *testing.T) {
Expand Down
8 changes: 7 additions & 1 deletion cli/cli.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package cli

import (
"errors"
"fmt"

"github.com/urfave/cli/v2"

"github.com/txsvc/apikit/config"
)

var (
// ErrInvalidNumArguments indicates that the number of arguments in a command is not valid
ErrInvalidNumArguments = errors.New("invalid number of arguments")
)

// NoOpCommand is just a placeholder
func NoOpCommand(c *cli.Context) error {
return cli.Exit(fmt.Sprintf("%s: command '%s' is not implemented", c.App.Name, c.Command.Name), 0)
Expand All @@ -18,7 +24,7 @@ func WithGlobalFlags() []cli.Flag {
&cli.StringFlag{
Name: "config",
Usage: "configuration and secrets directory",
DefaultText: config.DefaultConfigLocation(),
DefaultText: config.GetConfig().GetConfigLocation(),
Aliases: []string{"c"},
},
}
Expand Down
Loading

0 comments on commit 047aaf5

Please sign in to comment.