core

package
v0.0.0-...-b1b50a1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 17, 2020 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package core is the essential package required by all other packages. While all packages are, as much as possible, independent from each other, all of them depend on the core package.

Loggers are not in the core package only for "Separation of concerns" + "Code like a library". Exposing the logging library should not bring all the API related code with it.

Index

Constants

View Source
const (

	// APIv1 is the standardisation for first version of an API endpoint
	APIv1 string = "v1"
	// APIv2 is the standardisation for second version of an API endpoint
	APIv2 string = "v2"
	// APIMonolithic to enable monolithic mode
	APIMonolithic = true
	// APIMicroservice to enable microservice mode
	APIMicroservice = false
)
View Source
const (
	// TrackedCreatedBy is the createdBy key. TrackedEntity is assumed to in "bson:,inline"
	TrackedCreatedBy = "createdBy"
	// TrackedCreatedAt is the createdAt key. TrackedEntity is assumed to in "bson:,inline"
	TrackedCreatedAt = "createdAt"
	// TrackedUpdatedBy is the updatedBy key. TrackedEntity is assumed to in "bson:,inline"
	TrackedUpdatedBy = "updatedBy"
	// TrackedUpdatedAt is the updatedAt key. TrackedEntity is assumed to in "bson:,inline"
	TrackedUpdatedAt = "updatedAt"
)

Variables

View Source
var (

	// ClientDomain refers to the expected domain of the client application
	ClientDomain string
)

Functions

func AddCorsHeaders

func AddCorsHeaders(next http.Handler, corsConfig CorsConfig) http.Handler

AddCorsHeaders set the usual CORS headers.

It is a special middleware requiring parameters so it cannot use the standard adapter pattern

func AddJSONHeaders

func AddJSONHeaders(next http.Handler) http.Handler

AddJSONHeaders add the required header for accepting and providing JSON

func BuildJWT

func BuildJWT(claims JwtClaims) (string, error)

BuildJWT generate a JWT from a specific list of claims. List of claims is based on https://tools.ietf.org/html/rfc7519 found through https://auth0.com/docs/tokens/jwt-claims.

HMAC is chosen over RSA to protect against manipulation: https://security.stackexchange.com/a/220190

Generate Token : https://godoc.org/github.com/dgrijalva/jwt-go#example-New--Hmac Custom claims : https://godoc.org/github.com/dgrijalva/jwt-go#NewWithClaims

func DecodeJWT

func DecodeJWT(r *http.Request) (*JwtClaims, *ServiceMessage)

DecodeJWT extracts the claims from a JWT if it is valid. Parse token : https://godoc.org/github.com/dgrijalva/jwt-go#example-Parse--Hmac Custom claims : https://godoc.org/github.com/dgrijalva/jwt-go#ParseWithClaims

11-Apr-2020: Make authentication check 100% stateless for a microservice-ready architecture by removing the check in the database of the JWT status: a pure JWT is 100% stateless.

Returns: - JwtClaims : if token is present and valid - int : an `authStatus` code if some check already fails

func DoIfAccess

func DoIfAccess(canAccess AccessChecker, authenticatedHandler AuthenticatedHandler) http.Handler

DoIfAccess ensures that the provided accessChecker passes before proceeding to the authenticatedHandler.

Otherwise the request is rejected with the appropriate error code with error message

func GetVar

func GetVar(r *http.Request, varName string) string

GetVar fetch the variable defined in the route.

Such method can be framework-dependent.

func HandleServerError

func HandleServerError(w http.ResponseWriter, r *http.Request, err error)

HandleServerError is the generic way to handle server error: just send a 500 and the message with it

func LoggerInOutRequest

func LoggerInOutRequest(next http.Handler) http.Handler

LoggerInOutRequest displays information for inbound request and outbound result

func MongoConnectFromEnvVar

func MongoConnectFromEnvVar(envVarName string, logger logger.Logger) (*mongo.Client, *mongo.Database, error)

MongoConnectFromEnvVar connects to a Mongo database with the provided environment variable

This helper does not guarantee that dotenv files are properly loaded

func MongoConnectToDb

func MongoConnectToDb(mongoDbURI string, logger logger.Logger) (*mongo.Client, *mongo.Database, error)

MongoConnectToDb creates a Mongo client instance from an URI as well as the Mongo database instance depending on the database name in the URI

func MongoParseDbURI

func MongoParseDbURI(mongoDbURI string) (string, string)

MongoParseDbURI parse the DB URL. It is assumed that argument has the proper format such as mongodb://{user}:{passowrd}@{host}:{port}/{database}

return (connectionString, databaseName)

func SetupRouter

func SetupRouter(isMonolithic bool, apis ...*API) *mux.Router

SetupRouter loads all API

Types

type API

type API struct {
	// contains filtered or unexported fields
}

API exposes the list of handler with a specific URL root

Each APIHandler will be linked to the route URL. URL is defined by an urlBuilder

func NewAPI

func NewAPI(root string, logger logger.Logger) *API

NewAPI is the API constructor

- Allowed CORS headers and hosts are, for the moment, "*" - By default, the JSON middleware is always added

func (*API) AddMiddleware

func (api *API) AddMiddleware(mw EndpointAdapter)

AddMiddleware appends a middleware to a specific API.

There is no check about duplicates

func (*API) AddProtectedEndpoint

func (api *API) AddProtectedEndpoint(url string, httpMethod string, version string,
	accessChecker AccessChecker, handler AuthenticatedHandler)

AddProtectedEndpoint appends a handler which requires an AccessControl

AddHandler also checks if the provided httpMethod is valid.

