Files
kopia/internal/serverapi/client_wrappers.go
Jarek Kowalski 689ed0a851 server: refactored authentication and authorization (#871)
This formalizes the concept of a 'UI user' which is a local
user that can call APIs the same way that UI does it.

The server will now allow access to:

- UI user (identified using `--server-username` with password specified
  using `--server-password' or `--random-password`)
- remote users with usersnames/passwords specified in `--htpasswd-file`
- remote users defined in the repository using `kopia users add`
  when `--allow-repository-users` is passed.

The UI user only has access to methods specifically designated as such
(normally APIs used by the UI + few special ones such as 'shutdown').

Remote users (identified via `user@host`) don't get access to UI APIs.

There are some APIs that can be accessed by any authenticated
caller (UI or remote):

- /api/v1/flush
- /api/v1/repo/status
- /api/v1/repo/sync
- /api/v1/repo/parameters

To make this easier to understand in code, refactored server handlers
to require specifing what kind of authorization is required
at registration time.
2021-03-08 22:25:22 -08:00

139 lines
4.4 KiB
Go

package serverapi
import (
"context"
"strings"
"github.com/pkg/errors"
"github.com/kopia/kopia/internal/apiclient"
"github.com/kopia/kopia/repo/object"
"github.com/kopia/kopia/snapshot"
)
// CreateSnapshotSource creates snapshot source with a given path.
func CreateSnapshotSource(ctx context.Context, c *apiclient.KopiaAPIClient, req *CreateSnapshotSourceRequest) (*CreateSnapshotSourceResponse, error) {
resp := &CreateSnapshotSourceResponse{}
if err := c.Post(ctx, "sources", req, resp); err != nil {
return nil, errors.Wrap(err, "CreateSnapshotSource")
}
return resp, nil
}
// UploadSnapshots triggers snapshot upload on matching snapshots.
func UploadSnapshots(ctx context.Context, c *apiclient.KopiaAPIClient, match *snapshot.SourceInfo) (*MultipleSourceActionResponse, error) {
resp := &MultipleSourceActionResponse{}
if err := c.Post(ctx, "sources/upload"+matchSourceParameters(match), &Empty{}, resp); err != nil {
return nil, errors.Wrap(err, "UploadSnapshots")
}
return resp, nil
}
// CancelUpload cancels snapshot upload on matching snapshots.
func CancelUpload(ctx context.Context, c *apiclient.KopiaAPIClient, match *snapshot.SourceInfo) (*MultipleSourceActionResponse, error) {
resp := &MultipleSourceActionResponse{}
if err := c.Post(ctx, "sources/cancel"+matchSourceParameters(match), &Empty{}, resp); err != nil {
return nil, errors.Wrap(err, "CancelUpload")
}
return resp, nil
}
// CreateRepository invokes the 'repo/create' API.
func CreateRepository(ctx context.Context, c *apiclient.KopiaAPIClient, req *CreateRepositoryRequest) error {
return c.Post(ctx, "repo/create", req, &StatusResponse{})
}
// ConnectToRepository invokes the 'repo/connect' API.
func ConnectToRepository(ctx context.Context, c *apiclient.KopiaAPIClient, req *ConnectRepositoryRequest) error {
return c.Post(ctx, "repo/connect", req, &StatusResponse{})
}
// DisconnectFromRepository invokes the 'repo/disconnect' API.
func DisconnectFromRepository(ctx context.Context, c *apiclient.KopiaAPIClient) error {
return c.Post(ctx, "repo/disconnect", &Empty{}, &Empty{})
}
// Shutdown invokes the 'repo/shutdown' API.
func Shutdown(ctx context.Context, c *apiclient.KopiaAPIClient) error {
return c.Post(ctx, "shutdown", &Empty{}, &Empty{})
}
// Status invokes the 'repo/status' API.
func Status(ctx context.Context, c *apiclient.KopiaAPIClient) (*StatusResponse, error) {
resp := &StatusResponse{}
if err := c.Get(ctx, "repo/status", nil, resp); err != nil {
return nil, errors.Wrap(err, "Status")
}
return resp, nil
}
// ListSources lists the snapshot sources managed by the server.
func ListSources(ctx context.Context, c *apiclient.KopiaAPIClient, match *snapshot.SourceInfo) (*SourcesResponse, error) {
resp := &SourcesResponse{}
if err := c.Get(ctx, "sources"+matchSourceParameters(match), nil, resp); err != nil {
return nil, errors.Wrap(err, "ListSources")
}
return resp, nil
}
// ListSnapshots lists the snapshots managed by the server for a given source filter.
func ListSnapshots(ctx context.Context, c *apiclient.KopiaAPIClient, match *snapshot.SourceInfo) (*SnapshotsResponse, error) {
resp := &SnapshotsResponse{}
if err := c.Get(ctx, "snapshots"+matchSourceParameters(match), nil, resp); err != nil {
return nil, errors.Wrap(err, "ListSnapshots")
}
return resp, nil
}
// ListPolicies lists the policies managed by the server for a given target filter.
func ListPolicies(ctx context.Context, c *apiclient.KopiaAPIClient, match *snapshot.SourceInfo) (*PoliciesResponse, error) {
resp := &PoliciesResponse{}
if err := c.Get(ctx, "policies"+matchSourceParameters(match), nil, resp); err != nil {
return nil, errors.Wrap(err, "ListPolicies")
}
return resp, nil
}
// GetObject returns the object payload.
func GetObject(ctx context.Context, c *apiclient.KopiaAPIClient, objectID string) ([]byte, error) {
var b []byte
if err := c.Get(ctx, "objects/"+objectID, object.ErrObjectNotFound, &b); err != nil {
return nil, errors.Wrap(err, "GetObject")
}
return b, nil
}
func matchSourceParameters(match *snapshot.SourceInfo) string {
if match == nil {
return ""
}
var clauses []string
if v := match.Host; v != "" {
clauses = append(clauses, "host="+v)
}
if v := match.UserName; v != "" {
clauses = append(clauses, "username="+v)
}
if v := match.Path; v != "" {
clauses = append(clauses, "path="+v)
}
if len(clauses) == 0 {
return ""
}
return "?" + strings.Join(clauses, "&")
}