mirror of
https://github.com/dolthub/dolt.git
synced 2026-03-06 00:58:43 -06:00
Split Authentication and Authorization in the chunkstore API impl
This commit is contained in:
@@ -384,7 +384,7 @@ func Serve(
|
||||
}
|
||||
|
||||
ctxFactory := func() (*sql.Context, error) { return sqlEngine.NewDefaultContext(ctx) }
|
||||
authenticator := newAuthenticator(ctxFactory, sqlEngine.GetUnderlyingEngine().Analyzer.Catalog.MySQLDb)
|
||||
authenticator := newAccessController(ctxFactory, sqlEngine.GetUnderlyingEngine().Analyzer.Catalog.MySQLDb)
|
||||
args = sqle.WithUserPasswordAuth(args, authenticator)
|
||||
args.TLSConfig = serverConf.TLSConfig
|
||||
|
||||
@@ -587,29 +587,56 @@ func acquireGlobalSqlServerLock(port int, dEnv *env.DoltEnv) (*env.DBLock, error
|
||||
return &lck, nil
|
||||
}
|
||||
|
||||
// remotesapiAuth facilitates the implementation remotesrv.AccessControl for the remotesapi server.
|
||||
type remotesapiAuth struct {
|
||||
// ctxFactory is a function that returns a new sql.Context. This will create a new conext every time it is called,
|
||||
// so it should be called once per API request.
|
||||
ctxFactory func() (*sql.Context, error)
|
||||
rawDb *mysql_db.MySQLDb
|
||||
}
|
||||
|
||||
func newAuthenticator(ctxFactory func() (*sql.Context, error), rawDb *mysql_db.MySQLDb) remotesrv.Authenticator {
|
||||
func newAccessController(ctxFactory func() (*sql.Context, error), rawDb *mysql_db.MySQLDb) remotesrv.AccessControl {
|
||||
return &remotesapiAuth{ctxFactory, rawDb}
|
||||
}
|
||||
|
||||
func (r *remotesapiAuth) Authenticate(creds *remotesrv.RequestCredentials) bool {
|
||||
// ApiAuthenticate checks the provided credentials against the database and return a SQL context if the credentials are
|
||||
// valid. If the credentials are invalid, then a nil context is returned. Failures to authenticate are logged.
|
||||
func (r *remotesapiAuth) ApiAuthenticate(creds *remotesrv.RequestCredentials, lgr *logrus.Entry) *sql.Context {
|
||||
err := commands.ValidatePasswordWithAuthResponse(r.rawDb, creds.Username, creds.Password)
|
||||
if err != nil {
|
||||
return false
|
||||
lgr.Warnf("API Authentication Failure: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, err := r.ctxFactory()
|
||||
if err != nil {
|
||||
return false
|
||||
lgr.Warnf("API Runtime error: %v", err)
|
||||
return nil
|
||||
}
|
||||
ctx.Session.SetClient(sql.Client{User: creds.Username, Address: creds.Address, Capabilities: 0})
|
||||
|
||||
address := creds.Address
|
||||
if strings.Index(address, ":") > 0 {
|
||||
address, _, err = net.SplitHostPort(creds.Address)
|
||||
if err != nil {
|
||||
lgr.Warnf("Invalid Host string for authentication: %s", creds.Address)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Session.SetClient(sql.Client{User: creds.Username, Address: address, Capabilities: 0})
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (r *remotesapiAuth) ApiAuthorize(ctx *sql.Context, lgr *logrus.Entry) bool {
|
||||
privOp := sql.NewDynamicPrivilegedOperation(plan.DynamicPrivilege_CloneAdmin)
|
||||
return r.rawDb.UserHasPrivileges(ctx, privOp)
|
||||
authorized := r.rawDb.UserHasPrivileges(ctx, privOp)
|
||||
|
||||
if !authorized {
|
||||
lgr.Warnf("API Authorization Failure: %s has not been granted CLONE_ADMIN access", ctx.Session.Client().User)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func LoadClusterTLSConfig(cfg cluster.Config) (*tls.Config, error) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -34,12 +35,13 @@ type RequestCredentials struct {
|
||||
}
|
||||
|
||||
type ServerInterceptor struct {
|
||||
Lgr *logrus.Entry
|
||||
Authenticator Authenticator
|
||||
Lgr *logrus.Entry
|
||||
AccessController AccessControl
|
||||
}
|
||||
|
||||
type Authenticator interface {
|
||||
Authenticate(creds *RequestCredentials) bool
|
||||
type AccessControl interface {
|
||||
ApiAuthenticate(creds *RequestCredentials, lgr *logrus.Entry) *sql.Context
|
||||
ApiAuthorize(ctx *sql.Context, lgr *logrus.Entry) bool
|
||||
}
|
||||
|
||||
func (si *ServerInterceptor) Stream() grpc.StreamServerInterceptor {
|
||||
@@ -69,6 +71,8 @@ func (si *ServerInterceptor) Options() []grpc.ServerOption {
|
||||
}
|
||||
}
|
||||
|
||||
// authenticate checks the incoming request for authentication credentials and validates them. If the user is
|
||||
// legitimate, an authorization check is performed. If no error is returned, the user should be allowed to proceed.
|
||||
func (si *ServerInterceptor) authenticate(ctx context.Context) error {
|
||||
if md, ok := metadata.FromIncomingContext(ctx); ok {
|
||||
var username string
|
||||
@@ -98,9 +102,18 @@ func (si *ServerInterceptor) authenticate(ctx context.Context) error {
|
||||
si.Lgr.Info("incoming request had no peer")
|
||||
return status.Error(codes.Unauthenticated, "unauthenticated")
|
||||
}
|
||||
if authed := si.Authenticator.Authenticate(&RequestCredentials{Username: username, Password: password, Address: addr.Addr.String()}); !authed {
|
||||
|
||||
creds := &RequestCredentials{Username: username, Password: password, Address: addr.Addr.String()}
|
||||
sqlCtx := si.AccessController.ApiAuthenticate(creds, si.Lgr)
|
||||
if sqlCtx == nil {
|
||||
return status.Error(codes.Unauthenticated, "unauthenticated")
|
||||
}
|
||||
|
||||
if authorized := si.AccessController.ApiAuthorize(sqlCtx, si.Lgr); !authorized {
|
||||
return status.Error(codes.PermissionDenied, "unauthorized")
|
||||
}
|
||||
|
||||
// Access Granted.
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -78,10 +78,10 @@ func RemoteSrvServerArgs(ctxFactory func(context.Context) (*sql.Context, error),
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func WithUserPasswordAuth(args remotesrv.ServerArgs, auth remotesrv.Authenticator) remotesrv.ServerArgs {
|
||||
func WithUserPasswordAuth(args remotesrv.ServerArgs, authnz remotesrv.AccessControl) remotesrv.ServerArgs {
|
||||
si := remotesrv.ServerInterceptor{
|
||||
Lgr: args.Logger,
|
||||
Authenticator: auth,
|
||||
Lgr: args.Logger,
|
||||
AccessController: authnz,
|
||||
}
|
||||
args.Options = append(args.Options, si.Options()...)
|
||||
return args
|
||||
|
||||
Reference in New Issue
Block a user