add middleware (same principle as types) + scope check in progress

This commit is contained in:
Adrien Marquès 2018-07-07 22:10:56 +02:00
parent 8dc2de5e5d
commit b39dff10eb
5 changed files with 195 additions and 20 deletions

View File

@ -1 +1,17 @@
package config
import (
"fmt"
"git.xdrm.io/go/aicra/middleware"
)
// CheckScope returns whether a given scope matches the
// method configuration
//
// format is: [ [a,b], [c], [d,e] ]
// > level 1 is OR
// > level 2 is AND
func (m *Method) CheckScope(scope middleware.Scope) bool {
fmt.Printf("Scope: %v\n", m.Permission)
return false
}

110
middleware/public.go Normal file
View File

@ -0,0 +1,110 @@
package middleware
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"plugin"
"strings"
)
// CreateRegistry creates an empty middleware registry
// - if loadDir is set -> load all available middlewares
// inside the local ./middleware folder
func CreateRegistry(loadDir ...string) *MiddlewareRegistry {
/* (1) Create registry */
reg := &MiddlewareRegistry{
Middlewares: make([]MiddleWare, 0),
}
/* (2) If no default to use -> empty registry */
if len(loadDir) < 1 {
return reg
}
/* (3) List types */
plugins, err := ioutil.ReadDir(loadDir[0])
if err != nil {
log.Fatal(err)
}
/* (4) Else try to load each given default */
for _, file := range plugins {
// ignore non .so files
if !strings.HasSuffix(file.Name(), ".so") {
continue
}
err := reg.Add(file.Name())
if err != nil {
log.Fatalf("Cannot load plugin '%s'", file.Name())
}
}
return reg
}
// Add adds a middleware to the registry; it must be a
// valid and existing plugin name with or without the .so extension
// it must be located in the relative directory .build/middleware
func (tr *MiddlewareRegistry) Add(pluginName string) error {
/* (1) Check plugin name */
if len(pluginName) < 1 {
return fmt.Errorf("Plugin name must not be empty")
}
/* (2) Check if valid plugin name */
if strings.ContainsAny(pluginName, "/") {
return fmt.Errorf("'%s' can only be a name, not a path", pluginName)
}
/* (3) Check plugin extension */
if !strings.HasSuffix(pluginName, ".so") {
pluginName = fmt.Sprintf("%s.so", pluginName)
}
/* (4) Try to load the plugin */
p, err := plugin.Open(fmt.Sprintf(".build/middleware/%s", pluginName))
if err != nil {
return err
}
/* (5) Export wanted properties */
inspect, err := p.Lookup("Inspect")
if err != nil {
return fmt.Errorf("Missing method 'Inspect()'; %s", err)
}
/* (6) Cast Inspect */
inspectCast, ok := inspect.(func(http.Request, Scope))
if !ok {
return fmt.Errorf("Inspect() is malformed")
}
/* (7) Add type to registry */
tr.Middlewares = append(tr.Middlewares, MiddleWare{
Inspect: inspectCast,
})
return nil
}
// Runs all middlewares (default browse order)
func (mr MiddlewareRegistry) Run(req http.Request) Scope {
/* (1) Initialise scope */
scope := Scope{}
/* (2) Execute each middleware */
for _, m := range mr.Middlewares {
m.Inspect(req, scope)
}
return scope
}

26
middleware/types.go Normal file
View File

@ -0,0 +1,26 @@
package middleware
import (
"net/http"
)
// Scope represents a list of scope processed by middlewares
// and used by the router to block/allow some uris
// it is also passed to controllers
type Scope []string
// Inspector updates the @Scope passed to it according to
// the @http.Request
type Inspector func(http.Request, Scope)
// Middleware contains all necessary methods
// for a Middleware provided by user/developer
type MiddleWare struct {
Inspect func(http.Request, Scope)
}
// MiddlewareRegistry represents a registry containing all registered
// middlewares to be processed before routing any request
type MiddlewareRegistry struct {
Middlewares []MiddleWare
}

View File

