Skip to content

Commit

Permalink
incusd/scriptlet: Add project and instance authorization getters
Browse files Browse the repository at this point in the history
Signed-off-by: Benjamin Somers <[email protected]>
  • Loading branch information
bensmrs committed Nov 22, 2024
1 parent f5360a8 commit bed39c8
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 6 deletions.
8 changes: 4 additions & 4 deletions internal/server/auth/driver_scriptlet.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (s *Scriptlet) CheckPermission(ctx context.Context, r *http.Request, object
return nil
}

authorized, err := authScriptlet.AuthorizationRun(ctx, logger.Log, details, object.String(), string(entitlement))
authorized, err := authScriptlet.AuthorizationRun(logger.Log, details, object.String(), string(entitlement))
if err != nil {
return api.StatusErrorf(http.StatusForbidden, "Authorization scriptlet execution failed with error: %v", err)
}
Expand All @@ -40,7 +40,7 @@ func (s *Scriptlet) CheckPermission(ctx context.Context, r *http.Request, object

// GetInstanceAccess returns the list of entities who have access to the instance.
func (s *Scriptlet) GetInstanceAccess(ctx context.Context, projectName string, instanceName string) (*api.Access, error) {
return &api.Access{}, nil
return authScriptlet.GetInstanceAccessRun(logger.Log, projectName, instanceName)
}

// GetPermissionChecker returns a function that can be used to check whether a user has the required entitlement on an authorization object.
Expand All @@ -61,7 +61,7 @@ func (s *Scriptlet) GetPermissionChecker(ctx context.Context, r *http.Request, e
}

permissionChecker := func (o Object) bool {
authorized, err := authScriptlet.AuthorizationRun(ctx, logger.Log, details, o.String(), string(entitlement))
authorized, err := authScriptlet.AuthorizationRun(logger.Log, details, o.String(), string(entitlement))
if err != nil {
logger.Error("Authorization scriptlet execution failed", logger.Ctx{"err": err})
return false
Expand All @@ -75,7 +75,7 @@ func (s *Scriptlet) GetPermissionChecker(ctx context.Context, r *http.Request, e

// GetProjectAccess returns the list of entities who have access to the project.
func (s *Scriptlet) GetProjectAccess(ctx context.Context, projectName string) (*api.Access, error) {
return &api.Access{}, nil
return authScriptlet.GetProjectAccessRun(logger.Log, projectName)
}

func (s *Scriptlet) load(ctx context.Context, certificateCache *certificate.Cache, opts Opts) error {
Expand Down
90 changes: 88 additions & 2 deletions internal/server/scriptlet/auth/auth.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package auth

import (
"context"
"fmt"

"go.starlark.net/starlark"
Expand All @@ -10,11 +9,12 @@ import (
scriptletLoad "github.com/lxc/incus/v6/internal/server/scriptlet/load"
"github.com/lxc/incus/v6/internal/server/scriptlet/log"
"github.com/lxc/incus/v6/internal/server/scriptlet/marshal"
"github.com/lxc/incus/v6/shared/api"
"github.com/lxc/incus/v6/shared/logger"
)

// AuthorizationRun runs the authorization scriptlet.
func AuthorizationRun(ctx context.Context, l logger.Logger, details *common.RequestDetails, object string, entitlement string) (bool, error) {
func AuthorizationRun(l logger.Logger, details *common.RequestDetails, object string, entitlement string) (bool, error) {
logFunc := log.CreateLogger(l, "Authorization scriptlet")

// Remember to match the entries in scriptletLoad.AuthorizationCompile() with this list so Starlark can
Expand Down Expand Up @@ -71,3 +71,89 @@ func AuthorizationRun(ctx context.Context, l logger.Logger, details *common.Requ

return bool(v.(starlark.Bool)), nil
}

func getAccess(l logger.Logger, fun string, args []starlark.Tuple) (*api.Access, error) {
access := &api.Access{}
emptyAccess := &api.Access{}
logFunc := log.CreateLogger(l, fmt.Sprintf("Authorization scriptlet (%s)", fun))

// Remember to match the entries in scriptletLoad.AuthorizationCompile() with this list so Starlark can
// perform compile time validation of functions used.
env := starlark.StringDict{
"log_info": starlark.NewBuiltin("log_info", logFunc),
"log_warn": starlark.NewBuiltin("log_warn", logFunc),
"log_error": starlark.NewBuiltin("log_error", logFunc),
}

prog, thread, err := scriptletLoad.AuthorizationProgram()
if err != nil {
return emptyAccess, err
}

globals, err := prog.Init(thread, env)
if err != nil {
return emptyAccess, fmt.Errorf("Failed initializing: %w", err)
}

globals.Freeze()

// Retrieve a global variable from starlark environment.
getter := globals[fun]
if getter == nil {
return emptyAccess, nil
}

// Call starlark function from Go.
v, err := starlark.Call(thread, getter, nil, args)
if err != nil {
return emptyAccess, fmt.Errorf("Failed to run: %w", err)
}

value, err := marshal.StarlarkUnmarshal(v)
if err != nil {
return emptyAccess, err
}

identifiers, ok := value.([]any)
if !ok {
return emptyAccess, fmt.Errorf("Failed with unexpected return value: %v", v)
}

for _, id := range identifiers {
identifier, ok := id.(string)
if !ok {
return emptyAccess, fmt.Errorf("Failed with unexpected return value: %v", v)
}

*access = append(*access, api.AccessEntry{
Identifier: identifier,
Role: "unknown",
Provider: "scriptlet",
})
}

return access, nil
}

// GetInstanceAccessRun runs the optional get_instance_access scriptlet function.
func GetInstanceAccessRun(l logger.Logger, projectName string, instanceName string) (*api.Access, error) {
return getAccess(l, "get_instance_access", []starlark.Tuple{
{
starlark.String("project_name"),
starlark.String(projectName),
}, {
starlark.String("instance_name"),
starlark.String(instanceName),
},
})
}

// GetProjectAccessRun runs the optional get_project_access scriptlet function.
func GetProjectAccessRun(l logger.Logger, projectName string) (*api.Access, error) {
return getAccess(l, "get_project_access", []starlark.Tuple{
{
starlark.String("project_name"),
starlark.String(projectName),
},
})
}

0 comments on commit bed39c8

Please sign in to comment.