Skip to content

Commit

Permalink
handlers: add cors support (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
labkode authored Feb 11, 2019
1 parent c9a5173 commit 07f2f98
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 52 deletions.
81 changes: 66 additions & 15 deletions cmd/revad/httpserver/httpserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"net/http"
"time"

"github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/auth"

"github.com/cernbox/reva/cmd/revad/svcs/httpsvcs"
httplog "github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/log"
"github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/trace"
Expand All @@ -19,6 +17,20 @@ import (
"github.com/prometheus/client_golang/prometheus"
)

// Middlewares contains all the registered middlwares.
var Middlewares = map[string]NewMiddleware{}

// NewMiddleware is the function that HTTP middlewares need to register at init time.
type NewMiddleware func(conf map[string]interface{}) (Middleware, error)

// RegisterMiddleware registers a new HTTP middleware and its new function.
func RegisterMiddleware(name string, newFunc NewMiddleware) {
Middlewares[name] = newFunc
}

// Middleware is a middleware http handler.
type Middleware func(h http.Handler) http.Handler

// Services is a map of service name and its new function.
var Services = map[string]NewService{}

Expand All @@ -37,19 +49,21 @@ var (
)

type config struct {
Network string `mapstructure:"network"`
Address string `mapstructure:"address"`
Services map[string]map[string]interface{} `mapstructure:"services"`
EnabledServices []string `mapstructure:"enabled_services"`
Handlers map[string]map[string]interface{} `mapstructure:"handlers"`
Network string `mapstructure:"network"`
Address string `mapstructure:"address"`
Services map[string]map[string]interface{} `mapstructure:"services"`
EnabledServices []string `mapstructure:"enabled_services"`
Middlewares map[string]map[string]interface{} `mapstructure:"middlewares"`
EnabledMiddlewares []string `mapstructure:"enabled_middlewares"`
}

// Server contains the server info.
type Server struct {
httpServer *http.Server
conf *config
listener net.Listener
svcs map[string]http.Handler
httpServer *http.Server
conf *config
listener net.Listener
svcs map[string]http.Handler
middlewares map[string]Middleware
}

// New returns a new server
Expand All @@ -74,6 +88,10 @@ func (s *Server) Start(ln net.Listener) error {
return err
}

if err := s.registerMiddlewares(); err != nil {
return err
}

s.httpServer.Handler = s.getHandler()
s.listener = ln

Expand Down Expand Up @@ -117,11 +135,32 @@ func (s *Server) isEnabled(svcName string) bool {
return false
}

func (s *Server) registerServices() error {
if err := auth.Register(s.conf.Handlers["auth"]); err != nil {
return err
func (s *Server) isMiddlewareEnabled(name string) bool {
for _, key := range s.conf.EnabledMiddlewares {
if key == name {
return true
}
}
return false
}

func (s *Server) registerMiddlewares() error {
middlewares := map[string]Middleware{}
for name, newFunc := range Middlewares {
if s.isMiddlewareEnabled(name) {
m, err := newFunc(s.conf.Middlewares[name])
if err != nil {
err = errors.Wrap(err, "error creating new middleware: "+name)
}
middlewares[name] = m
logger.Printf(ctx, "http middleware enabled: %s", name)
}
}
s.middlewares = middlewares
return nil
}

func (s *Server) registerServices() error {
svcs := map[string]http.Handler{}
for svcName, newFunc := range Services {
if s.isEnabled(svcName) {
Expand Down Expand Up @@ -150,5 +189,17 @@ func (s *Server) getHandler() http.Handler {
}
w.WriteHeader(http.StatusNotFound)
})
return trace.Handler(httplog.Handler(logger, h))

handler := http.Handler(h)

// chain the middlewares
// TODO(labkode): set registritation priority
for name, m := range s.middlewares {
logger.Println(ctx, "chainning middleware: "+name)
handler = m(handler)
}

// chain must-have middlewares.
return trace.Handler(httplog.Handler(logger, handler))

}
2 changes: 2 additions & 0 deletions cmd/revad/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
_ "github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/auth/credential/loader"
_ "github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/auth/token/loader"
_ "github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/auth/tokenwriter/loader"
_ "github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/loader"

_ "github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/loader"
_ "github.com/cernbox/reva/pkg/auth/manager/loader"
_ "github.com/cernbox/reva/pkg/storage/broker/loader"
Expand Down
7 changes: 5 additions & 2 deletions cmd/revad/revad.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ log_file = "stderr"
log_mode = "dev"
max_cpus = "32"


[http]
enabled_services = ["prometheussvc", "ocdavsvc", "webuisvc", "iframeuisvc"]
enabled_middlewares = ["cors"]
network = "tcp"
address = "0.0.0.0:9998"

[http.handlers.auth]
[http.middlewares.auth]
authsvc = "0.0.0.0:9999"
credential_strategy = "basic"
token_strategy = "header"
Expand All @@ -25,6 +25,8 @@ header = "X-Access-Token"
[http.handlers.auth.token_writers.header]
header = "X-Access-Token"

[http.middlewares.cors]

[http.services.prometheussvc]
prefix = "metrics"

Expand All @@ -38,6 +40,7 @@ prefix = "ui"
prefix = "owncloud"
chunk_folder = "/var/tmp/owncloud/chunks"
storageprovidersvc = "localhost:9999"
enable_cors = true

[grpc]
network = "tcp"
Expand Down
33 changes: 18 additions & 15 deletions cmd/revad/svcs/httpsvcs/handlers/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

authv0alphapb "github.com/cernbox/go-cs3apis/cs3/auth/v0alpha"
rpcpb "github.com/cernbox/go-cs3apis/cs3/rpc"
"github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers"
"github.com/cernbox/reva/cmd/revad/httpserver"
"github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/auth/credential/registry"
tokenregistry "github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/auth/token/registry"
tokenwriterregistry "github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/auth/tokenwriter/registry"
Expand All @@ -21,6 +21,10 @@ import (

var logger = log.New("auth")

func init() {
httpserver.RegisterMiddleware("auth", New)
}

type config struct {
AuthSVC string `mapstructure:"authsvc"`
CredentialStrategy string `mapstructure:"credential_strategy"`
Expand All @@ -33,51 +37,51 @@ type config struct {
TokenWriters map[string]map[string]interface{} `mapstructure:"token_writers"`
}

// Register registers an auth handler.
func Register(m map[string]interface{}) error {
// New creates a new auth middleware.
func New(m map[string]interface{}) (httpserver.Middleware, error) {
conf := &config{}
if err := mapstructure.Decode(m, conf); err != nil {
return err
return nil, err
}

f, ok := registry.NewCredentialFuncs[conf.CredentialStrategy]
if !ok {
return fmt.Errorf("credential strategy not found: %s", conf.CredentialStrategy)
return nil, fmt.Errorf("credential strategy not found: %s", conf.CredentialStrategy)
}

credStrategy, err := f(conf.CredentialStrategies[conf.CredentialStrategy])
if err != nil {
return err
return nil, err
}

g, ok := tokenregistry.NewTokenFuncs[conf.TokenStrategy]
if !ok {
return fmt.Errorf("token strategy not found: %s", conf.TokenStrategy)
return nil, fmt.Errorf("token strategy not found: %s", conf.TokenStrategy)
}

tokenStrategy, err := g(conf.TokenStrategies[conf.TokenStrategy])
if err != nil {
return err
return nil, err
}

h, ok := tokenmgr.NewFuncs[conf.TokenManager]
if !ok {
return fmt.Errorf("token manager not found: %s", conf.TokenStrategy)
return nil, fmt.Errorf("token manager not found: %s", conf.TokenStrategy)
}

tokenManager, err := h(conf.TokenManagers[conf.TokenManager])
if err != nil {
return err
return nil, err
}

i, ok := tokenwriterregistry.NewTokenFuncs[conf.TokenWriter]
if !ok {
return fmt.Errorf("token writer not found: %s", conf.TokenWriter)
return nil, fmt.Errorf("token writer not found: %s", conf.TokenWriter)
}

tokenWriter, err := i(conf.TokenWriters[conf.TokenWriter])
if err != nil {
return err
return nil, err
}

chain := func(h http.Handler) http.Handler {
Expand Down Expand Up @@ -144,11 +148,10 @@ func Register(m map[string]interface{}) error {
h.ServeHTTP(w, r)
})
}

handlers.Register("auth", chain)
return nil
return chain, nil
}

// TODO(labkode): re-use connection using mutex.
func getAuthClient(host string) (authv0alphapb.AuthServiceClient, error) {
conn, err := getConn(host)
if err != nil {
Expand Down
45 changes: 45 additions & 0 deletions cmd/revad/svcs/httpsvcs/handlers/cors/cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cors

import (
"github.com/cernbox/reva/pkg/log"

"github.com/cernbox/reva/cmd/revad/httpserver"
"github.com/mitchellh/mapstructure"
"github.com/rs/cors"
)

func init() {
httpserver.RegisterMiddleware("cors", New)
}

var logger = log.New("cors")

type config struct {
AllowedOrigins []string `mapstructure:"allowed_origins"`
AllowCredentials bool `mapstructure:"allow_credentials"`
AllowedMethods []string `mapstructure:"allowed_methods"`
AllowedHeaders []string `mapstructure:"allowed_headers"`
ExposedHeaders []string `mapstructure:"exposed_headers"`
MaxAge int `mapstructure:"max_age"`
OptionsPassthrough bool `mapstructure:"options_passthrough"`
}

// New creates a new CORS middleware.
func New(m map[string]interface{}) (httpserver.Middleware, error) {
conf := &config{}
if err := mapstructure.Decode(m, conf); err != nil {
return nil, err
}

c := cors.New(cors.Options{
AllowCredentials: conf.AllowCredentials,
AllowedHeaders: conf.AllowedHeaders,
AllowedMethods: conf.AllowedMethods,
AllowedOrigins: conf.AllowedOrigins,
ExposedHeaders: conf.ExposedHeaders,
MaxAge: conf.MaxAge,
OptionsPassthrough: conf.OptionsPassthrough,
})

return c.Handler, nil
}
15 changes: 0 additions & 15 deletions cmd/revad/svcs/httpsvcs/handlers/handlers.go
Original file line number Diff line number Diff line change
@@ -1,16 +1 @@
package handlers

import (
"net/http"
)

// Handlers is a map of all registered handlers to be used from http services.
var Handlers = map[string]HandlerChain{}

// HandlerChain is the the type that http handlers need to register.
type HandlerChain func(http.Handler) http.Handler

// Register register a handler chain.
func Register(name string, chain HandlerChain) {
Handlers[name] = chain
}
8 changes: 8 additions & 0 deletions cmd/revad/svcs/httpsvcs/handlers/loader/loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package loader

import (
// Load core HTTP middlewares.
_ "github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/auth"
_ "github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers/cors"
// Add your own middlware.
)
7 changes: 2 additions & 5 deletions cmd/revad/svcs/httpsvcs/webuisvc/webuisvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

"github.com/cernbox/reva/cmd/revad/httpserver"
"github.com/cernbox/reva/cmd/revad/svcs/httpsvcs"
"github.com/cernbox/reva/cmd/revad/svcs/httpsvcs/handlers"
"github.com/mitchellh/mapstructure"
)

Expand Down Expand Up @@ -41,9 +40,7 @@ func (s *svc) Handler() http.Handler {
}

func getHandler() http.Handler {
// protect with auth
chain := handlers.Handlers["auth"]
return chain(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
html := `
<!DOCTYPE html>
<html>
Expand All @@ -55,5 +52,5 @@ func getHandler() http.Handler {
</html>
`
w.Write([]byte(html))
}))
})
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require (
github.com/pkg/errors v0.8.1
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/prometheus/client_golang v0.9.2
github.com/rs/cors v1.6.0
github.com/rs/zerolog v1.11.0
github.com/spf13/viper v1.3.1
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jO
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/zerolog v1.11.0 h1:DRuq/S+4k52uJzBQciUcofXx45GrMC6yrEbb/CoK6+M=
github.com/rs/zerolog v1.11.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
Expand Down

0 comments on commit 07f2f98

Please sign in to comment.