@@ -27,10 +27,16 @@ import (
2727 "github.com/dolthub/go-mysql-server/sql/plan"
2828 "github.com/dolthub/go-mysql-server/sql/types"
2929
30+ "github.com/dolthub/doltgresql/server/auth"
3031 "github.com/dolthub/doltgresql/server/functions/framework"
3132 pgtypes "github.com/dolthub/doltgresql/server/types"
3233)
3334
35+ var (
36+ ErrDoltProcedurePermissionDenied = errors .New ("permission denied for Dolt procedure" )
37+ ErrDoltProcedureSelectOnly = errors .New ("Dolt stored procedure may only be invoked using SELECT" )
38+ )
39+
3440func initDoltProcedures () {
3541 for _ , procDef := range dprocedures .DoltProcedures {
3642 p , err := resolveExternalStoredProcedure (nil , procDef )
@@ -40,8 +46,7 @@ func initDoltProcedures() {
4046
4147 funcVal := reflect .ValueOf (procDef .Function )
4248 varArgCallable := varArgCallableForDoltProcedure (p , funcVal )
43- noArgCallable := noArgCallableForDoltProcedure (funcVal )
44-
49+ noArgCallable := noArgCallableForDoltProcedure (p , funcVal )
4550 framework .RegisterFunction (framework.Function1 {
4651 Name : procDef .Name ,
4752 Return : pgtypes .TextArray ,
@@ -63,6 +68,11 @@ func varArgCallableForDoltProcedure(p *plan.ExternalProcedure, funcVal reflect.V
6368 funcType := funcVal .Type ()
6469
6570 return func (ctx * sql.Context , paramsAndReturn [2 ]* pgtypes.DoltgresType , val1 any ) (any , error ) {
71+ err := checkDoltProcedureAccess (ctx , p )
72+ if err != nil {
73+ return nil , err
74+ }
75+
6676 values , ok := val1 .([]any )
6777 if ! ok {
6878 return nil , sql .ErrExternalProcedureInvalidParamType .New (reflect .TypeOf (val1 ).String ())
@@ -111,8 +121,13 @@ func varArgCallableForDoltProcedure(p *plan.ExternalProcedure, funcVal reflect.V
111121
112122// noArgCallableForDoltProcedure creates a callable function that does not take any parameters. This is equivalent to
113123// calling "DOLT_PROC_NAME()".
114- func noArgCallableForDoltProcedure (funcVal reflect.Value ) func (ctx * sql.Context ) (any , error ) {
124+ func noArgCallableForDoltProcedure (p * plan. ExternalProcedure , funcVal reflect.Value ) func (ctx * sql.Context ) (any , error ) {
115125 return func (ctx * sql.Context ) (any , error ) {
126+ err := checkDoltProcedureAccess (ctx , p )
127+ if err != nil {
128+ return nil , err
129+ }
130+
116131 funcParams := []reflect.Value {reflect .ValueOf (ctx )}
117132 out := funcVal .Call (funcParams )
118133 if err , ok := out [1 ].Interface ().(error ); ok { // Only evaluates to true when error is not nil
@@ -129,6 +144,25 @@ func noArgCallableForDoltProcedure(funcVal reflect.Value) func(ctx *sql.Context)
129144 }
130145}
131146
147+ // checkDoltProcedureAccess ensures the current user is authorized as a SUPERUSER if the given |procedure| requires
148+ // admin.
149+ func checkDoltProcedureAccess (ctx * sql.Context , procedure * plan.ExternalProcedure ) error {
150+ if ! procedure .AdminOnly {
151+ return nil
152+ }
153+
154+ var userRole auth.Role
155+ auth .LockRead (func () {
156+ userRole = auth .GetRole (ctx .Client ().User )
157+ })
158+
159+ if ! userRole .IsValid () || ! userRole .IsSuperUser {
160+ return ErrDoltProcedurePermissionDenied
161+ }
162+
163+ return nil
164+ }
165+
132166func drainRowIter (ctx * sql.Context , rowIter sql.RowIter ) (any , error ) {
133167 defer rowIter .Close (ctx )
134168
0 commit comments