@ -6,16 +6,15 @@ import (
"git.xdrm.io/go/aicra/config"
e "git.xdrm.io/go/aicra/err"
"git.xdrm.io/go/aicra/implement"
"git.xdrm.io/go/aicra/middleware"
"git.xdrm.io/go/aicra/request"
"log"
"net/http"
)
// Init initilises a new framework instance
// - path is the configuration path
// - if typeChecker is nil, defaults will be used (all *.so files
// inside ./.build/types local directory)
func Init(path string, typeChecker ...*checker.TypeRegistry) (*Server, error) {
// - path is the configuration file
func New(path string) (*Server, error) {
/* (1) Init instance */
inst := &Server{
@ -30,20 +29,30 @@ func Init(path string, typeChecker ...*checker.TypeRegistry) (*Server, error) {
}
inst.config = config
/* (3) Store registry if given */
if len(typeChecker) > 0 && typeChecker[0] != nil {
inst.Checker = typeChecker[0]
/* (3) Default type registry */
inst.SetTypeFolder(".build/type")
/* (4) Default middleware registry */
inst.SetMiddlewareFolder(".build/middleware")
return inst, nil
}
/* (4) Default registry creation */
inst.Checker = checker.CreateRegistry(".build/type")
// Create the type (checker) registry from
// a given folder
func (s *Server) SetTypeFolder(path string) {
s.Checker = checker.CreateRegistry(path)
}
return inst, nil
// Create the middleware registry from
// a given folder
func (s *Server) SetMiddlewareFolder(path string) {
s.Middleware = middleware.CreateRegistry(path)
}
// Listens and binds the server to the given port
func (s *Server) Launch(port uint16) error {
func (s *Server) Listen(port uint16) error {
/* (1) Bind router */
http.HandleFunc("/", s.routeRequest)
@ -63,10 +72,13 @@ func (s *Server) routeRequest(res http.ResponseWriter, httpReq *http.Request) {
}
/* (2) Middleware: authentication */
// TODO: Auth
scope := s.Middleware.Run(*httpReq)
/* (3) Find a matching controller */
controller := s.findController(req)
if controller == nil {
return
}
/* (4) Check if matching method exists */
var method = controller.Method(httpReq.Method)
@ -76,6 +88,12 @@ func (s *Server) routeRequest(res http.ResponseWriter, httpReq *http.Request) {
return
}
/* (5) Check scope permissions */
if !method.CheckScope(scope) {
httpError(res, e.Permission)
return
}
/* (4) Check parameters
---------------------------------------------------------*/
parameters, paramError := s.ExtractParameters(req, method.Parameters)
@ -96,16 +114,19 @@ func (s *Server) routeRequest(res http.ResponseWriter, httpReq *http.Request) {
/* (6) Execute and get response
---------------------------------------------------------*/
/* (1) Show Authorization header into controller */
/* (1) Give Authorization header into controller */
authHeader := httpReq.Header.Get("Authorization")
if len(authHeader) > 0 {
parameters["_AUTHORIZATION_"] = authHeader
}
/* (2) Execute */
/* (2) Give Scope into controller */
parameters["_SCOPE_"] = scope
/* (3) Execute */
response := callable(parameters, implement.NewResponse())
/* (3) Extract http headers */
/* (4) Extract http headers */
for k, v := range response.Dump() {
if k == "_REDIRECT_" {
if newLocation, ok := v.(string); ok {
@ -115,7 +136,7 @@ func (s *Server) routeRequest(res http.ResponseWriter, httpReq *http.Request) {
}
}
/* (4) Build JSON response */
/* (5) Build JSON response */
httpPrint(res, response)
return

View File

@ -3,10 +3,12 @@ package aicra
import (
"git.xdrm.io/go/aicra/checker"
"git.xdrm.io/go/aicra/config"
"git.xdrm.io/go/aicra/middleware"
)
type Server struct {
config *config.Controller
Params map[string]interface{}
Checker *checker.TypeRegistry // type check
Middleware *middleware.MiddlewareRegistry // middlewares
}