Enforce policies

This commit is contained in:
Ingo Oppermann
2023-05-31 16:39:43 +02:00
parent 3a6bb02bfd
commit 401156e4d2
18 changed files with 1720 additions and 311 deletions

View File

@@ -236,7 +236,41 @@ func NewAPI(config APIConfig) (API, error) {
return c.JSON(http.StatusOK, "OK")
})
a.router.PUT("/v1/iam/user/:name", func(c echo.Context) error {
name := util.PathParam(c, "name")
r := client.UpdateIdentityRequest{}
if err := util.ShouldBindJSON(c, &r); err != nil {
return httpapi.Err(http.StatusBadRequest, "Invalid JSON", "%s", err)
}
origin := c.Request().Header.Get("X-Cluster-Origin")
if origin == a.id {
return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit")
}
a.logger.Debug().WithFields(log.Fields{
"name": name,
"identity": r.Identity,
}).Log("Update identity request")
err := a.cluster.UpdateIdentity(origin, name, r.Identity)
if err != nil {
a.logger.Debug().WithError(err).WithFields(log.Fields{
"name": name,
"identity": r.Identity,
}).Log("Unable to add identity")
return httpapi.Err(http.StatusInternalServerError, "unable to update identity", "%s", err)
}
return c.JSON(http.StatusOK, "OK")
})
a.router.PUT("/v1/iam/user/:name/policies", func(c echo.Context) error {
name := util.PathParam(c, "name")
r := client.SetPoliciesRequest{}
if err := util.ShouldBindJSON(c, &r); err != nil {
@@ -251,7 +285,7 @@ func NewAPI(config APIConfig) (API, error) {
a.logger.Debug().WithField("policies", r.Policies).Log("Set policiesrequest")
err = a.cluster.SetPolicies(origin, r.Name, r.Policies)
err = a.cluster.SetPolicies(origin, name, r.Policies)
if err != nil {
a.logger.Debug().WithError(err).WithField("policies", r.Policies).Log("Unable to set policies")
return httpapi.Err(http.StatusInternalServerError, "unable to add identity", "%s", err)

View File

@@ -36,6 +36,11 @@ type AddIdentityRequest struct {
Identity iamidentity.User `json:"identity"`
}
type UpdateIdentityRequest struct {
Name string `json:"name"`
Identity iamidentity.User `json:"identity"`
}
type SetPoliciesRequest struct {
Name string `json:"name"`
Policies []iamaccess.Policy `json:"policies"`
@@ -117,6 +122,17 @@ func (c *APIClient) AddIdentity(origin string, r AddIdentityRequest) error {
return err
}
func (c *APIClient) UpdateIdentity(origin, name string, r UpdateIdentityRequest) error {
data, err := json.Marshal(r)
if err != nil {
return err
}
_, err = c.call(http.MethodPut, "/iam/user/"+name, "application/json", bytes.NewReader(data), origin)
return err
}
func (c *APIClient) SetPolicies(origin, name string, r SetPoliciesRequest) error {
data, err := json.Marshal(r)
if err != nil {

View File

@@ -72,9 +72,12 @@ type Cluster interface {
UpdateProcess(origin, id string, config *app.Config) error
IAM(superuser iamidentity.User, jwtRealm, jwtSecret string) (iam.IAM, error)
ListIdentities() store.Users
ListPolicies() store.Policies
ListIdentities() (time.Time, []iamidentity.User)
ListIdentity(name string) (time.Time, iamidentity.User, error)
ListPolicies() (time.Time, []iamaccess.Policy)
ListUserPolicies(name string) (time.Time, []iamaccess.Policy)
AddIdentity(origin string, identity iamidentity.User) error
UpdateIdentity(origin, name string, identity iamidentity.User) error
SetPolicies(origin, name string, policies []iamaccess.Policy) error
RemoveIdentity(origin string, name string) error
@@ -779,12 +782,32 @@ func (c *cluster) IAM(superuser iamidentity.User, jwtRealm, jwtSecret string) (i
return iam, nil
}
func (c *cluster) ListIdentities() store.Users {
return c.store.UserList()
func (c *cluster) ListIdentities() (time.Time, []iamidentity.User) {
users := c.store.UserList()
return users.UpdatedAt, users.Users
}
func (c *cluster) ListPolicies() store.Policies {
return c.store.PolicyList()
func (c *cluster) ListIdentity(name string) (time.Time, iamidentity.User, error) {
user := c.store.GetUser(name)
if len(user.Users) == 0 {
return time.Time{}, iamidentity.User{}, fmt.Errorf("not found")
}
return user.UpdatedAt, user.Users[0], nil
}
func (c *cluster) ListPolicies() (time.Time, []iamaccess.Policy) {
policies := c.store.PolicyList()
return policies.UpdatedAt, policies.Policies
}
func (c *cluster) ListUserPolicies(name string) (time.Time, []iamaccess.Policy) {
policies := c.store.PolicyUserList(name)
return policies.UpdatedAt, policies.Policies
}
func (c *cluster) AddIdentity(origin string, identity iamidentity.User) error {
@@ -802,6 +825,22 @@ func (c *cluster) AddIdentity(origin string, identity iamidentity.User) error {
return c.applyCommand(cmd)
}
func (c *cluster) UpdateIdentity(origin, name string, identity iamidentity.User) error {
if !c.IsRaftLeader() {
return c.forwarder.UpdateIdentity(origin, name, identity)
}
cmd := &store.Command{
Operation: store.OpUpdateIdentity,
Data: &store.CommandUpdateIdentity{
Name: name,
Identity: identity,
},
}
return c.applyCommand(cmd)
}
func (c *cluster) SetPolicies(origin, name string, policies []iamaccess.Policy) error {
if !c.IsRaftLeader() {
return c.forwarder.SetPolicies(origin, name, policies)
@@ -841,7 +880,7 @@ func (c *cluster) applyCommand(cmd *store.Command) error {
err = c.raft.Apply(b)
if err != nil {
return fmt.Errorf("applying command failed: %w", err)
return fmt.Errorf("apply command: %w", err)
}
return nil

View File

@@ -27,6 +27,7 @@ type Forwarder interface {
RemoveProcess(origin, id string) error
AddIdentity(origin string, identity iamidentity.User) error
UpdateIdentity(origin, name string, identity iamidentity.User) error
SetPolicies(origin, name string, policies []iamaccess.Policy) error
RemoveIdentity(origin string, name string) error
}
@@ -198,6 +199,23 @@ func (f *forwarder) AddIdentity(origin string, identity iamidentity.User) error
return client.AddIdentity(origin, r)
}
func (f *forwarder) UpdateIdentity(origin, name string, identity iamidentity.User) error {
if origin == "" {
origin = f.id
}
r := apiclient.UpdateIdentityRequest{
Name: name,
Identity: identity,
}
f.lock.RLock()
client := f.client
f.lock.RUnlock()
return client.UpdateIdentity(origin, name, r)
}
func (f *forwarder) SetPolicies(origin, name string, policies []iamaccess.Policy) error {
if origin == "" {
origin = f.id

View File

@@ -223,6 +223,13 @@ func (r *raft) Apply(data []byte) error {
return fmt.Errorf("applying command failed: %w", err)
}
res := future.Response()
if res != nil {
if err, ok := res.(store.StoreError); ok {
return err
}
}
return nil
}

View File

@@ -24,7 +24,19 @@ type Store interface {
GetProcess(id string) (Process, error)
UserList() Users
GetUser(name string) Users
PolicyList() Policies
PolicyUserList(nam string) Policies
}
type StoreError string
func NewStoreError(format string, a ...any) StoreError {
return StoreError(fmt.Sprintf(format, a...))
}
func (se StoreError) Error() string {
return string(se)
}
type Process struct {
@@ -50,6 +62,7 @@ const (
OpRemoveProcess Operation = "removeProcess"
OpUpdateProcess Operation = "updateProcess"
OpAddIdentity Operation = "addIdentity"
OpUpdateIdentity Operation = "updateIdentity"
OpRemoveIdentity Operation = "removeIdentity"
OpSetPolicies Operation = "setPolicies"
)
@@ -76,6 +89,11 @@ type CommandAddIdentity struct {
Identity identity.User
}
type CommandUpdateIdentity struct {
Name string
Identity identity.User
}
type CommandRemoveIdentity struct {
Name string
}
@@ -138,7 +156,7 @@ func (s *store) Apply(entry *raft.Log) interface{} {
err := json.Unmarshal(entry.Data, &c)
if err != nil {
logger.Error().WithError(err).Log("Invalid entry")
return fmt.Errorf("invalid log entry")
return NewStoreError("invalid log entry, index: %d, term: %d", entry.Index, entry.Term)
}
logger.Debug().WithField("operation", c.Operation).Log("")
@@ -149,87 +167,51 @@ func (s *store) Apply(entry *raft.Log) interface{} {
cmd := CommandAddProcess{}
json.Unmarshal(b, &cmd)
s.lock.Lock()
_, ok := s.Process[cmd.ID]
if !ok {
now := time.Now()
s.Process[cmd.ID] = Process{
CreatedAt: now,
UpdatedAt: now,
Config: &cmd.Config,
}
}
s.lock.Unlock()
err = s.addProcess(cmd)
case OpRemoveProcess:
b, _ := json.Marshal(c.Data)
cmd := CommandRemoveProcess{}
json.Unmarshal(b, &cmd)
s.lock.Lock()
delete(s.Process, cmd.ID)
s.lock.Unlock()
err = s.removeProcess(cmd)
case OpUpdateProcess:
b, _ := json.Marshal(c.Data)
cmd := CommandUpdateProcess{}
json.Unmarshal(b, &cmd)
s.lock.Lock()
_, ok := s.Process[cmd.ID]
if ok {
if cmd.ID == cmd.Config.ID {
s.Process[cmd.ID] = Process{
UpdatedAt: time.Now(),
Config: &cmd.Config,
}
} else {
_, ok := s.Process[cmd.Config.ID]
if !ok {
delete(s.Process, cmd.ID)
s.Process[cmd.Config.ID] = Process{
UpdatedAt: time.Now(),
Config: &cmd.Config,
}
} else {
return fmt.Errorf("the process with the ID %s already exists", cmd.Config.ID)
}
}
}
s.lock.Unlock()
err = s.updateProcess(cmd)
case OpAddIdentity:
b, _ := json.Marshal(c.Data)
cmd := CommandAddIdentity{}
json.Unmarshal(b, &cmd)
s.lock.Lock()
_, ok := s.Users.Users[cmd.Identity.Name]
if !ok {
s.Users.UpdatedAt = time.Now()
s.Users.Users[cmd.Identity.Name] = cmd.Identity
}
s.lock.Unlock()
err = s.addIdentity(cmd)
case OpUpdateIdentity:
b, _ := json.Marshal(c.Data)
cmd := CommandUpdateIdentity{}
json.Unmarshal(b, &cmd)
err = s.updateIdentity(cmd)
case OpRemoveIdentity:
b, _ := json.Marshal(c.Data)
cmd := CommandRemoveIdentity{}
json.Unmarshal(b, &cmd)
s.lock.Lock()
delete(s.Users.Users, cmd.Name)
s.Users.UpdatedAt = time.Now()
delete(s.Policies.Policies, cmd.Name)
s.Policies.UpdatedAt = time.Now()
s.lock.Unlock()
err = s.removeIdentity(cmd)
case OpSetPolicies:
b, _ := json.Marshal(c.Data)
cmd := CommandSetPolicies{}
json.Unmarshal(b, &cmd)
s.lock.Lock()
delete(s.Policies.Policies, cmd.Name)
s.Policies.Policies[cmd.Name] = cmd.Policies
s.Policies.UpdatedAt = time.Now()
s.lock.Unlock()
err = s.setPolicies(cmd)
default:
s.logger.Warn().WithField("operation", c.Operation).Log("Unknown operation")
return nil
}
if err != nil {
logger.Debug().WithError(err).WithField("operation", c.Operation).Log("")
return err
}
s.lock.RLock()
@@ -238,9 +220,123 @@ func (s *store) Apply(entry *raft.Log) interface{} {
}
s.lock.RUnlock()
s.lock.RLock()
s.logger.Debug().WithField("processes", s.Process).Log("")
s.lock.RUnlock()
return nil
}
func (s *store) addProcess(cmd CommandAddProcess) error {
s.lock.Lock()
defer s.lock.Unlock()
_, ok := s.Process[cmd.ID]
if ok {
return NewStoreError("the process with the ID '%s' already exists", cmd.ID)
}
now := time.Now()
s.Process[cmd.ID] = Process{
CreatedAt: now,
UpdatedAt: now,
Config: &cmd.Config,
}
return nil
}
func (s *store) removeProcess(cmd CommandRemoveProcess) error {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.Process, cmd.ID)
return nil
}
func (s *store) updateProcess(cmd CommandUpdateProcess) error {
s.lock.Lock()
defer s.lock.Unlock()
_, ok := s.Process[cmd.ID]
if ok {
if cmd.ID == cmd.Config.ID {
s.Process[cmd.ID] = Process{
UpdatedAt: time.Now(),
Config: &cmd.Config,
}
} else {
_, ok := s.Process[cmd.Config.ID]
if !ok {
delete(s.Process, cmd.ID)
s.Process[cmd.Config.ID] = Process{
UpdatedAt: time.Now(),
Config: &cmd.Config,
}
} else {
return NewStoreError("the process with the ID %s already exists", cmd.Config.ID)
}
}
}
return nil
}
func (s *store) addIdentity(cmd CommandAddIdentity) error {
s.lock.Lock()
defer s.lock.Unlock()
_, ok := s.Users.Users[cmd.Identity.Name]
if ok {
return NewStoreError("the identity with the name '%s' already exists", cmd.Identity.Name)
}
s.Users.UpdatedAt = time.Now()
s.Users.Users[cmd.Identity.Name] = cmd.Identity
return nil
}
func (s *store) updateIdentity(cmd CommandUpdateIdentity) error {
s.lock.Lock()
defer s.lock.Unlock()
_, ok := s.Users.Users[cmd.Name]
if ok {
if cmd.Name == cmd.Identity.Name {
s.Users.UpdatedAt = time.Now()
s.Users.Users[cmd.Identity.Name] = cmd.Identity
} else {
_, ok := s.Users.Users[cmd.Identity.Name]
if !ok {
s.Users.UpdatedAt = time.Now()
s.Users.Users[cmd.Identity.Name] = cmd.Identity
} else {
return NewStoreError("the identity with the name '%s' already exists", cmd.Identity.Name)
}
}
}
return nil
}
func (s *store) removeIdentity(cmd CommandRemoveIdentity) error {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.Users.Users, cmd.Name)
s.Users.UpdatedAt = time.Now()
delete(s.Policies.Policies, cmd.Name)
s.Policies.UpdatedAt = time.Now()
return nil
}
func (s *store) setPolicies(cmd CommandSetPolicies) error {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.Policies.Policies, cmd.Name)
s.Policies.Policies[cmd.Name] = cmd.Policies
s.Policies.UpdatedAt = time.Now()
return nil
}
@@ -329,6 +425,21 @@ func (s *store) UserList() Users {
return u
}
func (s *store) GetUser(name string) Users {
s.lock.RLock()
defer s.lock.RUnlock()
u := Users{
UpdatedAt: s.Users.UpdatedAt,
}
if user, ok := s.Users.Users[name]; ok {
u.Users = append(u.Users, user)
}
return u
}
func (s *store) PolicyList() Policies {
s.lock.RLock()
defer s.lock.RUnlock()
@@ -344,6 +455,19 @@ func (s *store) PolicyList() Policies {
return p
}
func (s *store) PolicyUserList(name string) Policies {
s.lock.RLock()
defer s.lock.RUnlock()
p := Policies{
UpdatedAt: s.Policies.UpdatedAt,
}
p.Policies = append(p.Policies, s.Policies.Policies[name]...)
return p
}
type fsmSnapshot struct {
data []byte
}

View File

@@ -150,12 +150,6 @@ const docTemplate = `{
"schema": {
"$ref": "#/definitions/api.ClusterAbout"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
@@ -247,6 +241,44 @@ const docTemplate = `{
}
}
},
"/api/v3/cluster/db/user/{name}": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "List of identities in the cluster",
"produces": [
"application/json"
],
"tags": [
"v16.?.?"
],
"summary": "List of identities in the cluster",
"operationId": "cluster-3-db-list-identity",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.IAMUser"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
},
"/api/v3/cluster/iam/policies": {
"get": {
"security": [
@@ -276,22 +308,22 @@ const docTemplate = `{
}
}
},
"/api/v3/cluster/iam/policies/reload": {
"/api/v3/cluster/iam/reload": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Reload policies",
"description": "Reload identities and policies",
"produces": [
"application/json"
],
"tags": [
"v16.?.?"
],
"summary": "Reload policies",
"operationId": "cluster-3-iam-reload-policies",
"summary": "Reload identities and policies",
"operationId": "cluster-3-iam-reload",
"responses": {
"200": {
"description": "OK",
@@ -377,31 +409,118 @@ const docTemplate = `{
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
},
"/api/v3/cluster/iam/user/reload": {
"/api/v3/cluster/iam/user/{name}": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Reload identities",
"description": "Identity in IAM",
"produces": [
"application/json"
],
"tags": [
"v16.?.?"
],
"summary": "Reload identities",
"operationId": "cluster-3-iam-reload-identities",
"summary": "Identity in IAM",
"operationId": "cluster-3-iam-list-identity",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
"$ref": "#/definitions/api.IAMUser"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
},
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Replace an existing user.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"v16.?.?"
],
"summary": "Replace an existing user",
"operationId": "cluster-3-update-identity",
"parameters": [
{
"type": "string",
"description": "Username",
"name": "name",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain of the acting user",
"name": "domain",
"in": "query"
},
{
"description": "User definition",
"name": "user",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.IAMUser"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.IAMUser"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"500": {
@@ -411,9 +530,7 @@ const docTemplate = `{
}
}
}
}
},
"/api/v3/cluster/iam/user/{name}": {
},
"delete": {
"security": [
{
@@ -445,6 +562,12 @@ const docTemplate = `{
"type": "string"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -516,6 +639,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -671,6 +800,12 @@ const docTemplate = `{
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -727,7 +862,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.ClusterNode"
"$ref": "#/definitions/api.Version"
}
},
"404": {
@@ -755,6 +890,14 @@ const docTemplate = `{
],
"summary": "List of processes in the cluster",
"operationId": "cluster-3-list-all-node-processes",
"parameters": [
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
@@ -808,6 +951,12 @@ const docTemplate = `{
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
@@ -862,6 +1011,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -901,8 +1056,14 @@ const docTemplate = `{
"type": "string"
}
},
"404": {
"description": "Not Found",
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/api.Error"
}
@@ -930,7 +1091,7 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/github_com_datarhei_core_v16_http_api.Config"
"$ref": "#/definitions/api.GetConfig"
}
}
}
@@ -1784,6 +1945,12 @@ const docTemplate = `{
"summary": "List all known processes",
"operationId": "process-3-get-all",
"parameters": [
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{
"type": "string",
"description": "Comma separated list of fields (config, state, report, metadata) that will be part of the output. If empty, all fields will be part of the output.",
@@ -1880,6 +2047,12 @@ const docTemplate = `{
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
@@ -1908,6 +2081,12 @@ const docTemplate = `{
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{
"type": "string",
"description": "Comma separated list of fields (config, state, report, metadata) to be part of the output. If empty, all fields will be part of the output",
@@ -1922,6 +2101,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Process"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -1956,6 +2141,12 @@ const docTemplate = `{
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{
"description": "Process config",
"name": "config",
@@ -1979,6 +2170,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2009,6 +2206,12 @@ const docTemplate = `{
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2018,6 +2221,12 @@ const docTemplate = `{
"type": "string"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2054,6 +2263,12 @@ const docTemplate = `{
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{
"description": "Process command",
"name": "command",
@@ -2077,6 +2292,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2109,6 +2330,12 @@ const docTemplate = `{
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2124,6 +2351,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2163,6 +2396,12 @@ const docTemplate = `{
"name": "key",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2176,6 +2415,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2214,6 +2459,12 @@ const docTemplate = `{
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{
"description": "Arbitrary JSON data. The null value will remove the key and its contents",
"name": "data",
@@ -2233,6 +2484,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2635,6 +2892,12 @@ const docTemplate = `{
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2643,6 +2906,12 @@ const docTemplate = `{
"schema": {
"$ref": "#/definitions/api.Probe"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
@@ -2670,6 +2939,12 @@ const docTemplate = `{
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2685,6 +2960,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2717,6 +2998,12 @@ const docTemplate = `{
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2732,6 +3019,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -3900,6 +4193,29 @@ const docTemplate = `{
}
}
},
"api.GetConfig": {
"type": "object",
"properties": {
"config": {
"$ref": "#/definitions/api.ConfigData"
},
"created_at": {
"type": "string"
},
"loaded_at": {
"type": "string"
},
"overrides": {
"type": "array",
"items": {
"type": "string"
}
},
"updated_at": {
"type": "string"
}
}
},
"api.GraphQuery": {
"type": "object",
"properties": {
@@ -3945,6 +4261,9 @@ const docTemplate = `{
"domain": {
"type": "string"
},
"name": {
"type": "string"
},
"resource": {
"type": "string"
}
@@ -6022,29 +6341,6 @@ const docTemplate = `{
}
}
},
"github_com_datarhei_core_v16_http_api.Config": {
"type": "object",
"properties": {
"config": {
"$ref": "#/definitions/api.ConfigData"
},
"created_at": {
"type": "string"
},
"loaded_at": {
"type": "string"
},
"overrides": {
"type": "array",
"items": {
"type": "string"
}
},
"updated_at": {
"type": "string"
}
}
},
"value.Auth0Tenant": {
"type": "object",
"properties": {

View File

@@ -142,12 +142,6 @@
"schema": {
"$ref": "#/definitions/api.ClusterAbout"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
@@ -239,6 +233,44 @@
}
}
},
"/api/v3/cluster/db/user/{name}": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "List of identities in the cluster",
"produces": [
"application/json"
],
"tags": [
"v16.?.?"
],
"summary": "List of identities in the cluster",
"operationId": "cluster-3-db-list-identity",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.IAMUser"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
},
"/api/v3/cluster/iam/policies": {
"get": {
"security": [
@@ -268,22 +300,22 @@
}
}
},
"/api/v3/cluster/iam/policies/reload": {
"/api/v3/cluster/iam/reload": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Reload policies",
"description": "Reload identities and policies",
"produces": [
"application/json"
],
"tags": [
"v16.?.?"
],
"summary": "Reload policies",
"operationId": "cluster-3-iam-reload-policies",
"summary": "Reload identities and policies",
"operationId": "cluster-3-iam-reload",
"responses": {
"200": {
"description": "OK",
@@ -369,31 +401,118 @@
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
},
"/api/v3/cluster/iam/user/reload": {
"/api/v3/cluster/iam/user/{name}": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Reload identities",
"description": "Identity in IAM",
"produces": [
"application/json"
],
"tags": [
"v16.?.?"
],
"summary": "Reload identities",
"operationId": "cluster-3-iam-reload-identities",
"summary": "Identity in IAM",
"operationId": "cluster-3-iam-list-identity",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
"$ref": "#/definitions/api.IAMUser"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
},
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "Replace an existing user.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"v16.?.?"
],
"summary": "Replace an existing user",
"operationId": "cluster-3-update-identity",
"parameters": [
{
"type": "string",
"description": "Username",
"name": "name",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain of the acting user",
"name": "domain",
"in": "query"
},
{
"description": "User definition",
"name": "user",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.IAMUser"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.IAMUser"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"500": {
@@ -403,9 +522,7 @@
}
}
}
}
},
"/api/v3/cluster/iam/user/{name}": {
},
"delete": {
"security": [
{
@@ -437,6 +554,12 @@
"type": "string"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -508,6 +631,12 @@
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -663,6 +792,12 @@
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -719,7 +854,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/api.ClusterNode"
"$ref": "#/definitions/api.Version"
}
},
"404": {
@@ -747,6 +882,14 @@
],
"summary": "List of processes in the cluster",
"operationId": "cluster-3-list-all-node-processes",
"parameters": [
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
@@ -800,6 +943,12 @@
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
@@ -854,6 +1003,12 @@
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -893,8 +1048,14 @@
"type": "string"
}
},
"404": {
"description": "Not Found",
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/api.Error"
}
@@ -922,7 +1083,7 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/github_com_datarhei_core_v16_http_api.Config"
"$ref": "#/definitions/api.GetConfig"
}
}
}
@@ -1776,6 +1937,12 @@
"summary": "List all known processes",
"operationId": "process-3-get-all",
"parameters": [
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{
"type": "string",
"description": "Comma separated list of fields (config, state, report, metadata) that will be part of the output. If empty, all fields will be part of the output.",
@@ -1872,6 +2039,12 @@
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
@@ -1900,6 +2073,12 @@
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{
"type": "string",
"description": "Comma separated list of fields (config, state, report, metadata) to be part of the output. If empty, all fields will be part of the output",
@@ -1914,6 +2093,12 @@
"$ref": "#/definitions/api.Process"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -1948,6 +2133,12 @@
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{
"description": "Process config",
"name": "config",
@@ -1971,6 +2162,12 @@
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2001,6 +2198,12 @@
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2010,6 +2213,12 @@
"type": "string"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2046,6 +2255,12 @@
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{
"description": "Process command",
"name": "command",
@@ -2069,6 +2284,12 @@
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2101,6 +2322,12 @@
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2116,6 +2343,12 @@
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2155,6 +2388,12 @@
"name": "key",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2168,6 +2407,12 @@
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2206,6 +2451,12 @@
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{
"description": "Arbitrary JSON data. The null value will remove the key and its contents",
"name": "data",
@@ -2225,6 +2476,12 @@
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2627,6 +2884,12 @@
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2635,6 +2898,12 @@
"schema": {
"$ref": "#/definitions/api.Probe"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
@@ -2662,6 +2931,12 @@
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2677,6 +2952,12 @@
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -2709,6 +2990,12 @@
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": {
@@ -2724,6 +3011,12 @@
"$ref": "#/definitions/api.Error"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": {
"description": "Not Found",
"schema": {
@@ -3892,6 +4185,29 @@
}
}
},
"api.GetConfig": {
"type": "object",
"properties": {
"config": {
"$ref": "#/definitions/api.ConfigData"
},
"created_at": {
"type": "string"
},
"loaded_at": {
"type": "string"
},
"overrides": {
"type": "array",
"items": {
"type": "string"
}
},
"updated_at": {
"type": "string"
}
}
},
"api.GraphQuery": {
"type": "object",
"properties": {
@@ -3937,6 +4253,9 @@
"domain": {
"type": "string"
},
"name": {
"type": "string"
},
"resource": {
"type": "string"
}
@@ -6014,29 +6333,6 @@
}
}
},
"github_com_datarhei_core_v16_http_api.Config": {
"type": "object",
"properties": {
"config": {
"$ref": "#/definitions/api.ConfigData"
},
"created_at": {
"type": "string"
},
"loaded_at": {
"type": "string"
},
"overrides": {
"type": "array",
"items": {
"type": "string"
}
},
"updated_at": {
"type": "string"
}
}
},
"value.Auth0Tenant": {
"type": "object",
"properties": {

View File

@@ -588,6 +588,21 @@ definitions:
type:
type: string
type: object
api.GetConfig:
properties:
config:
$ref: '#/definitions/api.ConfigData'
created_at:
type: string
loaded_at:
type: string
overrides:
items:
type: string
type: array
updated_at:
type: string
type: object
api.GraphQuery:
properties:
query:
@@ -618,6 +633,8 @@ definitions:
type: array
domain:
type: string
name:
type: string
resource:
type: string
type: object
@@ -2079,21 +2096,6 @@ definitions:
uptime:
type: integer
type: object
github_com_datarhei_core_v16_http_api.Config:
properties:
config:
$ref: '#/definitions/api.ConfigData'
created_at:
type: string
loaded_at:
type: string
overrides:
items:
type: string
type: array
updated_at:
type: string
type: object
value.Auth0Tenant:
properties:
audience:
@@ -2231,10 +2233,6 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.ClusterAbout'
"404":
description: Not Found
schema:
$ref: '#/definitions/api.Error'
security:
- ApiKeyAuth: []
summary: List of nodes in the cluster
@@ -2294,6 +2292,30 @@ paths:
summary: List of identities in the cluster
tags:
- v16.?.?
/api/v3/cluster/db/user/{name}:
get:
description: List of identities in the cluster
operationId: cluster-3-db-list-identity
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.IAMUser'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
$ref: '#/definitions/api.Error'
security:
- ApiKeyAuth: []
summary: List of identities in the cluster
tags:
- v16.?.?
/api/v3/cluster/iam/policies:
get:
description: List of policies IAM
@@ -2312,10 +2334,10 @@ paths:
summary: List of policies in IAM
tags:
- v16.?.?
/api/v3/cluster/iam/policies/reload:
/api/v3/cluster/iam/reload:
get:
description: Reload policies
operationId: cluster-3-iam-reload-policies
description: Reload identities and policies
operationId: cluster-3-iam-reload
produces:
- application/json
responses:
@@ -2329,7 +2351,7 @@ paths:
$ref: '#/definitions/api.Error'
security:
- ApiKeyAuth: []
summary: Reload policies
summary: Reload identities and policies
tags:
- v16.?.?
/api/v3/cluster/iam/user:
@@ -2373,6 +2395,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
security:
- ApiKeyAuth: []
summary: Add a new identiy
@@ -2395,6 +2421,10 @@ paths:
description: OK
schema:
type: string
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -2404,6 +2434,78 @@ paths:
summary: Delete an identity by its name
tags:
- v16.?.?
get:
description: Identity in IAM
operationId: cluster-3-iam-list-identity
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.IAMUser'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
$ref: '#/definitions/api.Error'
security:
- ApiKeyAuth: []
summary: Identity in IAM
tags:
- v16.?.?
put:
consumes:
- application/json
description: Replace an existing user.
operationId: cluster-3-update-identity
parameters:
- description: Username
in: path
name: name
required: true
type: string
- description: Domain of the acting user
in: query
name: domain
type: string
- description: User definition
in: body
name: user
required: true
schema:
$ref: '#/definitions/api.IAMUser'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/api.IAMUser'
"400":
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
$ref: '#/definitions/api.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/api.Error'
security:
- ApiKeyAuth: []
summary: Replace an existing user
tags:
- v16.?.?
/api/v3/cluster/iam/user/{name}/policy:
put:
consumes:
@@ -2441,6 +2543,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -2454,26 +2560,6 @@ paths:
summary: Replace policies of an user
tags:
- v16.?.?
/api/v3/cluster/iam/user/reload:
get:
description: Reload identities
operationId: cluster-3-iam-reload-identities
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/api.Error'
security:
- ApiKeyAuth: []
summary: Reload identities
tags:
- v16.?.?
/api/v3/cluster/node:
get:
description: List of proxy nodes in the cluster
@@ -2558,6 +2644,10 @@ paths:
name: id
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
produces:
- application/json
responses:
@@ -2596,7 +2686,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/api.ClusterNode'
$ref: '#/definitions/api.Version'
"404":
description: Not Found
schema:
@@ -2610,6 +2700,11 @@ paths:
get:
description: List of processes in the cluster
operationId: cluster-3-list-all-node-processes
parameters:
- description: Domain to act on
in: query
name: domain
type: string
produces:
- application/json
responses:
@@ -2647,6 +2742,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
security:
- ApiKeyAuth: []
summary: Add a new process
@@ -2669,8 +2768,12 @@ paths:
description: OK
schema:
type: string
"404":
description: Not Found
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/api.Error'
security:
@@ -2706,6 +2809,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -2725,7 +2832,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/github_com_datarhei_core_v16_http_api.Config'
$ref: '#/definitions/api.GetConfig'
security:
- ApiKeyAuth: []
summary: Retrieve the currently active Restreamer configuration
@@ -3278,6 +3385,10 @@ paths:
listed processes.
operationId: process-3-get-all
parameters:
- description: Domain to act on
in: query
name: domain
type: string
- description: Comma separated list of fields (config, state, report, metadata)
that will be part of the output. If empty, all fields will be part of the
output.
@@ -3351,6 +3462,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
security:
- ApiKeyAuth: []
summary: Add a new process
@@ -3366,6 +3481,10 @@ paths:
name: id
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
produces:
- application/json
responses:
@@ -3373,6 +3492,10 @@ paths:
description: OK
schema:
type: string
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -3392,6 +3515,10 @@ paths:
name: id
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
- description: Comma separated list of fields (config, state, report, metadata)
to be part of the output. If empty, all fields will be part of the output
in: query
@@ -3404,6 +3531,10 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.Process'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -3424,6 +3555,10 @@ paths:
name: id
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
- description: Process config
in: body
name: config
@@ -3441,6 +3576,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -3462,6 +3601,10 @@ paths:
name: id
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
- description: Process command
in: body
name: command
@@ -3479,6 +3622,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -3499,6 +3646,10 @@ paths:
name: id
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
produces:
- application/json
responses:
@@ -3510,6 +3661,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -3535,6 +3690,10 @@ paths:
name: key
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
produces:
- application/json
responses:
@@ -3545,6 +3704,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -3570,6 +3733,10 @@ paths:
name: key
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
- description: Arbitrary JSON data. The null value will remove the key and its
contents
in: body
@@ -3586,6 +3753,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -3852,6 +4023,10 @@ paths:
name: id
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
produces:
- application/json
responses:
@@ -3859,6 +4034,10 @@ paths:
description: OK
schema:
$ref: '#/definitions/api.Probe'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
security:
- ApiKeyAuth: []
summary: Probe a process
@@ -3874,6 +4053,10 @@ paths:
name: id
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
produces:
- application/json
responses:
@@ -3885,6 +4068,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:
@@ -3904,6 +4091,10 @@ paths:
name: id
required: true
type: string
- description: Domain to act on
in: query
name: domain
type: string
produces:
- application/json
responses:
@@ -3915,6 +4106,10 @@ paths:
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
"403":
description: Forbidden
schema:
$ref: '#/definitions/api.Error'
"404":
description: Not Found
schema:

View File

@@ -19,8 +19,8 @@ type ConfigData struct {
config.Data
}
// Config is the config returned by the API
type Config struct {
// GetConfig is the config returned by the API
type GetConfig struct {
CreatedAt time.Time `json:"created_at"`
LoadedAt time.Time `json:"loaded_at"`
UpdatedAt time.Time `json:"updated_at"`
@@ -117,7 +117,7 @@ func (rscfg *SetConfig) MergeTo(cfg *config.Config) {
}
// Unmarshal converts a config.Config to a Config.
func (c *Config) Unmarshal(cfg *config.Config) {
func (c *GetConfig) Unmarshal(cfg *config.Config) {
if cfg == nil {
return
}

View File

@@ -106,6 +106,7 @@ type IAMAuth0Tenant struct {
}
type IAMPolicy struct {
Name string `json:"name,omitempty"`
Domain string `json:"domain"`
Resource string `json:"resource"`
Actions []string `json:"actions"`

View File

@@ -54,7 +54,6 @@ func NewCluster(cluster cluster.Cluster, iam iam.IAM) (*ClusterHandler, error) {
// @ID cluster-3-get-cluster
// @Produce json
// @Success 200 {object} api.ClusterAbout
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster [get]
func (h *ClusterHandler) About(c echo.Context) error {
@@ -91,15 +90,23 @@ func (h *ClusterHandler) About(c echo.Context) error {
// @Tags v16.?.?
// @ID cluster-3-list-all-node-processes
// @Produce json
// @Param domain query string false "Domain to act on"
// @Success 200 {array} api.ClusterProcess
// @Security ApiKeyAuth
// @Router /api/v3/cluster/process [get]
func (h *ClusterHandler) ListAllNodeProcesses(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
procs := h.proxy.ListProcesses()
processes := []api.ClusterProcess{}
for _, p := range procs {
if !h.iam.Enforce(ctxuser, domain, "process:"+p.Config.ID, "read") {
continue
}
processes = append(processes, api.ClusterProcess{
ProcessID: p.Config.ID,
NodeID: p.NodeID,
@@ -193,7 +200,7 @@ func (h *ClusterHandler) GetNode(c echo.Context) error {
// @ID cluster-3-get-node-version
// @Produce json
// @Param id path string true "Node ID"
// @Success 200 {object} api.ClusterNode
// @Success 200 {object} api.Version
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/node/{id}/version [get]
@@ -205,7 +212,16 @@ func (h *ClusterHandler) GetNodeVersion(c echo.Context) error {
return api.Err(http.StatusNotFound, "Node not found", "%s", err)
}
version := peer.Version()
v := peer.Version()
version := api.Version{
Number: v.Number,
Commit: v.Commit,
Branch: v.Branch,
Build: v.Build.Format(time.RFC3339),
Arch: v.Arch,
Compiler: v.Compiler,
}
return c.JSON(http.StatusOK, version)
}
@@ -258,12 +274,15 @@ func (h *ClusterHandler) GetNodeFiles(c echo.Context) error {
// @ID cluster-3-list-node-processes
// @Produce json
// @Param id path string true "Node ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {array} api.ClusterProcess
// @Failure 404 {object} api.Error
// @Failure 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/node/{id}/process [get]
func (h *ClusterHandler) ListNodeProcesses(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
id := util.PathParam(c, "id")
peer, err := h.proxy.GetNode(id)
@@ -279,6 +298,10 @@ func (h *ClusterHandler) ListNodeProcesses(c echo.Context) error {
processes := []api.ClusterProcess{}
for _, p := range procs {
if !h.iam.Enforce(ctxuser, domain, "process:"+p.Config.ID, "read") {
continue
}
processes = append(processes, api.ClusterProcess{
ProcessID: p.Config.ID,
NodeID: p.NodeID,
@@ -304,11 +327,18 @@ func (h *ClusterHandler) ListNodeProcesses(c echo.Context) error {
// @Security ApiKeyAuth
// @Router /api/v3/cluster/db/process [get]
func (h *ClusterHandler) ListStoreProcesses(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
procs := h.cluster.ListProcesses()
processes := []api.Process{}
for _, p := range procs {
if !h.iam.Enforce(ctxuser, domain, "process:"+p.Config.ID, "read") {
continue
}
process := api.Process{
ID: p.Config.ID,
Type: "ffmpeg",
@@ -338,11 +368,16 @@ func (h *ClusterHandler) ListStoreProcesses(c echo.Context) error {
// @Param config body api.ProcessConfig true "Process config"
// @Success 200 {object} api.ProcessConfig
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/process [post]
func (h *ClusterHandler) AddProcess(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
process := api.ProcessConfig{
ID: shortuuid.New(),
Owner: ctxuser,
Type: "ffmpeg",
Autostart: true,
}
@@ -351,6 +386,16 @@ func (h *ClusterHandler) AddProcess(c echo.Context) error {
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err)
}
if !h.iam.Enforce(ctxuser, process.Domain, "process:"+process.ID, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
if !superuser {
if !h.iam.Enforce(process.Owner, process.Domain, "process:"+process.ID, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
}
if process.Type != "ffmpeg" {
return api.Err(http.StatusBadRequest, "Unsupported process type", "Supported process types are: ffmpeg")
}
@@ -379,18 +424,28 @@ func (h *ClusterHandler) AddProcess(c echo.Context) error {
// @Param config body api.ProcessConfig true "Process config"
// @Success 200 {object} api.ProcessConfig
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/process/{id} [put]
func (h *ClusterHandler) UpdateProcess(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
id := util.PathParam(c, "id")
process := api.ProcessConfig{
ID: id,
Owner: ctxuser,
Domain: domain,
Type: "ffmpeg",
Autostart: true,
}
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
current, err := h.cluster.GetProcess(id)
if err != nil {
return api.Err(http.StatusNotFound, "Process not found", "%s", id)
@@ -403,6 +458,16 @@ func (h *ClusterHandler) UpdateProcess(c echo.Context) error {
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err)
}
if !h.iam.Enforce(ctxuser, process.Domain, "process:"+process.ID, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
if !superuser {
if !h.iam.Enforce(process.Owner, process.Domain, "process:"+process.ID, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
}
config := process.Marshal()
if err := h.cluster.UpdateProcess("", id, config); err != nil {
@@ -424,12 +489,19 @@ func (h *ClusterHandler) UpdateProcess(c echo.Context) error {
// @Produce json
// @Param id path string true "Process ID"
// @Success 200 {string} string
// @Failure 404 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/process/{id} [delete]
func (h *ClusterHandler) DeleteProcess(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "$none")
id := util.PathParam(c, "id")
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "write") {
return api.Err(http.StatusForbidden, "", "Not allowed to delete process")
}
if err := h.cluster.RemoveProcess("", id); err != nil {
return api.Err(http.StatusInternalServerError, "Process can't be deleted", "%s", err)
}
@@ -447,28 +519,128 @@ func (h *ClusterHandler) DeleteProcess(c echo.Context) error {
// @Param config body api.IAMUser true "Identity"
// @Success 200 {object} api.IAMUser
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/user [post]
func (h *ClusterHandler) AddIdentity(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
user := api.IAMUser{}
if err := util.ShouldBindJSON(c, &user); err != nil {
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err)
}
identity, policies := user.Unmarshal()
iamuser, iampolicies := user.Unmarshal()
if err := h.cluster.AddIdentity("", identity); err != nil {
if !h.iam.Enforce(ctxuser, domain, "iam:"+iamuser.Name, "write") {
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to create user '%s'", iamuser.Name)
}
for _, p := range iampolicies {
if !h.iam.Enforce(ctxuser, p.Domain, "iam:"+iamuser.Name, "write") {
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to write policy: %v", p)
}
}
if !superuser && iamuser.Superuser {
return api.Err(http.StatusForbidden, "Forbidden", "Only superusers can add superusers")
}
if err := h.cluster.AddIdentity("", iamuser); err != nil {
return api.Err(http.StatusBadRequest, "Invalid identity", "%s", err.Error())
}
if err := h.cluster.SetPolicies("", identity.Name, policies); err != nil {
if err := h.cluster.SetPolicies("", iamuser.Name, iampolicies); err != nil {
return api.Err(http.StatusBadRequest, "Invalid policies", "%s", err.Error())
}
return c.JSON(http.StatusOK, user)
}
// UpdateIdentity replaces an existing user
// @Summary Replace an existing user
// @Description Replace an existing user.
// @Tags v16.?.?
// @ID cluster-3-update-identity
// @Accept json
// @Produce json
// @Param name path string true "Username"
// @Param domain query string false "Domain of the acting user"
// @Param user body api.IAMUser true "User definition"
// @Success 200 {object} api.IAMUser
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Failure 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/user/{name} [put]
func (h *ClusterHandler) UpdateIdentity(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
name := util.PathParam(c, "name")
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to modify this user")
}
var iamuser identity.User
var err error
if name != "$anon" {
iamuser, err = h.iam.GetIdentity(name)
if err != nil {
return api.Err(http.StatusNotFound, "Not found", "%s", err)
}
} else {
iamuser = identity.User{
Name: "$anon",
}
}
iampolicies := h.iam.ListPolicies(name, "", "", nil)
user := api.IAMUser{}
user.Marshal(iamuser, iampolicies)
if err := util.ShouldBindJSON(c, &user); err != nil {
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err)
}
iamuser, iampolicies = user.Unmarshal()
if !h.iam.Enforce(ctxuser, domain, "iam:"+iamuser.Name, "write") {
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to create user '%s'", iamuser.Name)
}
for _, p := range iampolicies {
if !h.iam.Enforce(ctxuser, p.Domain, "iam:"+iamuser.Name, "write") {
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to write policy: %v", p)
}
}
if !superuser && iamuser.Superuser {
return api.Err(http.StatusForbidden, "Forbidden", "Only superusers can modify superusers")
}
if name != "$anon" {
err = h.cluster.UpdateIdentity("", name, iamuser)
if err != nil {
return api.Err(http.StatusBadRequest, "Bad request", "%s", err)
}
}
err = h.cluster.SetPolicies("", name, iampolicies)
if err != nil {
return api.Err(http.StatusInternalServerError, "", "set policies: %w", err)
}
return c.JSON(http.StatusOK, user)
}
// UpdateIdentityPolicies replaces existing user policies
// @Summary Replace policies of an user
// @Description Replace policies of an user
@@ -481,6 +653,7 @@ func (h *ClusterHandler) AddIdentity(c echo.Context) error {
// @Param user body []api.IAMPolicy true "Policy definitions"
// @Success 200 {array} api.IAMPolicy
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Failure 500 {object} api.Error
// @Security ApiKeyAuth
@@ -559,9 +732,111 @@ func (h *ClusterHandler) UpdateIdentityPolicies(c echo.Context) error {
// @Security ApiKeyAuth
// @Router /api/v3/cluster/db/user [get]
func (h *ClusterHandler) ListStoreIdentities(c echo.Context) error {
identities := h.cluster.ListIdentities()
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "$none")
return c.JSON(http.StatusOK, identities)
updatedAt, identities := h.cluster.ListIdentities()
users := make([]api.IAMUser, len(identities))
for i, iamuser := range identities {
if !h.iam.Enforce(ctxuser, domain, "iam:"+iamuser.Name, "read") {
continue
}
if !h.iam.Enforce(ctxuser, domain, "iam:"+iamuser.Name, "write") {
iamuser = identity.User{
Name: iamuser.Name,
}
}
_, policies := h.cluster.ListUserPolicies(iamuser.Name)
users[i].Marshal(iamuser, policies)
}
c.Response().Header().Set("Last-Modified", updatedAt.UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
return c.JSON(http.StatusOK, users)
}
// ListStoreIdentity returns the list of identities stored in the DB of the cluster
// @Summary List of identities in the cluster
// @Description List of identities in the cluster
// @Tags v16.?.?
// @ID cluster-3-db-list-identity
// @Produce json
// @Success 200 {object} api.IAMUser
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/db/user/{name} [get]
func (h *ClusterHandler) ListStoreIdentity(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "$none")
name := util.PathParam(c, "name")
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "read") {
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to access this user")
}
var updatedAt time.Time
var iamuser identity.User
var err error
if name != "$anon" {
updatedAt, iamuser, err = h.cluster.ListIdentity(name)
if err != nil {
return api.Err(http.StatusNotFound, "", "%s", err)
}
if ctxuser != iamuser.Name {
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
iamuser = identity.User{
Name: iamuser.Name,
}
}
}
} else {
iamuser = identity.User{
Name: "$anon",
}
}
policiesUpdatedAt, policies := h.cluster.ListUserPolicies(name)
if updatedAt.IsZero() {
updatedAt = policiesUpdatedAt
}
user := api.IAMUser{}
user.Marshal(iamuser, policies)
c.Response().Header().Set("Last-Modified", updatedAt.UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
return c.JSON(http.StatusOK, user)
}
// ReloadIAM reloads the identities and policies from the cluster store to IAM
// @Summary Reload identities and policies
// @Description Reload identities and policies
// @Tags v16.?.?
// @ID cluster-3-iam-reload
// @Produce json
// @Success 200 {string} string
// @Success 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/reload [get]
func (h *ClusterHandler) ReloadIAM(c echo.Context) error {
err := h.iam.ReloadIndentities()
if err != nil {
return api.Err(http.StatusInternalServerError, "", "reload identities: %w", err)
}
err = h.iam.ReloadPolicies()
if err != nil {
return api.Err(http.StatusInternalServerError, "", "reload policies: %w", err)
}
return c.JSON(http.StatusOK, "OK")
}
// ListIdentities returns the list of identities stored in IAM
@@ -574,28 +849,80 @@ func (h *ClusterHandler) ListStoreIdentities(c echo.Context) error {
// @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/user [get]
func (h *ClusterHandler) ListIdentities(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "$none")
identities := h.iam.ListIdentities()
return c.JSON(http.StatusOK, identities)
}
users := make([]api.IAMUser, len(identities))
// ReloadIdentities reloads the identities from the cluster store to IAM
// @Summary Reload identities
// @Description Reload identities
// @Tags v16.?.?
// @ID cluster-3-iam-reload-identities
// @Produce json
// @Success 200 {string} string
// @Success 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/user/reload [get]
func (h *ClusterHandler) ReloadIdentities(c echo.Context) error {
err := h.iam.ReloadIndentities()
if err != nil {
return api.Err(http.StatusInternalServerError, "", "reload identities: %w", err)
for i, iamuser := range identities {
if !h.iam.Enforce(ctxuser, domain, "iam:"+iamuser.Name, "read") {
continue
}
if !h.iam.Enforce(ctxuser, domain, "iam:"+iamuser.Name, "write") {
iamuser = identity.User{
Name: iamuser.Name,
}
}
policies := h.iam.ListPolicies(iamuser.Name, "", "", nil)
users[i].Marshal(iamuser, policies)
}
return c.JSON(http.StatusOK, "OK")
return c.JSON(http.StatusOK, users)
}
// ListIdentity returns the identity stored in IAM
// @Summary Identity in IAM
// @Description Identity in IAM
// @Tags v16.?.?
// @ID cluster-3-iam-list-identity
// @Produce json
// @Success 200 {object} api.IAMUser
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/user/{name} [get]
func (h *ClusterHandler) ListIdentity(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "$none")
name := util.PathParam(c, "name")
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "read") {
return api.Err(http.StatusForbidden, "", "Not allowed to access this user")
}
var iamuser identity.User
var err error
if name != "$anon" {
iamuser, err = h.iam.GetIdentity(name)
if err != nil {
return api.Err(http.StatusNotFound, "", "%s", err)
}
if ctxuser != iamuser.Name {
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
iamuser = identity.User{
Name: iamuser.Name,
}
}
}
} else {
iamuser = identity.User{
Name: "$anon",
}
}
iampolicies := h.iam.ListPolicies(name, "", "", nil)
user := api.IAMUser{}
user.Marshal(iamuser, iampolicies)
return c.JSON(http.StatusOK, user)
}
// ListStorePolicies returns the list of policies stored in the DB of the cluster
@@ -608,7 +935,20 @@ func (h *ClusterHandler) ReloadIdentities(c echo.Context) error {
// @Security ApiKeyAuth
// @Router /api/v3/cluster/db/policies [get]
func (h *ClusterHandler) ListStorePolicies(c echo.Context) error {
policies := h.cluster.ListPolicies()
updatedAt, clusterpolicies := h.cluster.ListPolicies()
policies := []api.IAMPolicy{}
for _, pol := range clusterpolicies {
policies = append(policies, api.IAMPolicy{
Name: pol.Name,
Domain: pol.Domain,
Resource: pol.Resource,
Actions: pol.Actions,
})
}
c.Response().Header().Set("Last-Modified", updatedAt.UTC().Format("Mon, 02 Jan 2006 15:04:05 GMT"))
return c.JSON(http.StatusOK, policies)
}
@@ -623,28 +963,20 @@ func (h *ClusterHandler) ListStorePolicies(c echo.Context) error {
// @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/policies [get]
func (h *ClusterHandler) ListPolicies(c echo.Context) error {
policies := h.iam.ListPolicies("", "", "", nil)
iampolicies := h.iam.ListPolicies("", "", "", nil)
return c.JSON(http.StatusOK, policies)
}
policies := []api.IAMPolicy{}
// ReloadPolicies reloads the policies from the cluster store to IAM
// @Summary Reload policies
// @Description Reload policies
// @Tags v16.?.?
// @ID cluster-3-iam-reload-policies
// @Produce json
// @Success 200 {string} string
// @Success 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/policies/reload [get]
func (h *ClusterHandler) ReloadPolicies(c echo.Context) error {
err := h.iam.ReloadPolicies()
if err != nil {
return api.Err(http.StatusInternalServerError, "", "reload policies: %w", err)
for _, pol := range iampolicies {
policies = append(policies, api.IAMPolicy{
Name: pol.Name,
Domain: pol.Domain,
Resource: pol.Resource,
Actions: pol.Actions,
})
}
return c.JSON(http.StatusOK, "OK")
return c.JSON(http.StatusOK, policies)
}
// Delete deletes the identity with the given name from the cluster
@@ -655,12 +987,29 @@ func (h *ClusterHandler) ReloadPolicies(c echo.Context) error {
// @Produce json
// @Param name path string true "Identity name"
// @Success 200 {string} string
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/user/{name} [delete]
func (h *ClusterHandler) RemoveIdentity(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
name := util.PathParam(c, "name")
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
return api.Err(http.StatusForbidden, "Forbidden", "Not allowed to delete this user")
}
iamuser, err := h.iam.GetIdentity(name)
if err != nil {
return api.Err(http.StatusNotFound, "Not found", "%s", err)
}
if !superuser && iamuser.Superuser {
return api.Err(http.StatusForbidden, "Forbidden", "Only superusers can remove superusers")
}
if err := h.cluster.RemoveIdentity("", name); err != nil {
return api.Err(http.StatusBadRequest, "Invalid identity", "%s", err.Error())
}

View File

@@ -32,13 +32,13 @@ func NewConfig(store cfgstore.Store) *ConfigHandler {
// @Tags v16.7.2
// @ID config-3-get
// @Produce json
// @Success 200 {object} api.Config
// @Success 200 {object} api.GetConfig
// @Security ApiKeyAuth
// @Router /api/v3/config [get]
func (p *ConfigHandler) Get(c echo.Context) error {
cfg := p.store.GetActive()
apicfg := api.Config{}
apicfg := api.GetConfig{}
apicfg.Unmarshal(cfg)
return c.JSON(http.StatusOK, apicfg)

View File

@@ -21,7 +21,7 @@ func NewIAM(iam iam.IAM) *IAMHandler {
}
}
// AddUser adds a new user
// AddIdentity adds a new user
// @Summary Add a new user
// @Description Add a new user
// @Tags v16.?.?
@@ -35,7 +35,7 @@ func NewIAM(iam iam.IAM) *IAMHandler {
// @Failure 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/iam/user [post]
func (h *IAMHandler) AddUser(c echo.Context) error {
func (h *IAMHandler) AddIdentity(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
@@ -74,7 +74,7 @@ func (h *IAMHandler) AddUser(c echo.Context) error {
return c.JSON(http.StatusOK, user)
}
// RemoveUser deletes the user with the given name
// RemoveIdentity deletes the user with the given name
// @Summary Delete an user by its name
// @Description Delete an user by its name
// @Tags v16.?.?
@@ -87,7 +87,7 @@ func (h *IAMHandler) AddUser(c echo.Context) error {
// @Failure 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/iam/user/{name} [delete]
func (h *IAMHandler) RemoveUser(c echo.Context) error {
func (h *IAMHandler) RemoveIdentity(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
@@ -118,7 +118,7 @@ func (h *IAMHandler) RemoveUser(c echo.Context) error {
return c.JSON(http.StatusOK, "OK")
}
// UpdateUser replaces an existing user
// UpdateIdentity replaces an existing user
// @Summary Replace an existing user
// @Description Replace an existing user.
// @Tags v16.?.?
@@ -134,7 +134,7 @@ func (h *IAMHandler) RemoveUser(c echo.Context) error {
// @Failure 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/iam/user/{name} [put]
func (h *IAMHandler) UpdateUser(c echo.Context) error {
func (h *IAMHandler) UpdateIdentity(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
@@ -199,7 +199,7 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error {
return c.JSON(http.StatusOK, user)
}
// UpdateUserPolicies replaces existing user policies
// UpdateIdentityPolicies replaces existing user policies
// @Summary Replace policies of an user
// @Description Replace policies of an user
// @Tags v16.?.?
@@ -215,7 +215,7 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error {
// @Failure 500 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/iam/user/{name}/policy [put]
func (h *IAMHandler) UpdateUserPolicies(c echo.Context) error {
func (h *IAMHandler) UpdateIdentityPolicies(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
@@ -271,7 +271,7 @@ func (h *IAMHandler) UpdateUserPolicies(c echo.Context) error {
return c.JSON(http.StatusOK, policies)
}
// GetUser returns the user with the given name
// GetIdentity returns the user with the given name
// @Summary List an user by its name
// @Description List aa user by its name
// @Tags v16.?.?
@@ -283,9 +283,8 @@ func (h *IAMHandler) UpdateUserPolicies(c echo.Context) error {
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/iam/user/{name} [get]
func (h *IAMHandler) GetUser(c echo.Context) error {
func (h *IAMHandler) GetIdentity(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none")
name := util.PathParam(c, "name")
@@ -302,7 +301,7 @@ func (h *IAMHandler) GetUser(c echo.Context) error {
return api.Err(http.StatusNotFound, "Not found", "%s", err)
}
if !superuser && name != iamuser.Name {
if ctxuser != iamuser.Name {
if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
iamuser = identity.User{
Name: iamuser.Name,

View File

@@ -37,15 +37,16 @@ func NewRestream(restream restream.Restreamer, iam iam.IAM) *RestreamHandler {
// @Param config body api.ProcessConfig true "Process config"
// @Success 200 {object} api.ProcessConfig
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process [post]
func (h *RestreamHandler) Add(c echo.Context) error {
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
process := api.ProcessConfig{
ID: shortuuid.New(),
Owner: user,
Owner: ctxuser,
Type: "ffmpeg",
Autostart: true,
}
@@ -54,6 +55,10 @@ func (h *RestreamHandler) Add(c echo.Context) error {
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err)
}
if !h.iam.Enforce(ctxuser, process.Domain, "process:"+process.ID, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
if !superuser {
if !h.iam.Enforce(process.Owner, process.Domain, "process:"+process.ID, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
@@ -90,6 +95,7 @@ func (h *RestreamHandler) Add(c echo.Context) error {
// @Tags v16.7.2
// @ID process-3-get-all
// @Produce json
// @Param domain query string false "Domain to act on"
// @Param filter query string false "Comma separated list of fields (config, state, report, metadata) that will be part of the output. If empty, all fields will be part of the output."
// @Param reference query string false "Return only these process that have this reference value. If empty, the reference will be ignored."
// @Param id query string false "Comma separated list of process ids to list. Overrides the reference. If empty all IDs will be returned."
@@ -101,7 +107,7 @@ func (h *RestreamHandler) Add(c echo.Context) error {
// @Security ApiKeyAuth
// @Router /api/v3/process [get]
func (h *RestreamHandler) GetAll(c echo.Context) error {
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
filter := util.DefaultQuery(c, "filter", "")
reference := util.DefaultQuery(c, "reference", "")
wantids := strings.FieldsFunc(util.DefaultQuery(c, "id", ""), func(r rune) bool {
@@ -117,7 +123,7 @@ func (h *RestreamHandler) GetAll(c echo.Context) error {
ids := []restream.TaskID{}
for _, id := range preids {
if !h.iam.Enforce(user, domain, "process:"+id.ID, "read") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id.ID, "read") {
continue
}
@@ -157,18 +163,20 @@ func (h *RestreamHandler) GetAll(c echo.Context) error {
// @ID process-3-get
// @Produce json
// @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Param filter query string false "Comma separated list of fields (config, state, report, metadata) to be part of the output. If empty, all fields will be part of the output"
// @Success 200 {object} api.Process
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process/{id} [get]
func (h *RestreamHandler) Get(c echo.Context) error {
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
id := util.PathParam(c, "id")
filter := util.DefaultQuery(c, "filter", "")
domain := util.DefaultQuery(c, "domain", "")
if !h.iam.Enforce(user, domain, "process:"+id, "read") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "read") {
return api.Err(http.StatusForbidden, "Forbidden")
}
@@ -192,12 +200,14 @@ func (h *RestreamHandler) Get(c echo.Context) error {
// @ID process-3-delete
// @Produce json
// @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {string} string
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process/{id} [delete]
func (h *RestreamHandler) Delete(c echo.Context) error {
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
id := util.PathParam(c, "id")
domain := util.DefaultQuery(c, "domain", "")
@@ -208,7 +218,7 @@ func (h *RestreamHandler) Delete(c echo.Context) error {
}
if !superuser {
if !h.iam.Enforce(user, domain, "process:"+id, "write") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
}
@@ -232,24 +242,28 @@ func (h *RestreamHandler) Delete(c echo.Context) error {
// @Accept json
// @Produce json
// @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Param config body api.ProcessConfig true "Process config"
// @Success 200 {object} api.ProcessConfig
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process/{id} [put]
func (h *RestreamHandler) Update(c echo.Context) error {
id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "")
id := util.PathParam(c, "id")
process := api.ProcessConfig{
ID: id,
Owner: ctxuser,
Type: "ffmpeg",
Autostart: true,
}
if !h.iam.Enforce(user, domain, "process:"+id, "write") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
@@ -270,13 +284,18 @@ func (h *RestreamHandler) Update(c echo.Context) error {
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err)
}
config := process.Marshal()
config.Owner = user
if !h.iam.Enforce(user, config.Domain, "process:"+config.ID, "write") {
if !h.iam.Enforce(ctxuser, process.Domain, "process:"+process.ID, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
if !superuser {
if !h.iam.Enforce(process.Owner, process.Domain, "process:"+process.ID, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
}
config := process.Marshal()
tid = restream.TaskID{
ID: id,
Domain: domain,
@@ -308,18 +327,20 @@ func (h *RestreamHandler) Update(c echo.Context) error {
// @Accept json
// @Produce json
// @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Param command body api.Command true "Process command"
// @Success 200 {string} string
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process/{id}/command [put]
func (h *RestreamHandler) Command(c echo.Context) error {
id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
if !h.iam.Enforce(user, domain, "process:"+id, "write") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
@@ -361,17 +382,19 @@ func (h *RestreamHandler) Command(c echo.Context) error {
// @ID process-3-get-config
// @Produce json
// @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {object} api.ProcessConfig
// @Failure 404 {object} api.Error
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process/{id}/config [get]
func (h *RestreamHandler) GetConfig(c echo.Context) error {
id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
if !h.iam.Enforce(user, domain, "process:"+id, "read") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "read") {
return api.Err(http.StatusForbidden, "Forbidden")
}
@@ -398,17 +421,19 @@ func (h *RestreamHandler) GetConfig(c echo.Context) error {
// @ID process-3-get-state
// @Produce json
// @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {object} api.ProcessState
// @Failure 404 {object} api.Error
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process/{id}/state [get]
func (h *RestreamHandler) GetState(c echo.Context) error {
id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
if !h.iam.Enforce(user, domain, "process:"+id, "read") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "read") {
return api.Err(http.StatusForbidden, "Forbidden")
}
@@ -435,17 +460,19 @@ func (h *RestreamHandler) GetState(c echo.Context) error {
// @ID process-3-get-report
// @Produce json
// @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {object} api.ProcessReport
// @Failure 404 {object} api.Error
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process/{id}/report [get]
func (h *RestreamHandler) GetReport(c echo.Context) error {
id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
if !h.iam.Enforce(user, domain, "process:"+id, "read") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "read") {
return api.Err(http.StatusForbidden, "Forbidden")
}
@@ -472,15 +499,17 @@ func (h *RestreamHandler) GetReport(c echo.Context) error {
// @ID process-3-probe
// @Produce json
// @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {object} api.Probe
// @Failure 403 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process/{id}/probe [get]
func (h *RestreamHandler) Probe(c echo.Context) error {
id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
if !h.iam.Enforce(user, domain, "process:"+id, "write") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
@@ -542,18 +571,20 @@ func (h *RestreamHandler) ReloadSkills(c echo.Context) error {
// @Produce json
// @Param id path string true "Process ID"
// @Param key path string true "Key for data store"
// @Param domain query string false "Domain to act on"
// @Success 200 {object} api.Metadata
// @Failure 404 {object} api.Error
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process/{id}/metadata/{key} [get]
func (h *RestreamHandler) GetProcessMetadata(c echo.Context) error {
id := util.PathParam(c, "id")
key := util.PathParam(c, "key")
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
if !h.iam.Enforce(user, domain, "process:"+id, "read") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "read") {
return api.Err(http.StatusForbidden, "Forbidden")
}
@@ -578,19 +609,21 @@ func (h *RestreamHandler) GetProcessMetadata(c echo.Context) error {
// @Produce json
// @Param id path string true "Process ID"
// @Param key path string true "Key for data store"
// @Param domain query string false "Domain to act on"
// @Param data body api.Metadata true "Arbitrary JSON data. The null value will remove the key and its contents"
// @Success 200 {object} api.Metadata
// @Failure 404 {object} api.Error
// @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth
// @Router /api/v3/process/{id}/metadata/{key} [put]
func (h *RestreamHandler) SetProcessMetadata(c echo.Context) error {
id := util.PathParam(c, "id")
key := util.PathParam(c, "key")
user := util.DefaultContext(c, "user", "")
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
if !h.iam.Enforce(user, domain, "process:"+id, "write") {
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}

View File

@@ -20,7 +20,7 @@
// an anonmyous user. If the Skipper function returns true for the request and the
// API is accessed, the username will be the one of the IAM superuser.
//
// The domain is provided as query parameter "group" for all API requests or the
// The domain is provided as query parameter "domain" for all API requests or the
// first path element after a mountpoint for filesystem requests.
//
// If the domain can't be detected, the domain "$none" will be used.
@@ -179,7 +179,7 @@ func NewWithConfig(config Config) echo.MiddlewareFunc {
username = config.IAM.GetDefaultVerifier().Name()
}
domain = c.QueryParam("group")
domain = c.QueryParam("domain")
resource = "api:" + resource
} else {
identity, err = mw.findIdentityFromBasicAuth(c)

View File

@@ -549,11 +549,11 @@ func (s *server) setRoutesV3(v3 *echo.Group) {
// v3 IAM
if s.v3handler.iam != nil {
v3.POST("/iam/user", s.v3handler.iam.AddUser)
v3.GET("/iam/user/:name", s.v3handler.iam.GetUser)
v3.PUT("/iam/user/:name", s.v3handler.iam.UpdateUser)
v3.PUT("/iam/user/:name/policy", s.v3handler.iam.UpdateUserPolicies)
v3.DELETE("/iam/user/:name", s.v3handler.iam.RemoveUser)
v3.POST("/iam/user", s.v3handler.iam.AddIdentity)
v3.GET("/iam/user/:name", s.v3handler.iam.GetIdentity)
v3.PUT("/iam/user/:name", s.v3handler.iam.UpdateIdentity)
v3.PUT("/iam/user/:name/policy", s.v3handler.iam.UpdateIdentityPolicies)
v3.DELETE("/iam/user/:name", s.v3handler.iam.RemoveIdentity)
}
// v3 Restreamer
@@ -658,10 +658,12 @@ func (s *server) setRoutesV3(v3 *echo.Group) {
v3.GET("/cluster", s.v3handler.cluster.About)
v3.GET("/cluster/db/process", s.v3handler.cluster.ListStoreProcesses)
v3.GET("/cluster/db/policies", s.v3handler.cluster.ListStorePolicies)
v3.GET("/cluster/db/user", s.v3handler.cluster.ListStoreIdentities)
v3.GET("/cluster/db/user/:name", s.v3handler.cluster.ListStoreIdentity)
v3.GET("/cluster/db/policies", s.v3handler.cluster.ListStorePolicies)
v3.GET("/cluster/iam/user", s.v3handler.cluster.ListIdentities)
v3.GET("/cluster/iam/user/:name", s.v3handler.cluster.ListIdentity)
v3.GET("/cluster/iam/policies", s.v3handler.cluster.ListPolicies)
v3.GET("/cluster/process", s.v3handler.cluster.ListAllNodeProcesses)
@@ -677,11 +679,11 @@ func (s *server) setRoutesV3(v3 *echo.Group) {
v3.PUT("/cluster/process/:id", s.v3handler.cluster.UpdateProcess)
v3.DELETE("/cluster/process/:id", s.v3handler.cluster.DeleteProcess)
v3.GET("/cluster/iam/user/reload", s.v3handler.cluster.ReloadIdentities)
v3.GET("/cluster/iam/reload", s.v3handler.cluster.ReloadIAM)
v3.POST("/cluster/iam/user", s.v3handler.cluster.AddIdentity)
v3.PUT("/cluster/iam/user/:name/policies", s.v3handler.cluster.UpdateIdentityPolicies)
v3.PUT("/cluster/iam/user/:name", s.v3handler.cluster.UpdateIdentity)
v3.PUT("/cluster/iam/user/:name/policy", s.v3handler.cluster.UpdateIdentityPolicies)
v3.DELETE("/cluster/iam/user/:name", s.v3handler.cluster.RemoveIdentity)
v3.GET("/cluster/iam/policies/reload", s.v3handler.cluster.ReloadPolicies)
}
}

View File

@@ -157,7 +157,7 @@ func (i *iam) DeleteIdentity(name string) error {
}
func (i *iam) ListIdentities() []identity.User {
return nil
return i.im.List()
}
func (i *iam) ReloadIndentities() error {