Skip to content

Commit

Permalink
Merge pull request #813 from BajuMcBites/instance-access-sb
Browse files Browse the repository at this point in the history
api: Implement new access API
  • Loading branch information
hallyn authored May 23, 2024
2 parents 2c5a38b + 54989eb commit 7391420
Show file tree
Hide file tree
Showing 31 changed files with 2,616 additions and 1,948 deletions.
17 changes: 17 additions & 0 deletions client/incus_instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -2120,6 +2120,23 @@ func (r *ProtocolIncus) UpdateInstanceState(name string, state api.InstanceState
return op, nil
}

// GetInstanceAccess returns an Access entry for the provided instance name.
func (r *ProtocolIncus) GetInstanceAccess(name string) (api.Access, error) {
access := api.Access{}

if !r.HasExtension("instance_access") {
return nil, fmt.Errorf("The server is missing the required \"instance_access\" API extension")
}

// Fetch the raw value
_, err := r.queryStruct("GET", fmt.Sprintf("/instances/%s/access", url.PathEscape(name)), nil, "", &access)
if err != nil {
return nil, err
}

return access, nil
}

// GetInstanceLogfiles returns a list of logfiles for the instance.
func (r *ProtocolIncus) GetInstanceLogfiles(name string) ([]string, error) {
path, _, err := r.instanceTypeToPath(api.InstanceTypeAny)
Expand Down
17 changes: 17 additions & 0 deletions client/incus_projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ func (r *ProtocolIncus) GetProjectState(name string) (*api.ProjectState, error)
return &projectState, nil
}

// GetProjectAccess returns an Access entry for the specified project.
func (r *ProtocolIncus) GetProjectAccess(name string) (api.Access, error) {
access := api.Access{}

if !r.HasExtension("project_access") {
return nil, fmt.Errorf("The server is missing the required \"project_access\" API extension")
}

// Fetch the raw value
_, err := r.queryStruct("GET", fmt.Sprintf("/projects/%s/access", url.PathEscape(name)), nil, "", &access)
if err != nil {
return nil, err
}

return access, nil
}

