File: auth.go

package info (click to toggle)
incus 6.0.6-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 25,220 kB
  • sloc: sh: 16,810; ansic: 3,122; python: 460; makefile: 341; ruby: 51; sql: 50; lisp: 6
file content (171 lines) | stat: -rw-r--r-- 4,750 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package auth

import (
	"crypto/x509"
	"errors"
	"fmt"

	"go.starlark.net/starlark"

	"github.com/lxc/incus/v6/internal/server/auth/common"
	scriptletLoad "github.com/lxc/incus/v6/internal/server/scriptlet/load"
	"github.com/lxc/incus/v6/internal/server/scriptlet/log"
	"github.com/lxc/incus/v6/shared/api"
	"github.com/lxc/incus/v6/shared/logger"
	"github.com/lxc/incus/v6/shared/scriptlet"
)

// AuthorizationRun runs the authorization scriptlet.
func AuthorizationRun(l logger.Logger, details *common.RequestDetails, peerCertificates []*x509.Certificate, apiCertificate *api.CertificatePut, 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
	// 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 false, err
	}

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

	globals.Freeze()

	// Retrieve a global variable from starlark environment.
	authorizer := globals["authorize"]
	if authorizer == nil {
		return false, errors.New("Scriptlet missing authorize function")
	}

	extendedDetails := struct {
		*common.RequestDetails
		Chain       []*x509.Certificate
		Certificate *api.CertificatePut
	}{
		details,
		peerCertificates,
		apiCertificate,
	}

	detailsv, err := scriptlet.StarlarkMarshal(extendedDetails)
	if err != nil {
		return false, fmt.Errorf("Marshalling details failed: %w", err)
	}

	// Call starlark function from Go.
	v, err := starlark.Call(thread, authorizer, nil, []starlark.Tuple{
		{
			starlark.String("details"),
			detailsv,
		}, {
			starlark.String("object"),
			starlark.String(object),
		}, {
			starlark.String("entitlement"),
			starlark.String(entitlement),
		},
	})
	if err != nil {
		return false, fmt.Errorf("Failed to run: %w", err)
	}

	if v.Type() != "bool" {
		return false, fmt.Errorf("Failed with unexpected return value: %v", v)
	}

	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 := scriptlet.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),
		},
	})
}