Skip to content

Commit

Permalink
Use Authorizer interface across the code base
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Hipp <[email protected]>
  • Loading branch information
monstermunchkin committed Aug 1, 2023
1 parent 04655dc commit f634240
Show file tree
Hide file tree
Showing 16 changed files with 106 additions and 99 deletions.
10 changes: 1 addition & 9 deletions lxd/api_1.0.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"strings"

"github.com/canonical/lxd/client"
"github.com/canonical/lxd/lxd/auth"
"github.com/canonical/lxd/lxd/auth/candid"
"github.com/canonical/lxd/lxd/auth/oidc"
"github.com/canonical/lxd/lxd/cluster"
Expand Down Expand Up @@ -375,7 +374,7 @@ func api10Get(d *Daemon, r *http.Request) response.Response {
fullSrv.AuthUserName = requestor.Username
fullSrv.AuthUserMethod = requestor.Protocol

if auth.UserIsAdmin(r) {
if s.Authorizer.UserIsAdmin(r) {
fullSrv.Config, err = daemonConfigRender(s)
if err != nil {
return response.InternalError(err)
Expand Down Expand Up @@ -911,13 +910,6 @@ func doApi10UpdateTriggers(d *Daemon, nodeChanged, clusterChanged map[string]str
if rbacChanged {
apiURL, apiKey, apiExpiry, agentURL, agentUsername, agentPrivateKey, agentPublicKey := clusterConfig.RBACServer()

// Since RBAC seems to have been set up already, we need to disable it temporarily
if d.rbac != nil {
d.candidVerifier = nil
d.rbac.StopStatusCheck()
d.rbac = nil
}

err := d.setupRBACServer(apiURL, apiKey, apiExpiry, agentURL, agentUsername, agentPrivateKey, agentPublicKey)
if err != nil {
return err
Expand Down
47 changes: 23 additions & 24 deletions lxd/api_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (

"github.com/gorilla/mux"

"github.com/canonical/lxd/lxd/auth"
"github.com/canonical/lxd/lxd/db"
"github.com/canonical/lxd/lxd/db/cluster"
"github.com/canonical/lxd/lxd/db/operationtype"
Expand Down Expand Up @@ -136,18 +135,20 @@ var projectStateCmd = APIEndpoint{
// "500":
// $ref: "#/responses/InternalServerError"
func projectsGet(d *Daemon, r *http.Request) response.Response {
s := d.State()

recursion := util.IsRecursionRequest(r)

var result any
err := d.State().DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
err := s.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
projects, err := cluster.GetProjects(ctx, tx.Tx())
if err != nil {
return err
}

filtered := []api.Project{}
for _, project := range projects {
if !auth.UserHasPermission(r, project.Name, "view") {
if !s.Authorizer.UserHasPermission(r, project.Name, "view") {
continue
}

Expand Down Expand Up @@ -334,11 +335,9 @@ func projectsPost(d *Daemon, r *http.Request) response.Response {
return response.SmartError(fmt.Errorf("Failed creating project %q: %w", project.Name, err))
}

if d.rbac != nil {
err = d.rbac.AddProject(id, project.Name)
if err != nil {
return response.SmartError(err)
}
err = s.Authorizer.AddProject(id, project.Name)
if err != nil {
return response.SmartError(err)
}

requestor := request.CreateRequestor(r)
Expand Down Expand Up @@ -399,19 +398,21 @@ func projectCreateDefaultProfile(tx *db.ClusterTx, project string) error {
// "500":
// $ref: "#/responses/InternalServerError"
func projectGet(d *Daemon, r *http.Request) response.Response {
s := d.State()

name, err := url.PathUnescape(mux.Vars(r)["name"])
if err != nil {
return response.SmartError(err)
}

// Check user permissions
if !auth.UserHasPermission(r, name, "view") {
if !s.Authorizer.UserHasPermission(r, name, "view") {
return response.Forbidden(nil)
}

// Get the database entry
var project *api.Project
err = d.State().DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
err = s.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
dbProject, err := cluster.GetProject(ctx, tx.Tx(), name)
if err != nil {
return err
Expand Down Expand Up @@ -475,7 +476,7 @@ func projectPut(d *Daemon, r *http.Request) response.Response {
}

// Check user permissions
if !auth.UserHasPermission(r, name, "manage-projects") {
if !s.Authorizer.UserHasPermission(r, name, "manage-projects") {
return response.Forbidden(nil)
}

Expand Down Expand Up @@ -566,7 +567,7 @@ func projectPatch(d *Daemon, r *http.Request) response.Response {
}

// Check user permissions
if !auth.UserHasPermission(r, name, "manage-projects") {
if !s.Authorizer.UserHasPermission(r, name, "manage-projects") {
return response.Forbidden(nil)
}

Expand Down Expand Up @@ -843,11 +844,9 @@ func projectPost(d *Daemon, r *http.Request) response.Response {
return err
}

if d.rbac != nil {
err = d.rbac.RenameProject(id, req.Name)
if err != nil {
return err
}
err = s.Authorizer.RenameProject(id, req.Name)
if err != nil {
return err
}

requestor := request.CreateRequestor(r)
Expand Down Expand Up @@ -923,11 +922,9 @@ func projectDelete(d *Daemon, r *http.Request) response.Response {
return response.SmartError(err)
}

if d.rbac != nil {
err = d.rbac.DeleteProject(id)
if err != nil {
return response.SmartError(err)
}
err = s.Authorizer.DeleteProject(id)
if err != nil {
return response.SmartError(err)
}

requestor := request.CreateRequestor(r)
Expand Down Expand Up @@ -971,21 +968,23 @@ func projectDelete(d *Daemon, r *http.Request) response.Response {
// "500":
// $ref: "#/responses/InternalServerError"
func projectStateGet(d *Daemon, r *http.Request) response.Response {
s := d.State()

name, err := url.PathUnescape(mux.Vars(r)["name"])
if err != nil {
return response.SmartError(err)
}

// Check user permissions.
if !auth.UserHasPermission(r, name, "view") {
if !s.Authorizer.UserHasPermission(r, name, "view") {
return response.Forbidden(nil)
}

// Setup the state struct.
state := api.ProjectState{}

// Get current limits and usage.
err = d.State().DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
err = s.DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
result, err := projecthelpers.GetCurrentAllocations(ctx, tx, name)
if err != nil {
return err
Expand Down
7 changes: 3 additions & 4 deletions lxd/certificates.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"github.com/gorilla/mux"

"github.com/canonical/lxd/client"
"github.com/canonical/lxd/lxd/auth"
"github.com/canonical/lxd/lxd/cluster"
clusterConfig "github.com/canonical/lxd/lxd/cluster/config"
clusterRequest "github.com/canonical/lxd/lxd/cluster/request"
Expand Down Expand Up @@ -541,7 +540,7 @@ func certificatesPost(d *Daemon, r *http.Request) response.Response {
}

// Handle requests by non-admin users.
if !auth.UserIsAdmin(r) {
if !s.Authorizer.UserIsAdmin(r) {
// Non-admin cannot issue tokens.
if req.Token {
return response.Forbidden(nil)
Expand Down Expand Up @@ -983,7 +982,7 @@ func doCertificateUpdate(d *Daemon, dbInfo api.Certificate, req api.CertificateP
// In order to prevent possible future security issues, the certificate information is
// reset in case a non-admin user is performing the update.
certProjects := req.Projects
if !auth.UserIsAdmin(r) {
if !s.Authorizer.UserIsAdmin(r) {
if r.TLS == nil {
response.Forbidden(fmt.Errorf("Cannot update certificate information"))
}
Expand Down Expand Up @@ -1128,7 +1127,7 @@ func certificateDelete(d *Daemon, r *http.Request) response.Response {
}

// Non-admins are able to delete only their own certificate.
if !auth.UserIsAdmin(r) {
if !s.Authorizer.UserIsAdmin(r) {
if r.TLS == nil {
response.Forbidden(fmt.Errorf("Cannot delete certificate"))
}
Expand Down
73 changes: 46 additions & 27 deletions lxd/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import (
"github.com/canonical/lxd/lxd/node"
"github.com/canonical/lxd/lxd/request"
"github.com/canonical/lxd/lxd/response"
"github.com/canonical/lxd/lxd/revert"
"github.com/canonical/lxd/lxd/rsync"
scriptletLoad "github.com/canonical/lxd/lxd/scriptlet/load"
"github.com/canonical/lxd/lxd/seccomp"
Expand Down Expand Up @@ -80,7 +81,6 @@ type Daemon struct {
maas *maas.Controller
bgp *bgp.Server
dns *dns.Server
rbac *auth.Server

// Event servers
devlxdEvents *events.DevLXDServer
Expand Down Expand Up @@ -238,16 +238,18 @@ func allowAuthenticated(d *Daemon, r *http.Request) response.Response {
// allowProjectPermission is a wrapper to check access against the project, its features and RBAC permission.
func allowProjectPermission(feature string, permission string) func(d *Daemon, r *http.Request) response.Response {
return func(d *Daemon, r *http.Request) response.Response {
s := d.State()

// Shortcut for speed
if auth.UserIsAdmin(r) {
if s.Authorizer.UserIsAdmin(r) {
return response.EmptySyncResponse
}

// Get the project
projectName := projectParam(r)

// Validate whether the user has the needed permission
if !auth.UserHasPermission(r, projectName, permission) {
if !s.Authorizer.UserHasPermission(r, projectName, permission) {
return response.Forbidden(nil)
}

Expand Down Expand Up @@ -538,12 +540,12 @@ func (d *Daemon) createCmd(restAPI *mux.Router, version string, c APIEndpoint) {
}

// If no external authentication configured, we're done now.
if d.candidVerifier == nil || d.rbac == nil || r.RemoteAddr == "@" {
if d.candidVerifier == nil || r.RemoteAddr == "@" {
return ua, nil
}

// Validate RBAC permissions.
ua, err = d.rbac.UserAccess(username)
ua, err = d.authorizer.UserAccess(username)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -645,7 +647,7 @@ func (d *Daemon) createCmd(restAPI *mux.Router, version string, c APIEndpoint) {
}
} else if !action.AllowUntrusted {
// Require admin privileges
if !auth.UserIsAdmin(r) {
if !d.authorizer.UserIsAdmin(r) {
return response.Forbidden(nil)
}
}
Expand Down Expand Up @@ -1823,51 +1825,68 @@ func (d *Daemon) Stop(ctx context.Context, sig os.Signal) error {

// Setup RBAC.
func (d *Daemon) setupRBACServer(rbacURL string, rbacKey string, rbacExpiry int64, rbacAgentURL string, rbacAgentUsername string, rbacAgentPrivateKey string, rbacAgentPublicKey string) error {
if d.rbac != nil || rbacURL == "" || rbacAgentURL == "" || rbacAgentUsername == "" || rbacAgentPrivateKey == "" || rbacAgentPublicKey == "" {
var err error

if rbacURL == "" || rbacAgentURL == "" || rbacAgentUsername == "" || rbacAgentPrivateKey == "" || rbacAgentPublicKey == "" {
return nil
}

// Get a new server struct
server, err := auth.NewServer(rbacURL, rbacKey, rbacAgentURL, rbacAgentUsername, rbacAgentPrivateKey, rbacAgentPublicKey)
d.candidVerifier = nil

if d.authorizer != nil {
d.authorizer.StopStatusCheck()
}

// Reset to default authorizer in case rbac fails.
d.authorizer, err = auth.LoadAuthorizer("tls", nil, logger.Log, nil)
if err != nil {
return err
}

// Set projects helper
server.ProjectsFunc = func() (map[int64]string, error) {
revert := revert.New()
defer revert.Fail()

projectsFunc := func() (map[int64]string, error) {
var result map[int64]string
err := d.db.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
err := d.State().DB.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
var err error
result, err = dbCluster.GetProjectIDsToNames(ctx, tx.Tx())
return err
})
if err != nil {
return nil, err
}

return result, err
return result, nil
}

// Perform full sync when online
go func() {
for {
err = server.SyncProjects()
if err != nil {
time.Sleep(time.Minute)
continue
}

break
}
}()
config := map[string]any{
"rbac.api.url": rbacURL,
"rbac.agent.url": rbacAgentURL,
"rbac.agent.private_key": rbacAgentPrivateKey,
"rbac.agent.public_key": rbacAgentPublicKey,
}

server.StartStatusCheck()
// Load RBAC authorizer
rbacAuthorizer, err := auth.LoadAuthorizer("rbac", config, logger.Log, projectsFunc)
if err != nil {
return err
}

d.rbac = server
revert.Add(func() {
// Stop status check in case candid fails.
rbacAuthorizer.StopStatusCheck()
})

// Enable candid authentication
d.candidVerifier, err = candid.NewVerifier(fmt.Sprintf("%s/auth", rbacURL), rbacKey, rbacExpiry, "")
if err != nil {
return err
}

d.authorizer = rbacAuthorizer

revert.Success()
return nil
}

Expand Down
5 changes: 2 additions & 3 deletions lxd/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"net/http"
"strings"

"github.com/canonical/lxd/lxd/auth"
"github.com/canonical/lxd/lxd/db"
"github.com/canonical/lxd/lxd/db/cluster"
"github.com/canonical/lxd/lxd/events"
Expand Down Expand Up @@ -64,7 +63,7 @@ func eventsSocket(s *state.State, r *http.Request, w http.ResponseWriter) error
if len(types) == 1 && types[0] == "" {
types = []string{}
for _, entry := range eventTypes {
if !auth.UserIsAdmin(r) && shared.StringInSlice(entry, privilegedEventTypes) {
if !s.Authorizer.UserIsAdmin(r) && shared.StringInSlice(entry, privilegedEventTypes) {
continue
}

Expand All @@ -79,7 +78,7 @@ func eventsSocket(s *state.State, r *http.Request, w http.ResponseWriter) error
}
}

if shared.StringInSlice(api.EventTypeLogging, types) && !auth.UserIsAdmin(r) {
if shared.StringInSlice(api.EventTypeLogging, types) && !s.Authorizer.UserIsAdmin(r) {
return api.StatusErrorf(http.StatusForbidden, "Forbidden")
}

Expand Down
Loading

0 comments on commit f634240

Please sign in to comment.