// CreateProject defines a new project.
func (r *ProtocolIncus) CreateProject(project api.ProjectsPost) error {
if !r.HasExtension("projects") {
Expand Down
3 changes: 3 additions & 0 deletions client/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ type InstanceServer interface {
GetInstanceState(name string) (state *api.InstanceState, ETag string, err error)
UpdateInstanceState(name string, state api.InstanceStatePut, ETag string) (op Operation, err error)

GetInstanceAccess(name string) (access api.Access, err error)

GetInstanceLogfiles(name string) (logfiles []string, err error)
GetInstanceLogfile(name string, filename string) (content io.ReadCloser, err error)
DeleteInstanceLogfile(name string, filename string) (err error)
Expand Down Expand Up @@ -287,6 +289,7 @@ type InstanceServer interface {
GetProjects() (projects []api.Project, err error)
GetProject(name string) (project *api.Project, ETag string, err error)
GetProjectState(name string) (project *api.ProjectState, err error)
GetProjectAccess(name string) (access api.Access, err error)
CreateProject(project api.ProjectsPost) (err error)
UpdateProject(name string, project api.ProjectPut, ETag string) (err error)
RenameProject(name string, project api.ProjectPost) (op Operation, err error)
Expand Down
26 changes: 22 additions & 4 deletions cmd/incus/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import (
type cmdInfo struct {
global *cmdGlobal

flagShowLog bool
flagResources bool
flagTarget string
flagShowAccess bool
flagShowLog bool
flagResources bool
flagTarget string
}

func (c *cmdInfo) Command() *cobra.Command {
Expand All @@ -40,7 +41,8 @@ incus info [<remote>:] [--resources]
For server information.`))

cmd.RunE = c.Run
cmd.Flags().BoolVar(&c.flagShowLog, "show-log", false, i18n.G("Show the instance's last 100 log lines?"))
cmd.Flags().BoolVar(&c.flagShowAccess, "show-access", false, i18n.G("Show the instance's access list"))
cmd.Flags().BoolVar(&c.flagShowLog, "show-log", false, i18n.G("Show the instance's recent log entries"))
cmd.Flags().BoolVar(&c.flagResources, "resources", false, i18n.G("Show the resources available to the server"))
cmd.Flags().StringVar(&c.flagTarget, "target", "", i18n.G("Cluster member name")+"``")

Expand Down Expand Up @@ -87,6 +89,22 @@ func (c *cmdInfo) Run(cmd *cobra.Command, args []string) error {
return c.remoteInfo(d)
}

if c.flagShowAccess {
access, err := d.GetInstanceAccess(cName)
if err != nil {
return err
}

data, err := yaml.Marshal(access)
if err != nil {
return err
}

fmt.Printf("%s", data)

return nil
}

return c.instanceInfo(d, conf.Remotes[remote], cName, c.flagShowLog)
}

Expand Down
18 changes: 17 additions & 1 deletion cmd/incus/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,8 @@ type cmdProjectInfo struct {
global *cmdGlobal
project *cmdProject

flagFormat string
flagShowAccess bool
flagFormat string
}

func (c *cmdProjectInfo) Command() *cobra.Command {
Expand All @@ -1016,6 +1017,7 @@ func (c *cmdProjectInfo) Command() *cobra.Command {
cmd.Short = i18n.G("Get a summary of resource allocations")
cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
`Get a summary of resource allocations`))
cmd.Flags().BoolVar(&c.flagShowAccess, "show-access", false, i18n.G("Show the instance's access list"))
cmd.Flags().StringVarP(&c.flagFormat, "format", "f", "table", i18n.G("Format (csv|json|table|yaml|compact)")+"``")

cmd.RunE = c.Run
Expand Down Expand Up @@ -1050,6 +1052,20 @@ func (c *cmdProjectInfo) Run(cmd *cobra.Command, args []string) error {
return fmt.Errorf(i18n.G("Missing project name"))
}

if c.flagShowAccess {
access, err := resource.server.GetProjectAccess(resource.name)
if err != nil {
return err
}

data, err := yaml.Marshal(access)
if err != nil {
return err
}

fmt.Printf("%s", data)
}

// Get the current allocations
projectState, err := resource.server.GetProjectState(resource.name)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions cmd/incusd/api_1.0.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ var api10 = []APIEndpoint{
instanceSnapshotCmd,
instanceSnapshotsCmd,
instanceStateCmd,
instanceAccessCmd,
eventsCmd,
imageAliasCmd,
imageAliasesCmd,
Expand Down Expand Up @@ -105,6 +106,7 @@ var api10 = []APIEndpoint{
projectCmd,
projectsCmd,
projectStateCmd,
projectAccessCmd,
storagePoolCmd,
storagePoolResourcesCmd,
storagePoolsCmd,
Expand Down
66 changes: 66 additions & 0 deletions cmd/incusd/api_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ var projectStateCmd = APIEndpoint{
Get: APIEndpointAction{Handler: projectStateGet, AccessHandler: allowPermission(auth.ObjectTypeProject, auth.EntitlementCanView, "name")},
}

var projectAccessCmd = APIEndpoint{
Path: "projects/{name}/access",

Get: APIEndpointAction{Handler: projectAccess, AccessHandler: allowPermission(auth.ObjectTypeProject, auth.EntitlementCanEdit, "name")},
}

// swagger:operation GET /1.0/projects projects projects_get
//
// Get the projects
Expand Down Expand Up @@ -1593,3 +1599,63 @@ func projectValidateRestrictedSubnets(s *state.State, value string) error {

return nil
}

// swagger:operation GET /1.0/projects/{name}/access projects project_access
//
// Get who has access to a project
//
// Gets the access information for the project.
//
// ---
// produces:
// - application/json
// responses:
// "200":
// description: Access
// schema:
// type: object
// description: Sync response
// properties:
// type:
// type: string
// description: Response type
// example: sync
// status:
// type: string
// description: Status description
// example: Success
// status_code:
// type: integer
// description: Status code
// example: 200
// metadata:
// $ref: "#/definitions/Access"
// "400":
// $ref: "#/responses/BadRequest"
// "403":
// $ref: "#/responses/Forbidden"
// "500":
// $ref: "#/responses/InternalServerError"
func projectAccess(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)
}

// Quick checks.
err = projectValidateName(name)
if err != nil {
return response.BadRequest(err)
}

// get the access struct
access, err := s.Authorizer.GetProjectAccess(context.TODO(), name)

if err != nil {
return response.InternalError(err)
}

return response.SyncResponse(true, access)
}
92 changes: 92 additions & 0 deletions cmd/incusd/instance_access.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package main

import (
"context"
"fmt"
"net/http"
"net/url"

"github.com/gorilla/mux"

internalInstance "github.com/lxc/incus/v6/internal/instance"
"github.com/lxc/incus/v6/internal/server/request"
"github.com/lxc/incus/v6/internal/server/response"
)

// swagger:operation GET /1.0/instances/{name}/access instances instance_access
//
// Get who has access to an instnace
//
// Gets the access information for the instance.
//
// ---
// produces:
// - application/json
// parameters:
// - in: query
// name: project
// description: Project name
// type: string
// responses:
// "200":
// description: Access
// schema:
// type: object
// description: Sync response
// properties:
// type:
// type: string
// description: Response type
// example: sync
// status:
// type: string
// description: Status description
// example: Success
// status_code:
// type: integer
// description: Status code
// example: 200
// metadata:
// $ref: "#/definitions/Access"
// "400":
// $ref: "#/responses/BadRequest"
// "403":
// $ref: "#/responses/Forbidden"
// "500":
// $ref: "#/responses/InternalServerError"
func instanceAccess(d *Daemon, r *http.Request) response.Response {
s := d.State()

instanceType, err := urlInstanceTypeDetect(r)
if err != nil {
return response.SmartError(err)
}

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

if internalInstance.IsSnapshot(name) {
return response.BadRequest(fmt.Errorf("Invalid instance name"))
}

// Handle requests targeted to a container on a different node
resp, err := forwardedResponseIfInstanceIsRemote(s, r, projectName, name, instanceType)
if err != nil {
return response.SmartError(err)
}

if resp != nil {
return resp
}

access, err := s.Authorizer.GetInstanceAccess(context.TODO(), projectName, mux.Vars(r)["name"])

if err != nil {
return response.InternalError(err)
}

return response.SyncResponse(true, access)
}
7 changes: 7 additions & 0 deletions cmd/incusd/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ var instanceBackupExportCmd = APIEndpoint{
Get: APIEndpointAction{Handler: instanceBackupExportGet, AccessHandler: allowPermission(auth.ObjectTypeInstance, auth.EntitlementCanManageBackups, "name")},
}

var instanceAccessCmd = APIEndpoint{
Name: "access",
Path: "instances/{name}/access",

Get: APIEndpointAction{Handler: instanceAccess, AccessHandler: allowPermission(auth.ObjectTypeInstance, auth.EntitlementCanView, "name")},
}

type instanceAutostartList []instance.Instance

func (slice instanceAutostartList) Len() int {
Expand Down
10 changes: 9 additions & 1 deletion doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2493,6 +2493,14 @@ This adds support for listing network ACLs across all projects through the `all-

This adds support for listing storage buckets across all projects through the `all-projects` parameter on the `GET /1.0/storage-pools/POOL/buckets`API.

## `resources_load
## `resources_load`

Add a new Load section to the resources API.

## `instance_access`

This introduces a new API endpoint at `GET /1.0/instances/NAME/access` which exposes who can interact with the instance and what role they have.

## `project_access`

This introduces a new API endpoint at `GET /1.0/projects/NAME/access` which exposes who can interact with the project and what role they have.
Loading

0 comments on commit 7391420

Please sign in to comment.