Refactor JWT implementation
This commit is contained in:
parent
2e0adf43b7
commit
59ec77e204
@ -5,11 +5,13 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/auth/settings"
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/cortezaproject/corteza-server/pkg/plugin"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-oauth2/oauth2/v4"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
@ -62,6 +64,10 @@ type (
|
||||
// CLI Commands
|
||||
Command *cobra.Command
|
||||
|
||||
jwt auth.MiddlewareValidator
|
||||
|
||||
oa2m oauth2.Manager
|
||||
|
||||
// Servers
|
||||
HttpServer httpApiServer
|
||||
GrpcServer grpcServer
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
|
||||
authService "github.com/cortezaproject/corteza-server/auth"
|
||||
authHandlers "github.com/cortezaproject/corteza-server/auth/handlers"
|
||||
"github.com/cortezaproject/corteza-server/auth/oauth2"
|
||||
"github.com/cortezaproject/corteza-server/auth/saml"
|
||||
authSettings "github.com/cortezaproject/corteza-server/auth/settings"
|
||||
autService "github.com/cortezaproject/corteza-server/automation/service"
|
||||
@ -120,13 +121,6 @@ func (app *CortezaApp) Setup() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// set base path for links&routes in auth server
|
||||
authHandlers.BasePath = app.Opt.HTTPServer.BaseUrl
|
||||
|
||||
if err = auth.SetupDefault(app.Opt.Auth.Secret, app.Opt.Auth.Expiry); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
http.SetupDefaults(
|
||||
app.Opt.HTTPClient.HttpClientTimeout,
|
||||
app.Opt.HTTPClient.ClientTSLInsecure,
|
||||
@ -323,6 +317,24 @@ func (app *CortezaApp) InitServices(ctx context.Context) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
{
|
||||
app.oa2m = oauth2.NewManager(
|
||||
app.Opt.Auth,
|
||||
app.Log,
|
||||
&oauth2.ContextClientStore{},
|
||||
&oauth2.CortezaTokenStore{Store: app.Store},
|
||||
)
|
||||
|
||||
// set base path for links&routes in auth server
|
||||
authHandlers.BasePath = app.Opt.HTTPServer.BaseUrl
|
||||
|
||||
if err = auth.SetupDefault(app.oa2m, app.Opt.Auth.Secret, app.Opt.Auth.Expiry); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
app.jwt = auth.JWT()
|
||||
}
|
||||
|
||||
app.WsServer = websocket.Server(app.Log, app.Opt.Websocket)
|
||||
|
||||
ctx = actionlog.RequestOriginToContext(ctx, actionlog.RequestOrigin_APP_Init)
|
||||
@ -402,7 +414,8 @@ func (app *CortezaApp) InitServices(ctx context.Context) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
auth.SetJWTStore(app.Store)
|
||||
//@todo remove vv
|
||||
//auth.SetJWTStore(app.Store)
|
||||
|
||||
corredor.Service().SetUserFinder(sysService.DefaultUser)
|
||||
corredor.Service().SetRoleFinder(sysService.DefaultRole)
|
||||
@ -498,7 +511,7 @@ func (app *CortezaApp) Activate(ctx context.Context) (err error) {
|
||||
|
||||
updateSmtpSettings(app.Log, sysService.CurrentSettings)
|
||||
|
||||
if app.AuthService, err = authService.New(ctx, app.Log, app.Store, app.Opt.Auth); err != nil {
|
||||
if app.AuthService, err = authService.New(ctx, app.Log, app.oa2m, app.Store, app.Opt.Auth); err != nil {
|
||||
return fmt.Errorf("failed to init auth service: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@ -95,13 +95,13 @@ func (app *CortezaApp) mountHttpRoutes(r chi.Router) {
|
||||
zap.String("baseUrl", fullpathAPI),
|
||||
)
|
||||
|
||||
r.Route("/system", systemRest.MountRoutes)
|
||||
r.Route("/automation", automationRest.MountRoutes)
|
||||
r.Route("/compose", composeRest.MountRoutes)
|
||||
r.Route("/system", systemRest.MountRoutes(app.jwt))
|
||||
r.Route("/automation", automationRest.MountRoutes(app.jwt))
|
||||
r.Route("/compose", composeRest.MountRoutes(app.jwt))
|
||||
r.Route("/websocket", app.WsServer.MountRoutes)
|
||||
|
||||
if app.Opt.Federation.Enabled {
|
||||
r.Route("/federation", federationRest.MountRoutes)
|
||||
r.Route("/federation", federationRest.MountRoutes(app.jwt))
|
||||
}
|
||||
|
||||
var fullpathDocs = options.CleanBase(ho.BaseUrl, ho.ApiBaseUrl, "docs")
|
||||
|
||||
11
auth/auth.go
11
auth/auth.go
@ -51,7 +51,7 @@ type (
|
||||
var PublicAssets embed.FS
|
||||
|
||||
// New initializes Auth service that orchestrates session manager, oauth2 manager and http request handlers
|
||||
func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthOpt) (svc *service, err error) {
|
||||
func New(ctx context.Context, log *zap.Logger, oa2m oauth2def.Manager, s store.Storer, opt options.AuthOpt) (svc *service, err error) {
|
||||
var (
|
||||
tpls templateExecutor
|
||||
defClient *types.AuthClient
|
||||
@ -73,14 +73,7 @@ func New(ctx context.Context, log *zap.Logger, s store.Storer, opt options.AuthO
|
||||
|
||||
sesManager := request.NewSessionManager(s, opt, log)
|
||||
|
||||
oauth2Manager := oauth2.NewManager(
|
||||
opt,
|
||||
log,
|
||||
&oauth2.ContextClientStore{},
|
||||
&oauth2.CortezaTokenStore{Store: s},
|
||||
)
|
||||
|
||||
oauth2Server := oauth2.NewServer(oauth2Manager)
|
||||
oauth2Server := oauth2.NewServer(oa2m)
|
||||
|
||||
// Called after oauth2 authorization request is validated
|
||||
// We'll try to get valid user out of the session or redirect user to login page
|
||||
|
||||
@ -91,9 +91,9 @@ func Command(ctx context.Context, app serviceInitializer, storeInit func(ctx con
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ctx = auth.SetIdentityToContext(ctx, auth.ServiceUser())
|
||||
var (
|
||||
at []byte
|
||||
user *types.User
|
||||
err error
|
||||
signedToken []byte
|
||||
user *types.User
|
||||
err error
|
||||
|
||||
userStr = args[0]
|
||||
)
|
||||
@ -104,15 +104,15 @@ func Command(ctx context.Context, app serviceInitializer, storeInit func(ctx con
|
||||
err = service.DefaultAuth.LoadRoleMemberships(ctx, user)
|
||||
cli.HandleError(err)
|
||||
|
||||
at, err = auth.DefaultJwtHandler.Generate(ctx, user, 0)
|
||||
signedToken, err = auth.JWT().Generate(ctx, user, 0, "api", "profile")
|
||||
cli.HandleError(err)
|
||||
cmd.Println(string(at))
|
||||
cmd.Println(string(signedToken))
|
||||
},
|
||||
}
|
||||
|
||||
testEmails := &cobra.Command{
|
||||
Use: "test-notifications [recipient]",
|
||||
Short: "Sends samples of all authentication notification to receipient",
|
||||
Short: "Sends samples of all authentication notification to recipient",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRunE: commandPreRunInitService(app),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
@ -49,8 +49,7 @@ func (h AuthHandlers) oauth2Authorize(req *request.AuthReq) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// add client to context so we can reach it from client store via context.Value() fn
|
||||
//
|
||||
// add client to context, now we can reach it from client store via context.Value() fn
|
||||
// this way we work around the limitations we have with the oauth2 lib.
|
||||
ctx = context.WithValue(req.Context(), &oauth2.ContextClientStore{}, client)
|
||||
|
||||
@ -60,7 +59,7 @@ func (h AuthHandlers) oauth2Authorize(req *request.AuthReq) (err error) {
|
||||
request.SetOauth2Client(req.Session, client)
|
||||
}
|
||||
|
||||
// set to -1 to make sure that wrapping request handler
|
||||
// set to -1 to make sure wrapping request handler
|
||||
// does not send status code!
|
||||
req.Status = -1
|
||||
|
||||
|
||||
@ -3,8 +3,8 @@ package handlers
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/auth/request"
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/locale"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/httprate"
|
||||
@ -36,7 +36,7 @@ func (h *AuthHandlers) MountHttpRoutes(r chi.Router) {
|
||||
r.Use(httprate.LimitByIP(h.Opt.RequestRateLimit, h.Opt.RequestRateWindowLength))
|
||||
}
|
||||
|
||||
r.Use(request.ExtraReqInfoMiddleware)
|
||||
r.Use(auth.ExtraReqInfoMiddleware)
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
// all routes protected with CSRF:
|
||||
|
||||
62
auth/oauth2/access_token.go
Normal file
62
auth/oauth2/access_token.go
Normal file
@ -0,0 +1,62 @@
|
||||
package oauth2
|
||||
|
||||
//import (
|
||||
// "context"
|
||||
// "strings"
|
||||
//
|
||||
// "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
// "github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
// "github.com/cortezaproject/corteza-server/pkg/rand"
|
||||
// "github.com/go-oauth2/oauth2/v4"
|
||||
// "github.com/spf13/cast"
|
||||
//)
|
||||
//
|
||||
//// JWTAccessGenerate generate the jwt access token
|
||||
//type (
|
||||
// tokenGenerator interface {
|
||||
// Generate(ctx context.Context, i auth.Identifiable, clientID uint64, scope ...string) (token []byte, err error)
|
||||
// }
|
||||
//
|
||||
// JWTAccessGenerate struct {
|
||||
// tm tokenGenerator
|
||||
// }
|
||||
//)
|
||||
//
|
||||
//// NewJWTAccessGenerate create to generate the jwt access token instance
|
||||
////
|
||||
//// @todo move this to pkg/auth (??) so it can be re-used
|
||||
//func NewJWTAccessGenerate(tg tokenGenerator) *JWTAccessGenerate {
|
||||
// return &JWTAccessGenerate{tg}
|
||||
//}
|
||||
//
|
||||
//// Token based on the UUID generated token
|
||||
//func (a *JWTAccessGenerate) Token(ctx context.Context, data *oauth2.GenerateBasic, isGenRefresh bool) (_ string, refresh string, err error) {
|
||||
// var (
|
||||
// user auth.Identifiable
|
||||
// rawToken []byte
|
||||
// )
|
||||
//
|
||||
// {
|
||||
// // extract user ID and roles from a space-delimited list of IDs stored in userID
|
||||
// userIdWithRoles := strings.Split(data.TokenInfo.GetUserID(), " ")
|
||||
// if len(userIdWithRoles) == 1 {
|
||||
// user = auth.Authenticated(cast.ToUint64(userIdWithRoles[0]))
|
||||
// } else {
|
||||
// user = auth.Authenticated(
|
||||
// cast.ToUint64(userIdWithRoles[0]),
|
||||
// payload.ParseUint64s(userIdWithRoles)...,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// rawToken, err = a.tm.Generate(ctx, user, cast.ToUint64(data.Client.GetID()), data.TokenInfo.GetScope())
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if isGenRefresh {
|
||||
// refresh = string(rand.Bytes(48))
|
||||
// }
|
||||
//
|
||||
// return string(rawToken), refresh, nil
|
||||
//}
|
||||
@ -19,7 +19,8 @@ var _ oauth2.ClientStore = &ContextClientStore{}
|
||||
//
|
||||
// This requires that client is put in context before oauth2 procedures are executed!
|
||||
func (s ContextClientStore) GetByID(ctx context.Context, id string) (oauth2.ClientInfo, error) {
|
||||
return &clientInfo{ctx.Value(&ContextClientStore{}).(*types.AuthClient)}, nil
|
||||
return &clientInfo{&types.AuthClient{}}, nil
|
||||
//return &clientInfo{ctx.Value(&ContextClientStore{}).(*types.AuthClient)}, nil
|
||||
}
|
||||
|
||||
type (
|
||||
|
||||
@ -7,7 +7,6 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/auth/request"
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/id"
|
||||
@ -44,23 +43,36 @@ var (
|
||||
|
||||
func (c CortezaTokenStore) Create(ctx context.Context, info oauth2.TokenInfo) (err error) {
|
||||
var (
|
||||
eti = request.GetExtraReqInfoFromContext(ctx)
|
||||
oa2t = &types.AuthOa2token{
|
||||
ID: nextID(),
|
||||
CreatedAt: *now(),
|
||||
RemoteAddr: eti.RemoteAddr,
|
||||
UserAgent: eti.UserAgent,
|
||||
}
|
||||
oa2t *types.AuthOa2token
|
||||
acc *types.AuthConfirmedClient
|
||||
|
||||
acc = &types.AuthConfirmedClient{
|
||||
ConfirmedAt: oa2t.CreatedAt,
|
||||
}
|
||||
userID uint64
|
||||
clientID uint64
|
||||
|
||||
jwtID = id.Next()
|
||||
)
|
||||
|
||||
if clientID, err = strconv.ParseUint(info.GetClientID(), 10, 64); err != nil {
|
||||
return fmt.Errorf("could not parse client ID from token info: %w", err)
|
||||
}
|
||||
|
||||
if userID, _ = auth.ExtractFromSubClaim(info.GetUserID()); userID == 0 {
|
||||
return fmt.Errorf("could not parse user ID from token info")
|
||||
}
|
||||
|
||||
// Make oauth2 token and auth confirmation structs from user and client IDs
|
||||
if oa2t, acc, err = makeAuthStructs(ctx, jwtID, userID, clientID, info, info.GetCodeExpiresIn()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// this is oauth2 specific go-code and there is no
|
||||
// need for it to be moved to MakeAuthStructs fn in auth pkg
|
||||
if code := info.GetCode(); code != "" {
|
||||
oa2t.Code = code
|
||||
oa2t.ExpiresAt = info.GetCodeCreateAt().Add(info.GetCodeExpiresIn())
|
||||
} else {
|
||||
// When creating non-access-code tokens,
|
||||
// we need to overwrite expiration time
|
||||
// with custom values for access or refresh token
|
||||
oa2t.Access = info.GetAccess()
|
||||
oa2t.ExpiresAt = info.GetAccessCreateAt().Add(info.GetAccessExpiresIn())
|
||||
|
||||
@ -70,32 +82,28 @@ func (c CortezaTokenStore) Create(ctx context.Context, info oauth2.TokenInfo) (e
|
||||
}
|
||||
}
|
||||
|
||||
if oa2t.Data, err = json.Marshal(info); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if oa2t.ClientID, err = strconv.ParseUint(info.GetClientID(), 10, 64); err != nil {
|
||||
return fmt.Errorf("could not parse client ID from token info: %w", err)
|
||||
}
|
||||
|
||||
// copy client id to auth client confirmation
|
||||
acc.ClientID = oa2t.ClientID
|
||||
|
||||
if info.GetUserID() != "" {
|
||||
if oa2t.UserID, _ = auth.ExtractFromSubClaim(info.GetUserID()); oa2t.UserID == 0 {
|
||||
// UserID stores collection of IDs: user's ID and set of all roles user is member of
|
||||
return fmt.Errorf("could not parse user ID from token info")
|
||||
}
|
||||
}
|
||||
|
||||
// copy user id to auth client confirmation
|
||||
acc.UserID = oa2t.UserID
|
||||
//if oa2t.ClientID, err = strconv.ParseUint(info.GetClientID(), 10, 64); err != nil {
|
||||
// return fmt.Errorf("could not parse client ID from token info: %w", err)
|
||||
//}
|
||||
|
||||
//if info.GetUserID() != "" {
|
||||
// if oa2t.UserID, _ = auth.ExtractFromSubClaim(info.GetUserID()); oa2t.UserID == 0 {
|
||||
// // UserID stores collection of IDs: user's ID and set of all roles user is member of
|
||||
// return fmt.Errorf("could not parse user ID from token info")
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// copy user id to auth client confirmation
|
||||
//acc.UserID = oa2t.UserID
|
||||
if err = store.UpsertAuthConfirmedClient(ctx, c.Store, acc); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return store.CreateAuthOa2token(ctx, c.Store, oa2t)
|
||||
if err = store.CreateAuthOa2token(ctx, c.Store, oa2t); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c CortezaTokenStore) RemoveByCode(ctx context.Context, code string) error {
|
||||
@ -115,8 +123,8 @@ func (c CortezaTokenStore) GetByCode(ctx context.Context, code string) (oauth2.T
|
||||
internal = &oauth2models.Token{}
|
||||
t, err = store.LookupAuthOa2tokenByCode(ctx, c.Store, code)
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil, oauth2errors.ErrInvalidAuthorizeCode
|
||||
}
|
||||
@ -132,6 +140,7 @@ func (c CortezaTokenStore) GetByAccess(ctx context.Context, access string) (oaut
|
||||
internal = &oauth2models.Token{}
|
||||
t, err = store.LookupAuthOa2tokenByAccess(ctx, c.Store, access)
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get access token: %w", err)
|
||||
}
|
||||
@ -144,6 +153,7 @@ func (c CortezaTokenStore) GetByRefresh(ctx context.Context, refresh string) (oa
|
||||
internal = &oauth2models.Token{}
|
||||
t, err = store.LookupAuthOa2tokenByRefresh(ctx, c.Store, refresh)
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
return nil, oauth2errors.ErrInvalidRefreshToken
|
||||
@ -158,3 +168,32 @@ func (c CortezaTokenStore) GetByRefresh(ctx context.Context, refresh string) (oa
|
||||
|
||||
return internal, t.Data.Unmarshal(internal)
|
||||
}
|
||||
|
||||
func makeAuthStructs(ctx context.Context, jwtID, userID, clientID uint64, data oauth2.TokenInfo, expiresAt time.Duration) (oa2t *types.AuthOa2token, acc *types.AuthConfirmedClient, err error) {
|
||||
var (
|
||||
eti = auth.GetExtraReqInfoFromContext(ctx)
|
||||
createdAt = time.Now().Round(time.Second)
|
||||
)
|
||||
|
||||
oa2t = &types.AuthOa2token{
|
||||
ID: jwtID,
|
||||
CreatedAt: createdAt,
|
||||
RemoteAddr: eti.RemoteAddr,
|
||||
UserAgent: eti.UserAgent,
|
||||
ClientID: clientID,
|
||||
UserID: userID,
|
||||
ExpiresAt: createdAt.Add(expiresAt),
|
||||
}
|
||||
|
||||
acc = &types.AuthConfirmedClient{
|
||||
ClientID: clientID,
|
||||
UserID: userID,
|
||||
ConfirmedAt: createdAt,
|
||||
}
|
||||
|
||||
if oa2t.Data, err = json.Marshal(data); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rand"
|
||||
"github.com/go-oauth2/oauth2/v4"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// JWTAccessGenerate generate the jwt access token
|
||||
type (
|
||||
JWTAccessGenerate struct {
|
||||
tm auth.TokenGenerator
|
||||
}
|
||||
)
|
||||
|
||||
// NewJWTAccessGenerate create to generate the jwt access token instance
|
||||
//
|
||||
// @todo move this to pkg/auth (??) so it can be re-used
|
||||
func NewJWTAccessGenerate(tg auth.TokenGenerator) *JWTAccessGenerate {
|
||||
return &JWTAccessGenerate{tg}
|
||||
}
|
||||
|
||||
// Token based on the UUID generated token
|
||||
func (a *JWTAccessGenerate) Token(_ context.Context, data *oauth2.GenerateBasic, isGenRefresh bool) (_ string, refresh string, err error) {
|
||||
var (
|
||||
user auth.Identifiable
|
||||
rawToken []byte
|
||||
)
|
||||
|
||||
{
|
||||
// extract user ID and roles from a space-delimited list of IDs stored in userID
|
||||
userIdWithRoles := strings.Split(data.TokenInfo.GetUserID(), " ")
|
||||
if len(userIdWithRoles) == 1 {
|
||||
user = auth.Authenticated(cast.ToUint64(userIdWithRoles[0]))
|
||||
} else {
|
||||
user = auth.Authenticated(
|
||||
cast.ToUint64(userIdWithRoles[0]),
|
||||
payload.ParseUint64s(userIdWithRoles)...,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
rawToken, err = a.tm.Encode(user, cast.ToUint64(data.Client.GetID()), data.TokenInfo.GetScope())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if isGenRefresh {
|
||||
refresh = string(rand.Bytes(48))
|
||||
}
|
||||
|
||||
return string(rawToken), refresh, nil
|
||||
}
|
||||
@ -3,7 +3,6 @@ package oauth2
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/go-oauth2/oauth2/v4"
|
||||
@ -30,9 +29,6 @@ func NewManager(opt options.AuthOpt, log *zap.Logger, cs oauth2.ClientStore, ts
|
||||
|
||||
// token store
|
||||
manager.MapTokenStorage(ts)
|
||||
|
||||
// generate jwt access token
|
||||
manager.MapAccessGenerate(NewJWTAccessGenerate(auth.DefaultJwtHandler))
|
||||
manager.MapClientStorage(cs)
|
||||
|
||||
manager.SetValidateURIHandler(func(baseURI, redirectURI string) (err error) {
|
||||
@ -67,7 +63,7 @@ func NewManager(opt options.AuthOpt, log *zap.Logger, cs oauth2.ClientStore, ts
|
||||
return manager
|
||||
}
|
||||
|
||||
func NewServer(manager *manage.Manager) *server.Server {
|
||||
func NewServer(manager oauth2.Manager) *server.Server {
|
||||
srv := server.NewServer(&server.Config{
|
||||
TokenType: "Bearer",
|
||||
AllowGetAccessRequest: false,
|
||||
|
||||
@ -134,21 +134,3 @@ func (req *AuthReq) PopKV() map[string]string {
|
||||
req.SetKV(nil)
|
||||
return val
|
||||
}
|
||||
|
||||
func ExtraReqInfoMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), ExtraReqInfo{}, ExtraReqInfo{
|
||||
RemoteAddr: r.RemoteAddr,
|
||||
UserAgent: r.UserAgent(),
|
||||
})))
|
||||
})
|
||||
}
|
||||
|
||||
func GetExtraReqInfoFromContext(ctx context.Context) ExtraReqInfo {
|
||||
eti := ctx.Value(ExtraReqInfo{})
|
||||
if eti != nil {
|
||||
return eti.(ExtraReqInfo)
|
||||
} else {
|
||||
return ExtraReqInfo{}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,15 +5,17 @@ import (
|
||||
"context"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/cortezaproject/corteza-server/pkg/rand"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/gorilla/securecookie"
|
||||
"github.com/gorilla/sessions"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// cortezaSessionStore implements the session store and bridge to corteza store
|
||||
@ -186,7 +188,7 @@ func (s cortezaSessionStore) save(ctx context.Context, ses *sessions.Session) (e
|
||||
cortezaSession.UserID = au.User.ID
|
||||
}
|
||||
|
||||
extra := GetExtraReqInfoFromContext(ctx)
|
||||
extra := auth.GetExtraReqInfoFromContext(ctx)
|
||||
cortezaSession.UserAgent = extra.UserAgent
|
||||
cortezaSession.RemoteAddr = extra.RemoteAddr
|
||||
|
||||
|
||||
@ -7,17 +7,19 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
)
|
||||
|
||||
func MountRoutes(r chi.Router) {
|
||||
// Protect all _private_ routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(auth.MiddlewareValidOnly)
|
||||
func MountRoutes(mv auth.MiddlewareValidator) func(r chi.Router) {
|
||||
return func(r chi.Router) {
|
||||
// Protect all _private_ routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(mv.HttpValidator("api"))
|
||||
|
||||
handlers.NewWorkflow(Workflow{}.New()).MountRoutes(r)
|
||||
handlers.NewTrigger(Trigger{}.New()).MountRoutes(r)
|
||||
handlers.NewSession(Session{}.New()).MountRoutes(r)
|
||||
handlers.NewFunction(Function{}.New()).MountRoutes(r)
|
||||
handlers.NewType(Type{}.New()).MountRoutes(r)
|
||||
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
|
||||
handlers.NewEventTypes(EventTypes{}.New()).MountRoutes(r)
|
||||
})
|
||||
handlers.NewWorkflow(Workflow{}.New()).MountRoutes(r)
|
||||
handlers.NewTrigger(Trigger{}.New()).MountRoutes(r)
|
||||
handlers.NewSession(Session{}.New()).MountRoutes(r)
|
||||
handlers.NewFunction(Function{}.New()).MountRoutes(r)
|
||||
handlers.NewType(Type{}.New()).MountRoutes(r)
|
||||
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
|
||||
handlers.NewEventTypes(EventTypes{}.New()).MountRoutes(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,34 +7,37 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
)
|
||||
|
||||
func MountRoutes(r chi.Router) {
|
||||
var (
|
||||
namespace = Namespace{}.New()
|
||||
module = Module{}.New()
|
||||
record = Record{}.New()
|
||||
page = Page{}.New()
|
||||
chart = Chart{}.New()
|
||||
notification = Notification{}.New()
|
||||
attachment = Attachment{}.New()
|
||||
automation = Automation{}.New()
|
||||
)
|
||||
func MountRoutes(mv auth.MiddlewareValidator) func(r chi.Router) {
|
||||
return func(r chi.Router) {
|
||||
var (
|
||||
namespace = Namespace{}.New()
|
||||
module = Module{}.New()
|
||||
record = Record{}.New()
|
||||
page = Page{}.New()
|
||||
chart = Chart{}.New()
|
||||
notification = Notification{}.New()
|
||||
attachment = Attachment{}.New()
|
||||
automation = Automation{}.New()
|
||||
)
|
||||
|
||||
// Initialize handlers & controllers.
|
||||
r.Group(func(r chi.Router) {
|
||||
// Use alternative handlers that support file serving
|
||||
handlers.NewAttachment(attachment).MountRoutes(r)
|
||||
})
|
||||
// Initialize handlers & controllers.
|
||||
r.Group(func(r chi.Router) {
|
||||
// Use alternative handlers that support file serving
|
||||
handlers.NewAttachment(attachment).MountRoutes(r)
|
||||
})
|
||||
|
||||
// Protect all _private_ routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(auth.MiddlewareValidOnly)
|
||||
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
|
||||
handlers.NewNamespace(namespace).MountRoutes(r)
|
||||
handlers.NewPage(page).MountRoutes(r)
|
||||
handlers.NewAutomation(automation).MountRoutes(r)
|
||||
handlers.NewModule(module).MountRoutes(r)
|
||||
handlers.NewRecord(record).MountRoutes(r)
|
||||
handlers.NewChart(chart).MountRoutes(r)
|
||||
handlers.NewNotification(notification).MountRoutes(r)
|
||||
})
|
||||
// Protect all _private_ routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(mv.HttpValidator("api"))
|
||||
|
||||
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
|
||||
handlers.NewNamespace(namespace).MountRoutes(r)
|
||||
handlers.NewPage(page).MountRoutes(r)
|
||||
handlers.NewAutomation(automation).MountRoutes(r)
|
||||
handlers.NewModule(module).MountRoutes(r)
|
||||
handlers.NewRecord(record).MountRoutes(r)
|
||||
handlers.NewChart(chart).MountRoutes(r)
|
||||
handlers.NewNotification(notification).MountRoutes(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
3
compose/types/locale.gen.go
generated
3
compose/types/locale.gen.go
generated
@ -14,9 +14,8 @@ package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/locale"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
16
compose/types/type_labels.gen.go
generated
16
compose/types/type_labels.gen.go
generated
@ -57,17 +57,17 @@ func (m Module) LabelResourceID() uint64 {
|
||||
}
|
||||
|
||||
// SetLabel adds new label to label map
|
||||
func (f *ModuleField) SetLabel(key string, value string) {
|
||||
if f.Labels == nil {
|
||||
f.Labels = make(map[string]string)
|
||||
func (m *ModuleField) SetLabel(key string, value string) {
|
||||
if m.Labels == nil {
|
||||
m.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
f.Labels[key] = value
|
||||
m.Labels[key] = value
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (f ModuleField) GetLabels() map[string]string {
|
||||
return f.Labels
|
||||
func (m ModuleField) GetLabels() map[string]string {
|
||||
return m.Labels
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
@ -76,8 +76,8 @@ func (ModuleField) LabelResourceKind() string {
|
||||
}
|
||||
|
||||
// GetLabels adds new label to label map
|
||||
func (f ModuleField) LabelResourceID() uint64 {
|
||||
return f.ID
|
||||
func (m ModuleField) LabelResourceID() uint64 {
|
||||
return m.ID
|
||||
}
|
||||
|
||||
// SetLabel adds new label to label map
|
||||
|
||||
@ -7,20 +7,23 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
)
|
||||
|
||||
func MountRoutes(r chi.Router) {
|
||||
r.Group(func(r chi.Router) {
|
||||
handlers.NewNodeHandshake(NodeHandshake{}.New()).MountRoutes(r)
|
||||
})
|
||||
func MountRoutes(mv auth.MiddlewareValidator) func(r chi.Router) {
|
||||
return func(r chi.Router) {
|
||||
r.Group(func(r chi.Router) {
|
||||
handlers.NewNodeHandshake(NodeHandshake{}.New()).MountRoutes(r)
|
||||
})
|
||||
|
||||
// Protect all _private_ routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(auth.MiddlewareValidOnly)
|
||||
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
|
||||
// Protect all _private_ routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(mv.HttpValidator("api"))
|
||||
|
||||
handlers.NewNode(Node{}.New()).MountRoutes(r)
|
||||
handlers.NewManageStructure((ManageStructure{}.New())).MountRoutes(r)
|
||||
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
|
||||
|
||||
handlers.NewSyncData((SyncData{}.New())).MountRoutes(r)
|
||||
handlers.NewSyncStructure((SyncStructure{}.New())).MountRoutes(r)
|
||||
})
|
||||
handlers.NewNode(Node{}.New()).MountRoutes(r)
|
||||
handlers.NewManageStructure((ManageStructure{}.New())).MountRoutes(r)
|
||||
|
||||
handlers.NewSyncData((SyncData{}.New())).MountRoutes(r)
|
||||
handlers.NewSyncStructure((SyncStructure{}.New())).MountRoutes(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,13 +24,17 @@ const (
|
||||
)
|
||||
|
||||
type (
|
||||
tokenGenerator interface {
|
||||
Generate(ctx context.Context, i auth.Identifiable, clientID uint64, scope ...string) (token []byte, err error)
|
||||
}
|
||||
|
||||
node struct {
|
||||
store store.Storer
|
||||
sysUser service.UserService
|
||||
|
||||
actionlog actionlog.Recorder
|
||||
|
||||
tokenEncoder auth.TokenGenerator
|
||||
tokenEncoder tokenGenerator
|
||||
|
||||
name string
|
||||
host string
|
||||
@ -56,7 +60,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func Node(s store.Storer, u service.UserService, al actionlog.Recorder, th auth.TokenHandler, options options.FederationOpt, ac nodeAccessController) *node {
|
||||
func Node(s store.Storer, u service.UserService, al actionlog.Recorder, th tokenGenerator, options options.FederationOpt, ac nodeAccessController) *node {
|
||||
return &node{
|
||||
store: s,
|
||||
sysUser: u,
|
||||
|
||||
@ -86,7 +86,7 @@ func Initialize(ctx context.Context, log *zap.Logger, s store.Storer, c Config)
|
||||
|
||||
DefaultAccessControl = AccessControl()
|
||||
|
||||
DefaultNode = Node(DefaultStore, service.DefaultUser, DefaultActionlog, auth.DefaultJwtHandler, c.Federation, DefaultAccessControl)
|
||||
DefaultNode = Node(DefaultStore, service.DefaultUser, DefaultActionlog, auth.JWT(), c.Federation, DefaultAccessControl)
|
||||
DefaultNodeSync = NodeSync()
|
||||
DefaultExposedModule = ExposedModule()
|
||||
DefaultSharedModule = SharedModule()
|
||||
|
||||
16
go.mod
16
go.mod
@ -18,18 +18,18 @@ require (
|
||||
github.com/PuerkitoBio/goquery v1.5.1 // indirect
|
||||
github.com/SentimensRG/ctx v0.0.0-20180729130232-0bfd988c655d
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||
github.com/brianvoe/gofakeit/v6 v6.11.1
|
||||
github.com/cortezaproject/corteza-locale v0.0.0-20220103124542-5d327f93f42c
|
||||
github.com/brianvoe/gofakeit/v6 v6.12.1
|
||||
github.com/cortezaproject/corteza-locale v0.0.0-20220111135803-4fb0db6196bc
|
||||
github.com/crewjam/saml v0.4.6
|
||||
github.com/crusttech/go-oidc v0.0.0-20180918092017-982855dad3e1
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/deckarep/golang-set v1.7.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dop251/goja v0.0.0-20220102113305-2298ace6d09d
|
||||
github.com/dop251/goja v0.0.0-20220110113543-261677941f3c
|
||||
github.com/edwvee/exiffix v0.0.0-20210922235313-0f6cbda5e58f
|
||||
github.com/evanw/esbuild v0.14.10
|
||||
github.com/evanw/esbuild v0.14.11
|
||||
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1
|
||||
github.com/gabriel-vasile/mimetype v1.4.0
|
||||
@ -56,7 +56,7 @@ require (
|
||||
github.com/jmoiron/sqlx v1.3.4
|
||||
github.com/joho/godotenv v1.4.0
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0
|
||||
github.com/lestrrat-go/jwx v1.2.14
|
||||
github.com/lestrrat-go/jwx v1.2.15
|
||||
github.com/lestrrat-go/strftime v1.0.5
|
||||
github.com/lib/pq v1.10.4
|
||||
github.com/markbates/goth v1.68.0
|
||||
@ -71,7 +71,7 @@ require (
|
||||
github.com/prometheus/tsdb v0.7.1 // indirect
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
|
||||
github.com/sony/sonyflake v1.0.0
|
||||
github.com/spf13/afero v1.7.1
|
||||
github.com/spf13/afero v1.8.0
|
||||
github.com/spf13/cast v1.4.1
|
||||
github.com/spf13/cobra v1.3.0
|
||||
github.com/steinfletcher/apitest v1.5.11
|
||||
@ -79,7 +79,7 @@ require (
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tebeka/strftime v0.1.5 // indirect
|
||||
go.uber.org/atomic v1.9.0
|
||||
go.uber.org/zap v1.19.1
|
||||
go.uber.org/zap v1.20.0
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
||||
golang.org/x/text v0.3.7
|
||||
|
||||
17
go.sum
17
go.sum
@ -125,6 +125,8 @@ github.com/brianvoe/gofakeit/v6 v6.5.0 h1:zoWqGsuB8TB4MSwUZXtV3OwUSdzi8EHeXO8JfR
|
||||
github.com/brianvoe/gofakeit/v6 v6.5.0/go.mod h1:palrJUk4Fyw38zIFB/uBZqsgzW5VsNllhHKKwAebzew=
|
||||
github.com/brianvoe/gofakeit/v6 v6.11.1 h1:Srilo77ZZfDRwqyJ7sYFBtyhrOdC57NG69s6sVUHwmE=
|
||||
github.com/brianvoe/gofakeit/v6 v6.11.1/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
|
||||
github.com/brianvoe/gofakeit/v6 v6.12.1 h1:12JSuDkqX/eUiqnNcwetTrbHMdxdLIBx1pBEZNlCp98=
|
||||
github.com/brianvoe/gofakeit/v6 v6.12.1/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
@ -165,6 +167,8 @@ github.com/cortezaproject/corteza-locale v0.0.0-20211210121242-931011c1250c h1:z
|
||||
github.com/cortezaproject/corteza-locale v0.0.0-20211210121242-931011c1250c/go.mod h1:wsI1UftEdBqTuEDKBZmx2LfNu/kZun5pRbCAi420JCg=
|
||||
github.com/cortezaproject/corteza-locale v0.0.0-20220103124542-5d327f93f42c h1:VV5Z9ZiULjNoNltJ0ho7Du6svKovDQ92wkxbkdB2gmg=
|
||||
github.com/cortezaproject/corteza-locale v0.0.0-20220103124542-5d327f93f42c/go.mod h1:wsI1UftEdBqTuEDKBZmx2LfNu/kZun5pRbCAi420JCg=
|
||||
github.com/cortezaproject/corteza-locale v0.0.0-20220111135803-4fb0db6196bc h1:d4xI2vuPSA/U+y1JJQKC9nwk2FSnoYansUquX6IPj5E=
|
||||
github.com/cortezaproject/corteza-locale v0.0.0-20220111135803-4fb0db6196bc/go.mod h1:wsI1UftEdBqTuEDKBZmx2LfNu/kZun5pRbCAi420JCg=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
@ -205,6 +209,8 @@ github.com/dop251/goja v0.0.0-20210726224656-a55e4cfac4cf h1:eK64KqjIBLpCtzIbzci
|
||||
github.com/dop251/goja v0.0.0-20210726224656-a55e4cfac4cf/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||
github.com/dop251/goja v0.0.0-20220102113305-2298ace6d09d h1:RHB3jZIxEzQHPzoGtvn47BMbD7jzTfHAXpVC3v4aVI8=
|
||||
github.com/dop251/goja v0.0.0-20220102113305-2298ace6d09d/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||
github.com/dop251/goja v0.0.0-20220110113543-261677941f3c h1:1XnAlcjYBdO7xsa2rhNB/BTztiu4cFKOxE+3brXVtG4=
|
||||
github.com/dop251/goja v0.0.0-20220110113543-261677941f3c/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/edwvee/exiffix v0.0.0-20180602190213-b57537c92a6b h1:6CBzNasH8+bKeFwr5Bt5JtALHLFN4iQp7sf4ShlP/ik=
|
||||
@ -228,6 +234,8 @@ github.com/evanw/esbuild v0.12.16 h1:UxvizOzRZk0gnlal2g2MulpCjIiAPtciLr674nOKtcI
|
||||
github.com/evanw/esbuild v0.12.16/go.mod h1:y2AFBAGVelPqPodpdtxWWqe6n2jYf5FrsJbligmRmuw=
|
||||
github.com/evanw/esbuild v0.14.10 h1:+7c1VNndl7uLLxVEeRH4rOUz0Y+nrSw8xfmE9rGtrtw=
|
||||
github.com/evanw/esbuild v0.14.10/go.mod h1:GG+zjdi59yh3ehDn4ZWfPcATxjPDUH53iU4ZJbp7dkY=
|
||||
github.com/evanw/esbuild v0.14.11 h1:bw50N4v70Dqf/B6Wn+3BM6BVttz4A6tHn8m8Ydj9vxk=
|
||||
github.com/evanw/esbuild v0.14.11/go.mod h1:GG+zjdi59yh3ehDn4ZWfPcATxjPDUH53iU4ZJbp7dkY=
|
||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
||||
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU=
|
||||
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
|
||||
@ -305,6 +313,8 @@ github.com/goccy/go-json v0.7.10 h1:ulhbuNe1JqE68nMRXXTJRrUu0uhouf0VevLINxQq4Ec=
|
||||
github.com/goccy/go-json v0.7.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.8.1 h1:4/Wjm0JIJaTDm8K1KcGrLHJoa8EsJ13YWeX+6Kfq6uI=
|
||||
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/goccy/go-json v0.9.0 h1:2flW7bkbrRgU8VuDi0WXDqTmPimjv1thfxkPe8sug+8=
|
||||
github.com/goccy/go-json v0.9.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
@ -555,6 +565,8 @@ github.com/lestrrat-go/jwx v1.2.11 h1:e9BS5NQ003hxXogNsgf5fEWf01ZJvj4Aj1qy7Dykqm
|
||||
github.com/lestrrat-go/jwx v1.2.11/go.mod h1:25DcLbNWArPA/Ew5CcBmewl32cJKxOk5cbepBsIJFzw=
|
||||
github.com/lestrrat-go/jwx v1.2.14 h1:69OeaiFKCTn8xDmBGzHTgv/GBoO1LJcXw99GfYCDKzg=
|
||||
github.com/lestrrat-go/jwx v1.2.14/go.mod h1:3Q3Re8TaOcVTdpx4Tvz++OWmryDklihTDqrrwQiyS2A=
|
||||
github.com/lestrrat-go/jwx v1.2.15 h1:58CEGJpf1TS3NJASMfMkTp6stlvPTsqs1xxAu/Yf/uM=
|
||||
github.com/lestrrat-go/jwx v1.2.15/go.mod h1:DJKaoM8f1OvYVwWoW45gBrUxMlpD4FHjT0UnrW3iX28=
|
||||
github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
|
||||
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
@ -760,6 +772,8 @@ github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY52
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.7.1 h1:F37zV8E8RLstLpZ0RUGK2NGg1X57y6/B0Eg6S8oqdoA=
|
||||
github.com/spf13/afero v1.7.1/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
|
||||
github.com/spf13/afero v1.8.0 h1:5MmtuhAgYeU6qpa7w7bP0dv6MBYuup0vekhSpSkoq60=
|
||||
github.com/spf13/afero v1.8.0/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||
@ -872,6 +886,7 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
@ -882,6 +897,8 @@ go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE=
|
||||
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
go.uber.org/zap v1.20.0 h1:N4oPlghZwYG55MlU6LXk/Zp00FVNE9X9wrYO8CEs4lc=
|
||||
go.uber.org/zap v1.20.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
||||
@ -85,12 +85,8 @@ func (s server) Serve(ctx context.Context) {
|
||||
r.Use(LogResponse)
|
||||
}
|
||||
|
||||
println("using up DefaultJwtHandler", auth.DefaultJwtHandler != nil)
|
||||
|
||||
r.Use(
|
||||
auth.DefaultJwtHandler.HttpVerifier(),
|
||||
auth.DefaultJwtHandler.HttpAuthenticator(),
|
||||
)
|
||||
// Verifies JWT in headers, cookies, ...
|
||||
r.Use(auth.JWT().HttpVerifier())
|
||||
|
||||
for _, mountRoutes := range s.endpoints {
|
||||
mountRoutes(r)
|
||||
|
||||
@ -38,3 +38,20 @@ func ErrUnauthorizedScope() error {
|
||||
errors.StackTrimAtFn("http.HandlerFunc.ServeHTTP"),
|
||||
)
|
||||
}
|
||||
|
||||
func ErrMalformedToken(details string) error {
|
||||
return errors.New(
|
||||
errors.KindUnauthorized,
|
||||
|
||||
"malformed token: "+details,
|
||||
|
||||
errors.Meta("type", "malformedToken"),
|
||||
|
||||
// translation namespace & key
|
||||
errors.Meta(locale.ErrorMetaNamespace{}, "internal"),
|
||||
errors.Meta(locale.ErrorMetaKey{}, "auth.errors.malformedToken"),
|
||||
|
||||
errors.StackSkip(1),
|
||||
errors.StackTrimAtFn("http.HandlerFunc.ServeHTTP"),
|
||||
)
|
||||
}
|
||||
|
||||
31
pkg/auth/extra.go
Normal file
31
pkg/auth/extra.go
Normal file
@ -0,0 +1,31 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
ExtraReqInfo struct {
|
||||
RemoteAddr string
|
||||
UserAgent string
|
||||
}
|
||||
)
|
||||
|
||||
func ExtraReqInfoMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), ExtraReqInfo{}, ExtraReqInfo{
|
||||
RemoteAddr: r.RemoteAddr,
|
||||
UserAgent: r.UserAgent(),
|
||||
})))
|
||||
})
|
||||
}
|
||||
|
||||
func GetExtraReqInfoFromContext(ctx context.Context) ExtraReqInfo {
|
||||
eti := ctx.Value(ExtraReqInfo{})
|
||||
if eti != nil {
|
||||
return eti.(ExtraReqInfo)
|
||||
} else {
|
||||
return ExtraReqInfo{}
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,5 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
Identifiable interface {
|
||||
Identity() uint64
|
||||
@ -13,16 +8,16 @@ type (
|
||||
String() string
|
||||
}
|
||||
|
||||
TokenGenerator interface {
|
||||
Encode(i Identifiable, clientID uint64, scope ...string) (token []byte, err error)
|
||||
Generate(ctx context.Context, i Identifiable, clientID uint64, scope ...string) (token []byte, err error)
|
||||
}
|
||||
//TokenGenerator interface {
|
||||
// Encode(i Identifiable, clientID uint64, scope ...string) (token []byte, err error)
|
||||
// Generate(ctx context.Context, i Identifiable, clientID uint64, scope ...string) (token []byte, err error)
|
||||
//}
|
||||
|
||||
TokenHandler interface {
|
||||
TokenGenerator
|
||||
HttpVerifier() func(http.Handler) http.Handler
|
||||
HttpAuthenticator() func(http.Handler) http.Handler
|
||||
}
|
||||
//TokenHandler interface {
|
||||
// TokenGenerator
|
||||
// HttpVerifier() func(http.Handler) http.Handler
|
||||
// HttpAuthenticator() func(http.Handler) http.Handler
|
||||
//}
|
||||
|
||||
Signer interface {
|
||||
Sign(userID uint64, pp ...interface{}) string
|
||||
|
||||
424
pkg/auth/jwt.go
424
pkg/auth/jwt.go
@ -2,67 +2,96 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/api"
|
||||
"github.com/cortezaproject/corteza-server/pkg/id"
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/go-chi/jwtauth"
|
||||
"github.com/go-oauth2/oauth2/v4"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
"github.com/spf13/cast"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
tokenManager struct {
|
||||
signer interface {
|
||||
Sign(accessToken string, identity Identifiable, clientID uint64, scope ...string) (signed []byte, err error)
|
||||
}
|
||||
|
||||
MiddlewareValidator interface {
|
||||
HttpValidator(scope ...string) func(http.Handler) http.Handler
|
||||
}
|
||||
|
||||
oauth2manager interface {
|
||||
LoadAccessToken(ctx context.Context, access string) (ti oauth2.TokenInfo, err error)
|
||||
GenerateAccessToken(ctx context.Context, gt oauth2.GrantType, tgr *oauth2.TokenGenerateRequest) (oauth2.TokenInfo, error)
|
||||
}
|
||||
|
||||
jwtManager struct {
|
||||
// Expiration time in minutes
|
||||
expiry time.Duration
|
||||
|
||||
signAlgo jwa.SignatureAlgorithm
|
||||
signKey jwk.Key
|
||||
|
||||
log *zap.Logger
|
||||
|
||||
oa2m oauth2manager
|
||||
|
||||
issuerClaim string
|
||||
}
|
||||
|
||||
// @todo remove
|
||||
tokenStore interface {
|
||||
LookupUserByID(ctx context.Context, id uint64) (*types.User, error)
|
||||
LookupAuthOa2tokenByAccess(ctx context.Context, access string) (*types.AuthOa2token, error)
|
||||
SearchRoleMembers(ctx context.Context, f types.RoleMemberFilter) (types.RoleMemberSet, types.RoleMemberFilter, error)
|
||||
|
||||
CreateAuthOa2token(ctx context.Context, rr ...*types.AuthOa2token) error
|
||||
DeleteAuthOA2TokenByUserID(ctx context.Context, _userID uint64) error
|
||||
|
||||
UpsertAuthConfirmedClient(ctx context.Context, rr ...*types.AuthConfirmedClient) error
|
||||
}
|
||||
|
||||
ExtraReqInfo struct {
|
||||
RemoteAddr string
|
||||
UserAgent string
|
||||
}
|
||||
// @todo remove
|
||||
//tokenLookup interface {
|
||||
// LookupAuthOa2tokenByID(ctx context.Context, id uint64) (*types.AuthOa2token, error)
|
||||
//}
|
||||
//
|
||||
//tokenStoreWithLookup interface {
|
||||
// tokenStore
|
||||
// tokenLookup
|
||||
//}
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultJwtHandler TokenHandler
|
||||
DefaultJwtStore tokenStore
|
||||
defaultJWTManager *jwtManager
|
||||
//DefaultJwtStore tokenStoreWithLookup
|
||||
)
|
||||
|
||||
func SetupDefault(secret string, expiry time.Duration) (err error) {
|
||||
// JWT returns d
|
||||
func JWT() *jwtManager {
|
||||
return defaultJWTManager
|
||||
}
|
||||
|
||||
func SetupDefault(oa2m oauth2manager, secret string, expiry time.Duration) (err error) {
|
||||
// Use JWT secret for hmac signer for now
|
||||
DefaultSigner = HmacSigner(secret)
|
||||
DefaultJwtHandler, err = TokenManager(secret, expiry)
|
||||
defaultJWTManager, err = NewJWTManager(oa2m, jwa.HS512, secret, expiry)
|
||||
return
|
||||
}
|
||||
|
||||
// TokenManager returns token management facility
|
||||
// NewJWTManager initializes and returns new instance of JWT manager
|
||||
// @todo should be extended to accept different kinds of algorythms, private-keys etc.
|
||||
func TokenManager(secret string, expiry time.Duration) (tm *tokenManager, err error) {
|
||||
tm = &tokenManager{
|
||||
expiry: expiry,
|
||||
signAlgo: jwa.HS512,
|
||||
func NewJWTManager(oa2m oauth2manager, algo jwa.SignatureAlgorithm, secret string, expiry time.Duration) (tm *jwtManager, err error) {
|
||||
tm = &jwtManager{
|
||||
expiry: expiry,
|
||||
signAlgo: algo,
|
||||
issuerClaim: "cortezaproject.org",
|
||||
log: logger.Default(),
|
||||
oa2m: oa2m,
|
||||
}
|
||||
|
||||
if len(secret) == 0 {
|
||||
@ -74,72 +103,40 @@ func TokenManager(secret string, expiry time.Duration) (tm *tokenManager, err er
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
//
|
||||
//var (
|
||||
// // tuukn = jwt.New()
|
||||
// // signed []byte
|
||||
// //)
|
||||
// //
|
||||
// //if err = tuukn.Set(jwt.ExpirationKey, expiry); err != nil {
|
||||
// // return
|
||||
// //}
|
||||
// //
|
||||
// signed, err = jwt.Sign(tuukn, jwa.HS512, []byte(secret))
|
||||
//
|
||||
// tkn = &tokenManager{
|
||||
// expiry: expiry,
|
||||
// tokenAuth: jwtauth.New(jwt.SigningMethodHS512.Alg(), []byte(secret), nil),
|
||||
// secret: []byte(secret),
|
||||
// }, nil
|
||||
//
|
||||
//return tkn, nil
|
||||
}
|
||||
|
||||
// SetJWTStore set store for JWT
|
||||
// @todo find better way to initiate store,
|
||||
// it mainly used for generating and storing accessToken for impersonate and corredor, Ref: j.Generate()
|
||||
func SetJWTStore(store tokenStore) {
|
||||
DefaultJwtStore = store
|
||||
}
|
||||
|
||||
// Authenticate the token from the given string and return parsed token or error
|
||||
func (tm *tokenManager) Authenticate(token string) (pToken jwt.Token, err error) {
|
||||
if pToken, err = jwt.Parse([]byte(token), jwt.WithVerify(tm.signAlgo, tm.signKey)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = jwt.Validate(pToken); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//// Encode identity into a
|
||||
//func (tm *tokenManager) Encode(identity Identifiable, scope ...string) ([]byte, error) {
|
||||
// var (
|
||||
// // when possible, extend this with the client
|
||||
// clientID uint64 = 0
|
||||
// )
|
||||
//// @todo remove
|
||||
////// SetJWTStore set store for JWT
|
||||
////// @todo find better way to initiate store,
|
||||
////// it mainly used for generating and storing accessToken for impersonate and corredor, Ref: j.Generate()
|
||||
////func SetJWTStore(store tokenStoreWithLookup) {
|
||||
//// DefaultJwtStore = store
|
||||
////}
|
||||
//
|
||||
// if len(scope) == 0 {
|
||||
// // for backward compatibility we default
|
||||
// // unset scope to profile & api
|
||||
// scope = []string{"profile", "api"}
|
||||
//// Authenticate the token from the given string and return parsed token or error
|
||||
//func (m *jwtManager) Authenticate(s string) (pToken jwt.Token, err error) {
|
||||
// if pToken, err = jwt.Parse([]byte(s), jwt.WithVerify(m.signAlgo, m.signKey)); err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// return tm.Encode(identity, clientID, scope...)
|
||||
// if err = jwt.Validate(pToken); err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// return
|
||||
//}
|
||||
|
||||
// Encode give identity, clientID & scope into JWT access token (that can be use for API requests)
|
||||
// Sign takes security information and returns signed JWT
|
||||
//
|
||||
// @todo this follows implementation in auth/oauth2/jwt_access.go
|
||||
// and should be refactored accordingly (move both into the same location/pkg => here)
|
||||
func (tm *tokenManager) Encode(identity Identifiable, clientID uint64, scope ...string) (_ []byte, err error) {
|
||||
// Access token is expected to be issued by OAuth2 token manager
|
||||
// without it, we can only do static (JWT itself) validation
|
||||
//f
|
||||
// Identity holds user ID and all roles that go into this security context
|
||||
// Client ID represents the auth client that was used
|
||||
func (m *jwtManager) Sign(accessToken string, identity Identifiable, clientID uint64, scope ...string) (signed []byte, err error) {
|
||||
var (
|
||||
roles string
|
||||
token = jwt.New()
|
||||
roles = ""
|
||||
)
|
||||
|
||||
if len(scope) == 0 {
|
||||
@ -149,12 +146,12 @@ func (tm *tokenManager) Encode(identity Identifiable, clientID uint64, scope ...
|
||||
}
|
||||
|
||||
for _, r := range identity.Roles() {
|
||||
roles += fmt.Sprintf(" %d", r)
|
||||
roles += strconv.FormatUint(r, 10)
|
||||
}
|
||||
|
||||
// previous implementation had special a "salt" claim that ensured JWT uniquness
|
||||
// we're using more standard approach with JWT ID now.
|
||||
if err = token.Set(jwt.JwtIDKey, fmt.Sprintf("%d", id.Next())); err != nil {
|
||||
// this is the key part
|
||||
// here we put access token to the JWT ID claim
|
||||
if err = token.Set(jwt.JwtIDKey, accessToken); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -162,11 +159,19 @@ func (tm *tokenManager) Encode(identity Identifiable, clientID uint64, scope ...
|
||||
return
|
||||
}
|
||||
|
||||
if err = token.Set(jwt.ExpirationKey, time.Now().Add(tm.expiry).Unix()); err != nil {
|
||||
if err = token.Set(jwt.ExpirationKey, time.Now().Add(m.expiry).Unix()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = token.Set(jwt.AudienceKey, fmt.Sprintf("%d", clientID)); err != nil {
|
||||
if err = token.Set(jwt.IssuerKey, m.issuerClaim); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = token.Set(jwt.IssuedAtKey, time.Now().Unix()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = token.Set("clientID", strconv.FormatUint(clientID, 10)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -178,57 +183,87 @@ func (tm *tokenManager) Encode(identity Identifiable, clientID uint64, scope ...
|
||||
return
|
||||
}
|
||||
|
||||
return jwt.Sign(token, tm.signAlgo, tm.signKey)
|
||||
if signed, err = jwt.Sign(token, m.signAlgo, m.signKey); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//claims := jwt.MapClaims{
|
||||
// "sub": identity.String(),
|
||||
// "exp": time.Now().Add(tm.expiry).Unix(),
|
||||
// "aud": fmt.Sprintf("%d", clientID),
|
||||
// "scope": strings.Join(scope, " "),
|
||||
// "roles": strings.TrimSpace(roles),
|
||||
//}
|
||||
//
|
||||
//newToken := jwt.NewWithClaims(jwt.SigningMethodHS512, claims)
|
||||
//newToken.Header["salt"] = string(rand.Bytes(32))
|
||||
//access, _ := newToken.SignedString(tm.secret)
|
||||
//return access
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
// HttpVerifier returns a HTTP handler that verifies JWT and stores it into context
|
||||
func (tm *tokenManager) HttpVerifier() func(http.Handler) http.Handler {
|
||||
////jwt.WithHTTPClient()
|
||||
//return func(next http.Handler) http.Handler {
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
// token, err := jwt.ParseRequest(req)
|
||||
// if err != nil {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// next.ServeHTTP(w, req)
|
||||
// })
|
||||
//}
|
||||
func (m *jwtManager) Generate(ctx context.Context, i Identifiable, clientID uint64, scope ...string) (signed []byte, err error) {
|
||||
var (
|
||||
ti oauth2.TokenInfo
|
||||
)
|
||||
|
||||
return jwtauth.Verifier(jwtauth.New(tm.signAlgo.String(), tm.signKey, nil))
|
||||
ti, err = m.oa2m.GenerateAccessToken(ctx, oauth2.Implicit, &oauth2.TokenGenerateRequest{
|
||||
ClientID: strconv.FormatUint(clientID, 10),
|
||||
UserID: i.String(),
|
||||
Scope: strings.Join(scope, " "),
|
||||
Refresh: "cli?",
|
||||
AccessTokenExp: m.expiry,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return m.Sign(ti.GetAccess(), i, 0, scope...)
|
||||
}
|
||||
|
||||
// HttpAuthenticator converts JWT claims into identity and stores it into context
|
||||
func (tm *tokenManager) HttpAuthenticator() func(http.Handler) http.Handler {
|
||||
func ValidateContext(ctx context.Context, oa2m oauth2manager, scope ...string) (err error) {
|
||||
var (
|
||||
token jwt.Token
|
||||
)
|
||||
|
||||
if token, _, err = jwtauth.FromContext(ctx); err != nil {
|
||||
return ErrUnauthorized()
|
||||
}
|
||||
|
||||
return Validate(ctx, token, oa2m, scope...)
|
||||
}
|
||||
|
||||
func Validate(ctx context.Context, token jwt.Token, oa2m oauth2manager, scope ...string) (err error) {
|
||||
if !CheckJwtScope(token, scope...) {
|
||||
return ErrUnauthorizedScope()
|
||||
}
|
||||
|
||||
// Extract the JWT id from the token (string) and convert it to uint64
|
||||
// to be compatible with the lookup function
|
||||
if len(token.JwtID()) < 10 {
|
||||
return ErrMalformedToken("missing or malformed JWT ID")
|
||||
}
|
||||
|
||||
// @todo we could use a simple caching mechanism here
|
||||
// 1. if lookup is successful, add a JWT ID to the list
|
||||
// 2. add short exp time (that should not last longer than token's exp time)
|
||||
// 3. check against the list first; if JWT ID is not present there check in storage
|
||||
//
|
||||
if _, err = oa2m.LoadAccessToken(ctx, token.JwtID()); err != nil {
|
||||
return ErrUnauthorized()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HttpVerifier http middleware handler will verify a JWT string from a http request.
|
||||
func (m *jwtManager) HttpVerifier() func(http.Handler) http.Handler {
|
||||
return jwtauth.Verifier(jwtauth.New(m.signAlgo.String(), m.signKey, nil))
|
||||
}
|
||||
|
||||
func (m *jwtManager) HttpValidator(scope ...string) func(http.Handler) http.Handler {
|
||||
if len(scope) == 0 {
|
||||
// ensure that scope is not empty
|
||||
scope = []string{"api"}
|
||||
}
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
tkn, _, err := jwtauth.FromContext(ctx)
|
||||
|
||||
// When token is present, expect no errors and valid claims!
|
||||
if tkn != nil {
|
||||
if err != nil {
|
||||
// But if token is present, there shouldn't be an error
|
||||
api.Send(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = SetIdentityToContext(ctx, IdentityFromToken(tkn))
|
||||
r = r.WithContext(ctx)
|
||||
if err := ValidateContext(r.Context(), m.oa2m, scope...); err != nil {
|
||||
errors.ProperlyServeHTTP(w, r, err, false)
|
||||
return
|
||||
} else {
|
||||
token, _, _ := jwtauth.FromContext(r.Context())
|
||||
r = r.WithContext(SetIdentityToContext(r.Context(), IdentityFromToken(token)))
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
@ -236,62 +271,87 @@ func (tm *tokenManager) HttpAuthenticator() func(http.Handler) http.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// Generates JWT and stores alongside with client-confirmation entry,
|
||||
func (tm *tokenManager) Generate(ctx context.Context, i Identifiable, clientID uint64, scope ...string) (token []byte, err error) {
|
||||
var (
|
||||
eti = GetExtraReqInfoFromContext(ctx)
|
||||
oa2t = &types.AuthOa2token{
|
||||
ID: id.Next(),
|
||||
CreatedAt: time.Now().Round(time.Second),
|
||||
RemoteAddr: eti.RemoteAddr,
|
||||
UserAgent: eti.UserAgent,
|
||||
ClientID: clientID,
|
||||
}
|
||||
//// HttpAuthenticator converts JWT claims into identity and stores it into context
|
||||
//func (m *jwtManager) HttpAuthenticator(next http.Handler) http.Handler {
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// ctx := r.Context()
|
||||
//
|
||||
// tkn, _, err := jwtauth.FromContext(ctx)
|
||||
//
|
||||
// // Requests w/o token should not yield an error
|
||||
// // there are parts of the system that can be access without it
|
||||
// // and/or handle such situation internally
|
||||
// if err != nil && !errors.Is(err, jwtauth.ErrNoTokenFound) {
|
||||
// api.Send(w, r, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // If token is present extract identity
|
||||
// if tkn != nil {
|
||||
// ctx = SetIdentityToContext(ctx, IdentityFromToken(tkn))
|
||||
// r = r.WithContext(ctx)
|
||||
//
|
||||
// // @todo verify JWT ID (access-token!!
|
||||
// tkn.JwtID()
|
||||
//
|
||||
// }
|
||||
//
|
||||
// next.ServeHTTP(w, r)
|
||||
// })
|
||||
//}
|
||||
|
||||
acc = &types.AuthConfirmedClient{
|
||||
ConfirmedAt: oa2t.CreatedAt,
|
||||
ClientID: clientID,
|
||||
}
|
||||
)
|
||||
//
|
||||
//// Generate makes a new token and stores it in the database
|
||||
//func (tm *tokenManager) Generate(ctx context.Context, i Identifiable, clientID uint64, scope ...string) (token []byte, err error) {
|
||||
// var (
|
||||
// // eti = GetExtraReqInfoFromContext(ctx)
|
||||
// // oa2t = &types.AuthOa2token{
|
||||
// // ID: id.Next(),
|
||||
// // CreatedAt: time.Now().Round(time.Second),
|
||||
// // RemoteAddr: eti.RemoteAddr,
|
||||
// // UserAgent: eti.UserAgent,
|
||||
// // ClientID: clientID,
|
||||
// // }
|
||||
// //
|
||||
// // acc = &types.AuthConfirmedClient{
|
||||
// // ConfirmedAt: oa2t.CreatedAt,
|
||||
// // ClientID: clientID,
|
||||
// // }
|
||||
// oa2t *types.AuthOa2token
|
||||
// acc *types.AuthConfirmedClient
|
||||
//
|
||||
// jwtID = id.Next()
|
||||
// )
|
||||
//
|
||||
// if oa2t, acc, err = MakeAuthStructs(ctx, jwtID, i.Identity(), clientID, nil, tm.expiry); err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if token, err = tm.make(jwtID, i, clientID, scope...); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// oa2t.Access = string(token)
|
||||
//
|
||||
// // use the same expiration as on token
|
||||
// //oa2t.ExpiresAt = oa2t.CreatedAt.Add(tm.expiry)
|
||||
//
|
||||
// //if oa2t.Data, err = json.Marshal(oa2t); err != nil {
|
||||
// // return
|
||||
// //}
|
||||
//
|
||||
// //if oa2t.UserID, _ = ExtractFromSubClaim(i.String()); oa2t.UserID == 0 {
|
||||
// // // UserID stores collection of IDs: user's ID and set of all roles' user is member of
|
||||
// // return nil, fmt.Errorf("could not parse user ID from token")
|
||||
// //}
|
||||
// //
|
||||
// //// copy user id to auth client confirmation
|
||||
// //acc.UserID = oa2t.UserID
|
||||
//
|
||||
// return token, StoreAuthToken(ctx, DefaultJwtStore, oa2t, acc)
|
||||
//}
|
||||
|
||||
if token, err = tm.Encode(i, clientID, scope...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
oa2t.Access = string(token)
|
||||
|
||||
// use the same expiration as on token
|
||||
oa2t.ExpiresAt = oa2t.CreatedAt.Add(tm.expiry)
|
||||
|
||||
if oa2t.Data, err = json.Marshal(oa2t); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if oa2t.UserID, _ = ExtractFromSubClaim(i.String()); oa2t.UserID == 0 {
|
||||
// UserID stores collection of IDs: user's ID and set of all roles' user is member of
|
||||
return nil, fmt.Errorf("could not parse user ID from token")
|
||||
}
|
||||
|
||||
// copy user id to auth client confirmation
|
||||
acc.UserID = oa2t.UserID
|
||||
|
||||
if err = DefaultJwtStore.UpsertAuthConfirmedClient(ctx, acc); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return token, DefaultJwtStore.CreateAuthOa2token(ctx, oa2t)
|
||||
}
|
||||
|
||||
func GetExtraReqInfoFromContext(ctx context.Context) ExtraReqInfo {
|
||||
eti := ctx.Value(ExtraReqInfo{})
|
||||
if eti != nil {
|
||||
return eti.(ExtraReqInfo)
|
||||
} else {
|
||||
return ExtraReqInfo{}
|
||||
}
|
||||
}
|
||||
|
||||
// ClaimsToIdentity decodes sub & roles claims into identity
|
||||
// IdentityFromToken decodes sub & roles claims into identity
|
||||
func IdentityFromToken(token jwt.Token) *identity {
|
||||
var (
|
||||
roles, _ = token.Get("roles")
|
||||
|
||||
@ -1,53 +1,57 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
//import (
|
||||
// "net/http"
|
||||
//)
|
||||
//
|
||||
//func MiddlewareValidOnly(next http.Handler) http.Handler {
|
||||
// return AccessTokenCheck("api")(next)
|
||||
//}
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/go-chi/jwtauth"
|
||||
)
|
||||
|
||||
func MiddlewareValidOnly(next http.Handler) http.Handler {
|
||||
return AccessTokenCheck("api")(next)
|
||||
}
|
||||
|
||||
func AccessTokenCheck(scope ...string) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var ctx = r.Context()
|
||||
|
||||
token, _, err := jwtauth.FromContext(ctx)
|
||||
spew.Dump(token, err)
|
||||
|
||||
if err != nil {
|
||||
errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false)
|
||||
return
|
||||
}
|
||||
|
||||
if !CheckJwtScope(token, scope...) {
|
||||
errors.ProperlyServeHTTP(w, r, ErrUnauthorizedScope(), false)
|
||||
}
|
||||
|
||||
// @todo we need to check if token is in store!!
|
||||
// @todo we need to check if token is in store!!
|
||||
// @todo we need to check if token is in store!!
|
||||
// @todo we need to check if token is in store!!
|
||||
// @todo we need to check if token is in store!!
|
||||
// @todo we need to check if token is in store!!
|
||||
// @todo we need to check if token is in store!!
|
||||
// @todo we need to check if token is in store!!
|
||||
// @todo we need to check if token is in store!!
|
||||
// @todo we need to check if token is in store!!
|
||||
//
|
||||
//// verify JWT from store
|
||||
//_, err = DefaultJwtStore.LookupAuthOa2tokenByAccess(ctx, tkn.Raw)
|
||||
//if err != nil {
|
||||
// errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false)
|
||||
// return
|
||||
//}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
//func AccessTokenCheck(s store.AuthOa2tokens, scope ...string) func(http.Handler) http.Handler {
|
||||
// return func(next http.Handler) http.Handler {
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// if err := validateContextToken(r.Context(), s, scope); err != nil {
|
||||
// errors.ProperlyServeHTTP(w, r, err, false)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// next.ServeHTTP(w, r)
|
||||
// })
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func validateContextToken(ctx context.Context, s store.AuthOa2tokens, scope []string) (err error) {
|
||||
// var (
|
||||
// token jwt.Token
|
||||
// )
|
||||
//
|
||||
// if token, _, err = jwtauth.FromContext(ctx); err != nil {
|
||||
// return ErrUnauthorized()
|
||||
// }
|
||||
//
|
||||
// if !CheckJwtScope(token, scope...) {
|
||||
// return ErrUnauthorizedScope()
|
||||
// }
|
||||
//
|
||||
// // Extract the JWT id from the token (string) and convert it to uint64
|
||||
// // to be compatible with the lookup function
|
||||
// if len(token.JwtID()) < 10 {
|
||||
// return ErrMalformedToken("missing or malformed JWT ID")
|
||||
// }
|
||||
//
|
||||
// // check if token exists in our DB
|
||||
// // there is no need to check for anything beyond existence
|
||||
// // because
|
||||
// //
|
||||
// // @todo we could use a simple caching mechanism here
|
||||
// // 1. if lookup is successful, add a JWT ID to the list
|
||||
// // 2. add short exp time (that should not last onger than token's exp time)
|
||||
// // 3. check against the list first; if JWT ID is not present there check in storage
|
||||
// //
|
||||
// if _, err = store.LookupAuthOa2tokenByAccess(ctx, s, token.JwtID()); err != nil {
|
||||
// return ErrUnauthorized()
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
//}
|
||||
|
||||
@ -104,7 +104,7 @@ type (
|
||||
}
|
||||
|
||||
authTokenMaker interface {
|
||||
auth.TokenGenerator
|
||||
Generate(ctx context.Context, i auth.Identifiable, clientID uint64, scope ...string) (token []byte, err error)
|
||||
}
|
||||
)
|
||||
|
||||
@ -168,7 +168,7 @@ func NewService(logger *zap.Logger, opt options.CorredorOpt) *service {
|
||||
|
||||
iteratorProviders: make(map[string]IteratorResourceFinder),
|
||||
|
||||
authTokenMaker: auth.DefaultJwtHandler,
|
||||
authTokenMaker: auth.JWT(),
|
||||
eventRegistry: eventbus.Service(),
|
||||
|
||||
denyExec: make(map[string]map[uint64]bool),
|
||||
|
||||
@ -7,15 +7,15 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func TestSession_procRawMessage(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
s = session{server: Server(nil, options.WebsocketOpt{})}
|
||||
jwtHandler, err = auth.TokenManager("secret", time.Minute)
|
||||
req = require.New(t)
|
||||
s = session{server: Server(nil, options.WebsocketOpt{})}
|
||||
|
||||
userID uint64 = 123
|
||||
token []byte
|
||||
@ -28,6 +28,9 @@ func TestSession_procRawMessage(t *testing.T) {
|
||||
}
|
||||
)
|
||||
|
||||
jwtManager, err := auth.NewJWTManager(nil, jwa.HS512, "secret", time.Minute)
|
||||
req.NoError(err)
|
||||
|
||||
if testing.Verbose() {
|
||||
s.logger = logger.MakeDebugLogger()
|
||||
} else {
|
||||
@ -36,7 +39,7 @@ func TestSession_procRawMessage(t *testing.T) {
|
||||
|
||||
req.NoError(err)
|
||||
|
||||
token, err = jwtHandler.Encode(auth.Authenticated(userID, 456, 789), 0, "api")
|
||||
token, err = jwtManager.Sign("access-token", auth.Authenticated(userID, 456, 789), 0, "api")
|
||||
req.NoError(err)
|
||||
|
||||
req.EqualError(s.procRawMessage([]byte("{}")), "unauthenticated session")
|
||||
@ -53,7 +56,7 @@ func TestSession_procRawMessage(t *testing.T) {
|
||||
req.Equal(userID, s.identity.Identity())
|
||||
|
||||
// Repeat with the same user
|
||||
token, err = jwtHandler.Encode(auth.Authenticated(userID, 456, 789), 0, "api")
|
||||
token, err = jwtManager.Sign("access-token", auth.Authenticated(userID, 456, 789), 0, "api")
|
||||
req.NoError(err)
|
||||
|
||||
req.NoError(s.procRawMessage(mockResponse(token)))
|
||||
@ -61,9 +64,10 @@ func TestSession_procRawMessage(t *testing.T) {
|
||||
req.Equal(userID, s.identity.Identity())
|
||||
|
||||
// Try to authenticate on an existing authenticated session as a different user
|
||||
token, err = jwtHandler.Encode(auth.Authenticated(userID+1, 456, 789), 0, "api")
|
||||
token, err = jwtManager.Sign("access-token", auth.Authenticated(userID+1, 456, 789), 0, "api")
|
||||
req.NoError(err)
|
||||
|
||||
req.EqualError(s.procRawMessage(mockResponse(token)), "unauthorized: identity does not match")
|
||||
|
||||
t.Error("are we actually checking if access token exists?")
|
||||
}
|
||||
|
||||
7
store/auth_oa2tokens.gen.go
generated
7
store/auth_oa2tokens.gen.go
generated
@ -10,12 +10,14 @@ package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
AuthOa2tokens interface {
|
||||
SearchAuthOa2tokens(ctx context.Context, f types.AuthOa2tokenFilter) (types.AuthOa2tokenSet, types.AuthOa2tokenFilter, error)
|
||||
LookupAuthOa2tokenByID(ctx context.Context, id uint64) (*types.AuthOa2token, error)
|
||||
LookupAuthOa2tokenByCode(ctx context.Context, code string) (*types.AuthOa2token, error)
|
||||
LookupAuthOa2tokenByAccess(ctx context.Context, access string) (*types.AuthOa2token, error)
|
||||
LookupAuthOa2tokenByRefresh(ctx context.Context, refresh string) (*types.AuthOa2token, error)
|
||||
@ -54,6 +56,11 @@ func SearchAuthOa2tokens(ctx context.Context, s AuthOa2tokens, f types.AuthOa2to
|
||||
return s.SearchAuthOa2tokens(ctx, f)
|
||||
}
|
||||
|
||||
// LookupAuthOa2tokenByID
|
||||
func LookupAuthOa2tokenByID(ctx context.Context, s AuthOa2tokens, id uint64) (*types.AuthOa2token, error) {
|
||||
return s.LookupAuthOa2tokenByID(ctx, id)
|
||||
}
|
||||
|
||||
// LookupAuthOa2tokenByCode
|
||||
func LookupAuthOa2tokenByCode(ctx context.Context, s AuthOa2tokens, code string) (*types.AuthOa2token, error) {
|
||||
return s.LookupAuthOa2tokenByCode(ctx, code)
|
||||
|
||||
@ -20,6 +20,7 @@ rdbms:
|
||||
customFilterConverter: true
|
||||
|
||||
lookups:
|
||||
- fields: [ ID ]
|
||||
- fields: [ Code ]
|
||||
uniqueConstraintCheck: true
|
||||
- fields: [ Access ]
|
||||
|
||||
8
store/rdbms/auth_oa2tokens.gen.go
generated
8
store/rdbms/auth_oa2tokens.gen.go
generated
@ -11,6 +11,7 @@ package rdbms
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/store"
|
||||
@ -85,6 +86,13 @@ func (s Store) QueryAuthOa2tokens(
|
||||
return set, nil
|
||||
}
|
||||
|
||||
// LookupAuthOa2tokenByID
|
||||
func (s Store) LookupAuthOa2tokenByID(ctx context.Context, id uint64) (*types.AuthOa2token, error) {
|
||||
return s.execLookupAuthOa2token(ctx, squirrel.Eq{
|
||||
s.preprocessColumn("tkn.id", ""): store.PreprocessValue(id, ""),
|
||||
})
|
||||
}
|
||||
|
||||
// LookupAuthOa2tokenByCode
|
||||
func (s Store) LookupAuthOa2tokenByCode(ctx context.Context, code string) (*types.AuthOa2token, error) {
|
||||
return s.execLookupAuthOa2token(ctx, squirrel.Eq{
|
||||
|
||||
@ -2,6 +2,7 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/cli"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -14,8 +14,12 @@ import (
|
||||
var _ = errors.Wrap
|
||||
|
||||
type (
|
||||
tokenGenerator interface {
|
||||
Generate(ctx context.Context, i auth.Identifiable, clientID uint64, scope ...string) (token []byte, err error)
|
||||
}
|
||||
|
||||
Auth struct {
|
||||
tokenHandler auth.TokenGenerator
|
||||
tokenHandler tokenGenerator
|
||||
settings *types.AppSettings
|
||||
authSvc authUserService
|
||||
}
|
||||
@ -47,7 +51,7 @@ type (
|
||||
|
||||
func (Auth) New() *Auth {
|
||||
return &Auth{
|
||||
tokenHandler: auth.DefaultJwtHandler,
|
||||
tokenHandler: auth.JWT(),
|
||||
settings: service.CurrentSettings,
|
||||
authSvc: service.DefaultAuth,
|
||||
}
|
||||
|
||||
@ -8,39 +8,41 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/system/service"
|
||||
)
|
||||
|
||||
func MountRoutes(r chi.Router) {
|
||||
r.Group(func(r chi.Router) {
|
||||
handlers.NewLocale(Locale{}.New()).MountRoutes(r)
|
||||
func MountRoutes(mv auth.MiddlewareValidator) func(r chi.Router) {
|
||||
return func(r chi.Router) {
|
||||
r.Group(func(r chi.Router) {
|
||||
handlers.NewLocale(Locale{}.New()).MountRoutes(r)
|
||||
|
||||
handlers.NewAttachment(Attachment{}.New()).MountRoutes(r)
|
||||
handlers.NewAuth((Auth{}).New()).MountRoutes(r)
|
||||
handlers.NewAttachment(Attachment{}.New()).MountRoutes(r)
|
||||
handlers.NewAuth((Auth{}).New()).MountRoutes(r)
|
||||
|
||||
// A special case that, we do not add this through standard request, handlers & controllers
|
||||
// combo but directly -- we need access to r.Body
|
||||
r.Handle(service.SinkBaseURL+"*", &Sink{
|
||||
svc: service.DefaultSink,
|
||||
sign: auth.DefaultSigner,
|
||||
// A special case that, we do not add this through standard request, handlers & controllers
|
||||
// combo but directly -- we need access to r.Body
|
||||
r.Handle(service.SinkBaseURL+"*", &Sink{
|
||||
svc: service.DefaultSink,
|
||||
sign: auth.DefaultSigner,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Protect all _private_ routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(auth.MiddlewareValidOnly)
|
||||
// Protect all _private_ routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(mv.HttpValidator("api"))
|
||||
|
||||
handlers.NewAuthClient(AuthClient{}.New()).MountRoutes(r)
|
||||
handlers.NewAutomation(Automation{}.New()).MountRoutes(r)
|
||||
handlers.NewUser(User{}.New()).MountRoutes(r)
|
||||
handlers.NewRole(Role{}.New()).MountRoutes(r)
|
||||
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
|
||||
handlers.NewApplication(Application{}.New()).MountRoutes(r)
|
||||
handlers.NewTemplate(Template{}.New()).MountRoutes(r)
|
||||
handlers.NewReport(Report{}.New()).MountRoutes(r)
|
||||
handlers.NewSettings(Settings{}.New()).MountRoutes(r)
|
||||
handlers.NewStats(Stats{}.New()).MountRoutes(r)
|
||||
handlers.NewReminder(Reminder{}.New()).MountRoutes(r)
|
||||
handlers.NewActionlog(Actionlog{}.New()).MountRoutes(r)
|
||||
handlers.NewQueues(Queue{}.New()).MountRoutes(r)
|
||||
handlers.NewApigwRoute(ApigwRoute{}.New()).MountRoutes(r)
|
||||
handlers.NewApigwFilter(ApigwFilter{}.New()).MountRoutes(r)
|
||||
})
|
||||
handlers.NewAuthClient(AuthClient{}.New()).MountRoutes(r)
|
||||
handlers.NewAutomation(Automation{}.New()).MountRoutes(r)
|
||||
handlers.NewUser(User{}.New()).MountRoutes(r)
|
||||
handlers.NewRole(Role{}.New()).MountRoutes(r)
|
||||
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
|
||||
handlers.NewApplication(Application{}.New()).MountRoutes(r)
|
||||
handlers.NewTemplate(Template{}.New()).MountRoutes(r)
|
||||
handlers.NewReport(Report{}.New()).MountRoutes(r)
|
||||
handlers.NewSettings(Settings{}.New()).MountRoutes(r)
|
||||
handlers.NewStats(Stats{}.New()).MountRoutes(r)
|
||||
handlers.NewReminder(Reminder{}.New()).MountRoutes(r)
|
||||
handlers.NewActionlog(Actionlog{}.New()).MountRoutes(r)
|
||||
handlers.NewQueues(Queue{}.New()).MountRoutes(r)
|
||||
handlers.NewApigwRoute(ApigwRoute{}.New()).MountRoutes(r)
|
||||
handlers.NewApigwFilter(ApigwFilter{}.New()).MountRoutes(r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
sqlxTypes "github.com/jmoiron/sqlx/types"
|
||||
"time"
|
||||
|
||||
sqlxTypes "github.com/jmoiron/sqlx/types"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@ -79,7 +79,7 @@ func InitTestApp() {
|
||||
helpers.BindAuthMiddleware(r)
|
||||
|
||||
// Sys routes for route management tests
|
||||
rest.MountRoutes(r)
|
||||
r.Group(rest.MountRoutes(auth.JWT()))
|
||||
|
||||
// API gw routes
|
||||
apigw.Setup(options.Apigw(), service.DefaultLogger, service.DefaultStore)
|
||||
@ -111,7 +111,7 @@ func newHelper(t *testing.T) helper {
|
||||
helpers.UpdateRBAC(h.roleID)
|
||||
|
||||
var err error
|
||||
h.token, err = auth.DefaultJwtHandler.Generate(context.Background(), h.cUser, 0)
|
||||
h.token, err = auth.JWT().Generate(context.Background(), h.cUser, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ func InitTestApp() {
|
||||
r = chi.NewRouter()
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
helpers.BindAuthMiddleware(r)
|
||||
rest.MountRoutes(r)
|
||||
r.Group(rest.MountRoutes(auth.JWT()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ func newHelper(t *testing.T) helper {
|
||||
helpers.UpdateRBAC(h.roleID)
|
||||
|
||||
var err error
|
||||
h.token, err = auth.DefaultJwtHandler.Generate(context.Background(), h.cUser, 0)
|
||||
h.token, err = auth.JWT().Generate(context.Background(), h.cUser, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ func InitTestApp() {
|
||||
r = chi.NewRouter()
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
helpers.BindAuthMiddleware(r)
|
||||
rest.MountRoutes(r)
|
||||
r.Group(rest.MountRoutes(auth.JWT()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ func newHelper(t *testing.T) helper {
|
||||
func (h *helper) identityToHelper(u *sysTypes.User) {
|
||||
var err error
|
||||
h.cUser = u
|
||||
h.token, err = auth.DefaultJwtHandler.Generate(context.Background(), u, 0)
|
||||
h.token, err = auth.JWT().Generate(context.Background(), u, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ func InitTestApp() {
|
||||
r = chi.NewRouter()
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
helpers.BindAuthMiddleware(r)
|
||||
rest.MountRoutes(r)
|
||||
r.Group(rest.MountRoutes(auth.JWT()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ func newHelper(t *testing.T) helper {
|
||||
helpers.UpdateRBAC(h.roleID)
|
||||
|
||||
var err error
|
||||
h.token, err = auth.DefaultJwtHandler.Generate(context.Background(), h.cUser, 0)
|
||||
h.token, err = auth.JWT().Generate(context.Background(), h.cUser, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -11,8 +11,8 @@ import (
|
||||
|
||||
func BindAuthMiddleware(r chi.Router) {
|
||||
r.Use(
|
||||
auth.DefaultJwtHandler.HttpVerifier(),
|
||||
auth.DefaultJwtHandler.HttpAuthenticator(),
|
||||
auth.JWT().HttpVerifier(),
|
||||
auth.JWT().HttpValidator("api"),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -102,7 +102,7 @@ func InitTestApp() {
|
||||
r = chi.NewRouter()
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
helpers.BindAuthMiddleware(r)
|
||||
rest.MountRoutes(r)
|
||||
r.Group(rest.MountRoutes(auth.JWT()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ func newHelper(t *testing.T) helper {
|
||||
helpers.UpdateRBAC(h.roleID)
|
||||
|
||||
var err error
|
||||
h.token, err = auth.DefaultJwtHandler.Generate(context.Background(), h.cUser, 0)
|
||||
h.token, err = auth.JWT().Generate(context.Background(), h.cUser, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ func InitTestApp() {
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
|
||||
helpers.BindAuthMiddleware(r)
|
||||
rest.MountRoutes(r)
|
||||
r.Group(rest.MountRoutes(auth.JWT()))
|
||||
hh.MountHttpRoutes(r)
|
||||
}
|
||||
}
|
||||
@ -142,7 +142,7 @@ func newHelper(t *testing.T) helper {
|
||||
h.mockPermissionsWithAccess()
|
||||
|
||||
var err error
|
||||
h.token, err = auth.DefaultJwtHandler.Generate(context.Background(), h.cUser, 0)
|
||||
h.token, err = auth.JWT().Generate(context.Background(), h.cUser, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODQiLCJleHAiOjE2MjEyNDMwODIsImlhdCI6MTYyMTI0Mjk5MiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDg0IiwibmJmIjoxNjIxMjQyOTkyLCJzdWIiOiJ0Q3U1UFY2RWd4Y3ZVQWE5ZTU3dUoyZy1iVGtxbk5reXlISGFPdTE1eUVmWmpnV0t0MDJBdFhHZSIsImlkIjoiaWQtN2JmZmIyNmQ2YmVlNGI1YmRmODZmOGRjNGRhMmNmYzExN2Q4OWQwZiIsInVyaSI6Ii9hdXRoL2V4dGVybmFsL3NhbWwvaW5pdCIsInNhbWwtYXV0aG4tcmVxdWVzdCI6dHJ1ZX0.CU_jrc5gx6JhafzbekO-7VXLJU6jzDd-R4QyrtQIZN3jyqIZtYB466KiTFZnyYeEWEjK7GW18eHuzZFHmDpcQ9weOtvu9u0Z7UUDm3YQoG-6XgUeQKTV2i1uPzq1ZlT8iiMBUsn0kdKL2F18U4jl4Fss0_Ysdc3OqoEJ73xcu0P721ZSsg-vEwyooe1WMSosunN_HEmWOU2aC61uQwNFSRk5_JotdUEytko1Jzn9JeOnLllf8izr7z7JWEnBqN8845IV_zqiScrAptpAZRocAeMAbFPFPmj5_lL2SzKC4GF4lkoOIZ5vWRBVdfzIxvD21rVZK5rRlSdYUmT0txFoQw
|
||||
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODQiLCJpYXQiOjE2MjEyNDI5OTIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4NCIsIm5iZiI6MTYyMTI0Mjk5Miwic3ViIjoidEN1NVBWNkVneGN2VUFhOWU1N3VKMmctYlRrcW5Oa3l5SEhhT3UxNXlFZlpqZ1dLdDAyQXRYR2UiLCJpZCI6ImlkLTdiZmZiMjZkNmJlZTRiNWJkZjg2ZjhkYzRkYTJjZmMxMTdkODlkMGYiLCJ1cmkiOiIvYXV0aC9leHRlcm5hbC9zYW1sL2luaXQiLCJzYW1sLWF1dGhuLXJlcXVlc3QiOnRydWV9.fBI_TorEbXYMtoNRGApQs5_89Q9IZjV-1dwkOeF5ZC9xQ6p3Mbo3r0x4CgKzYS2n8i4mMIEUDI_C4bY5jqyVEfmrwtv4qGbhYCJjnvSu1vAncJGQNfbcWCmSW0RMiiJZzfj3whHTzmK_mLgOch07iwxKGBOyNscdZfxfJp_sDMuHePwiggGssWglCC_KWXNkGh3TPad-_mo4kc_9qUf4onyISms6uyZpbJW-BqGP-iYiTnbEGdbF-24bmbTpVBU8Arv3jQjaHq8teT9XI4vFgWHfEp497LD7snYNOn3-9S05JGWKA74wrgZwFcRBaIhfVMtOqy7YMHBJZ8NNbVH8Tg
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user