func (*API) AddPublicEndpoint

func (api *API) AddPublicEndpoint(url string, httpMethod string, version string,
	publicHandler http.HandlerFunc)

AddPublicEndpoint adds a traditional HTTP handler without access check

func (*API) LoadInRouter

func (api *API) LoadInRouter(router *mux.Router)

LoadInRouter load all API handlers into the provided routing system.

This method aims at making the whole project framework-agnostic: if the routing framework change, only this method should change

The Middleware could have been added AFTER the different endpoints definition. Consequently, it is better to merge the middleware at the last minute, when loading into the router

This method also list all endpoints and the associated HTTP methods for CORS handling. Doing this way allows a single endpoint to support multiple HTTP method. However, each endpoint, given a method, still need CORS headers

type APIEndpoint

type APIEndpoint struct {
	// contains filtered or unexported fields
}

APIEndpoint maps a handler (authenticated or standard) with an URL pattern and a HTTP method

If both "publicHandler" and "protectedHandler" are defined, the public version takes precedence

type AccessChecker

type AccessChecker func(r *http.Request, jwtClaims JwtClaims) bool

AccessChecker ensures that the provided request is allowed to proceed or not.

Most of the checks are based on the token header. An AccessChecker always assumes that the JWT is properly formed, hence the jwtClaims argument. An AccessChecker's check success often leads to some AuthenticatedHandler to proceed.

var CheckIfAdmin AccessChecker = func(r *http.Request, claims JwtClaims) bool {
	return claims.IsAdmin
}

CheckIfAdmin simply checks if the JWT has admin privilege

var CheckIfLogged AccessChecker = func(r *http.Request, claims JwtClaims) bool {
	return claims.UserID != ""
}

CheckIfLogged is the AuthChecher ensuring the request has a properly logged-in user

var CheckPublic AccessChecker = func(r *http.Request, claims JwtClaims) bool {
	return true
}

CheckPublic always returns true to provided public access

type AuthenticatedHandler

type AuthenticatedHandler func(w http.ResponseWriter, r *http.Request, claims JwtClaims)

AuthenticatedHandler is meant to be the core logic of the handler with the user informations already extracted from the request.

claims are assumed to be always non-nil and always validated beforehand

type CorsConfig

type CorsConfig struct {
	Hosts   string
	Methods string
	Headers string
}

CorsConfig allows a flexible way to handle CORS stuff

type EndpointAdapter

type EndpointAdapter func(http.Handler) http.Handler

EndpointAdapter (or Decorator design pattern) wrapper consecutive middlewares.

EndpointAdapter must use the standard http.Handler method and, when authentication is required, another type of Adapter might be required

type JwtClaims

type JwtClaims struct {
	IsAdmin bool   `json:"isAdmin"`
	UserID  string `json:"userId"`
	jwt.StandardClaims
}

JwtClaims extends standard claims for our User model.

By including the IsAdmin and UserID fields, authorization check can be based on those values

type ServiceMessage

type ServiceMessage struct {
	Code       int    `json:"code"`            // Internal code: 0 is fine, any code different from 0 is an error
	Message    string `json:"message"`         // Explicit description
	HTTPStatus int    `json:"-"`               // HTTP Status code, skipped during serialisation
	Error      error  `json:"error,omitempty"` // Error if any
}

ServiceMessage is a token to forward the status of an action to the next function / whatever handler processing it.

While it is meant to standardize errors handling, it can also help to identify internal success status thanks to its code

func NewServiceErrorMessage

func NewServiceErrorMessage(err error) *ServiceMessage

NewServiceErrorMessage generate a ServiceMessage from an error. By default, status error is 500

func (*ServiceMessage) Write

func (msg *ServiceMessage) Write(rw http.ResponseWriter, req *http.Request)

Write writes the ServiceMessage into a Http.ReponseWriter and uses the incoming request for logging purpose only

type TrackedEntity

type TrackedEntity struct {
	CreatedBy primitive.ObjectID `json:"createdBy,omitempty" bson:"createdBy,omitempty"`
	CreatedAt time.Time          `json:"createdAt,omitempty" bson:"createdAt,omitempty"`
	UpdatedBy primitive.ObjectID `json:"updatedBy,omitempty" bson:"updatedBy,omitempty"`
	UpdatedAt time.Time          `json:"updatedAt,omitempty" bson:"updatedAt,omitempty"`
}

TrackedEntity is the basic structure for all entities which require tracking: user tracking and time tracking

User reference are `primitive.ObjectID` to match "primary keys" of the users collection

func (*TrackedEntity) Equals

func (t *TrackedEntity) Equals(t2 TrackedEntity) bool

Equals check the equality of each field and time fields are compared with a precision of one second

func (*TrackedEntity) PrepareForCreate

func (t *TrackedEntity) PrepareForCreate(claims JwtClaims)

PrepareForCreate set creation related fields

func (*TrackedEntity) PrepareForUpdate

func (t *TrackedEntity) PrepareForUpdate(claims JwtClaims)

PrepareForUpdate updates modification related field before any update based on the UserID provided by the claims

type URLBuilder

type URLBuilder func(root string, version string, url string) string

URLBuilder defines how an API generates a final endpoint URL given an (optional) root url, a version and the endpoint url

Root URL is optional for microservices-ready structure

var URLDefaultBuilder URLBuilder = func(root string, version string, url string) string {
	if root == "" {
		return fmt.Sprintf("/%s/%s", version, url)
	}

	return fmt.Sprintf("/%s/%s/%s", root, version, url)
}

URLDefaultBuilder concatenates "/{version}/{root}/{url}""

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL