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") 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 { a.router.PUT("/v1/iam/user/:name/policies", func(c echo.Context) error {
name := util.PathParam(c, "name")
r := client.SetPoliciesRequest{} r := client.SetPoliciesRequest{}
if err := util.ShouldBindJSON(c, &r); err != nil { 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") 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 { if err != nil {
a.logger.Debug().WithError(err).WithField("policies", r.Policies).Log("Unable to set policies") 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) 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"` Identity iamidentity.User `json:"identity"`
} }
type UpdateIdentityRequest struct {
Name string `json:"name"`
Identity iamidentity.User `json:"identity"`
}
type SetPoliciesRequest struct { type SetPoliciesRequest struct {
Name string `json:"name"` Name string `json:"name"`
Policies []iamaccess.Policy `json:"policies"` Policies []iamaccess.Policy `json:"policies"`
@@ -117,6 +122,17 @@ func (c *APIClient) AddIdentity(origin string, r AddIdentityRequest) error {
return err 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 { func (c *APIClient) SetPolicies(origin, name string, r SetPoliciesRequest) error {
data, err := json.Marshal(r) data, err := json.Marshal(r)
if err != nil { if err != nil {

View File

@@ -72,9 +72,12 @@ type Cluster interface {
UpdateProcess(origin, id string, config *app.Config) error UpdateProcess(origin, id string, config *app.Config) error
IAM(superuser iamidentity.User, jwtRealm, jwtSecret string) (iam.IAM, error) IAM(superuser iamidentity.User, jwtRealm, jwtSecret string) (iam.IAM, error)
ListIdentities() store.Users ListIdentities() (time.Time, []iamidentity.User)
ListPolicies() store.Policies 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 AddIdentity(origin string, identity iamidentity.User) error
UpdateIdentity(origin, name string, identity iamidentity.User) error
SetPolicies(origin, name string, policies []iamaccess.Policy) error SetPolicies(origin, name string, policies []iamaccess.Policy) error
RemoveIdentity(origin string, name string) 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 return iam, nil
} }
func (c *cluster) ListIdentities() store.Users { func (c *cluster) ListIdentities() (time.Time, []iamidentity.User) {
return c.store.UserList() users := c.store.UserList()
return users.UpdatedAt, users.Users
} }
func (c *cluster) ListPolicies() store.Policies { func (c *cluster) ListIdentity(name string) (time.Time, iamidentity.User, error) {
return c.store.PolicyList() 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 { 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) 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 { func (c *cluster) SetPolicies(origin, name string, policies []iamaccess.Policy) error {
if !c.IsRaftLeader() { if !c.IsRaftLeader() {
return c.forwarder.SetPolicies(origin, name, policies) return c.forwarder.SetPolicies(origin, name, policies)
@@ -841,7 +880,7 @@ func (c *cluster) applyCommand(cmd *store.Command) error {
err = c.raft.Apply(b) err = c.raft.Apply(b)
if err != nil { if err != nil {
return fmt.Errorf("applying command failed: %w", err) return fmt.Errorf("apply command: %w", err)
} }
return nil return nil

View File

@@ -27,6 +27,7 @@ type Forwarder interface {
RemoveProcess(origin, id string) error RemoveProcess(origin, id string) error
AddIdentity(origin string, identity iamidentity.User) error AddIdentity(origin string, identity iamidentity.User) error
UpdateIdentity(origin, name string, identity iamidentity.User) error
SetPolicies(origin, name string, policies []iamaccess.Policy) error SetPolicies(origin, name string, policies []iamaccess.Policy) error
RemoveIdentity(origin string, name string) 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) 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 { func (f *forwarder) SetPolicies(origin, name string, policies []iamaccess.Policy) error {
if origin == "" { if origin == "" {
origin = f.id origin = f.id

View File

@@ -223,6 +223,13 @@ func (r *raft) Apply(data []byte) error {
return fmt.Errorf("applying command failed: %w", err) 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 return nil
} }

View File

@@ -24,7 +24,19 @@ type Store interface {
GetProcess(id string) (Process, error) GetProcess(id string) (Process, error)
UserList() Users UserList() Users
GetUser(name string) Users
PolicyList() Policies 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 { type Process struct {
@@ -50,6 +62,7 @@ const (
OpRemoveProcess Operation = "removeProcess" OpRemoveProcess Operation = "removeProcess"
OpUpdateProcess Operation = "updateProcess" OpUpdateProcess Operation = "updateProcess"
OpAddIdentity Operation = "addIdentity" OpAddIdentity Operation = "addIdentity"
OpUpdateIdentity Operation = "updateIdentity"
OpRemoveIdentity Operation = "removeIdentity" OpRemoveIdentity Operation = "removeIdentity"
OpSetPolicies Operation = "setPolicies" OpSetPolicies Operation = "setPolicies"
) )
@@ -76,6 +89,11 @@ type CommandAddIdentity struct {
Identity identity.User Identity identity.User
} }
type CommandUpdateIdentity struct {
Name string
Identity identity.User
}
type CommandRemoveIdentity struct { type CommandRemoveIdentity struct {
Name string Name string
} }
@@ -138,7 +156,7 @@ func (s *store) Apply(entry *raft.Log) interface{} {
err := json.Unmarshal(entry.Data, &c) err := json.Unmarshal(entry.Data, &c)
if err != nil { if err != nil {
logger.Error().WithError(err).Log("Invalid entry") 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("") logger.Debug().WithField("operation", c.Operation).Log("")
@@ -149,87 +167,51 @@ func (s *store) Apply(entry *raft.Log) interface{} {
cmd := CommandAddProcess{} cmd := CommandAddProcess{}
json.Unmarshal(b, &cmd) json.Unmarshal(b, &cmd)
s.lock.Lock() err = s.addProcess(cmd)
_, 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()
case OpRemoveProcess: case OpRemoveProcess:
b, _ := json.Marshal(c.Data) b, _ := json.Marshal(c.Data)
cmd := CommandRemoveProcess{} cmd := CommandRemoveProcess{}
json.Unmarshal(b, &cmd) json.Unmarshal(b, &cmd)
s.lock.Lock() err = s.removeProcess(cmd)
delete(s.Process, cmd.ID)
s.lock.Unlock()
case OpUpdateProcess: case OpUpdateProcess:
b, _ := json.Marshal(c.Data) b, _ := json.Marshal(c.Data)
cmd := CommandUpdateProcess{} cmd := CommandUpdateProcess{}
json.Unmarshal(b, &cmd) json.Unmarshal(b, &cmd)
s.lock.Lock() err = s.updateProcess(cmd)
_, 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()
case OpAddIdentity: case OpAddIdentity:
b, _ := json.Marshal(c.Data) b, _ := json.Marshal(c.Data)
cmd := CommandAddIdentity{} cmd := CommandAddIdentity{}
json.Unmarshal(b, &cmd) json.Unmarshal(b, &cmd)
s.lock.Lock() err = s.addIdentity(cmd)
_, ok := s.Users.Users[cmd.Identity.Name] case OpUpdateIdentity:
if !ok { b, _ := json.Marshal(c.Data)
s.Users.UpdatedAt = time.Now() cmd := CommandUpdateIdentity{}
s.Users.Users[cmd.Identity.Name] = cmd.Identity json.Unmarshal(b, &cmd)
}
s.lock.Unlock() err = s.updateIdentity(cmd)
case OpRemoveIdentity: case OpRemoveIdentity:
b, _ := json.Marshal(c.Data) b, _ := json.Marshal(c.Data)
cmd := CommandRemoveIdentity{} cmd := CommandRemoveIdentity{}
json.Unmarshal(b, &cmd) json.Unmarshal(b, &cmd)
s.lock.Lock() err = s.removeIdentity(cmd)
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()
case OpSetPolicies: case OpSetPolicies:
b, _ := json.Marshal(c.Data) b, _ := json.Marshal(c.Data)
cmd := CommandSetPolicies{} cmd := CommandSetPolicies{}
json.Unmarshal(b, &cmd) json.Unmarshal(b, &cmd)
s.lock.Lock() err = s.setPolicies(cmd)
delete(s.Policies.Policies, cmd.Name)
s.Policies.Policies[cmd.Name] = cmd.Policies
s.Policies.UpdatedAt = time.Now()
s.lock.Unlock()
default: default:
s.logger.Warn().WithField("operation", c.Operation).Log("Unknown operation") 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() s.lock.RLock()
@@ -238,9 +220,123 @@ func (s *store) Apply(entry *raft.Log) interface{} {
} }
s.lock.RUnlock() s.lock.RUnlock()
s.lock.RLock() return nil
s.logger.Debug().WithField("processes", s.Process).Log("") }
s.lock.RUnlock()
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 return nil
} }
@@ -329,6 +425,21 @@ func (s *store) UserList() Users {
return u 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 { func (s *store) PolicyList() Policies {
s.lock.RLock() s.lock.RLock()
defer s.lock.RUnlock() defer s.lock.RUnlock()
@@ -344,6 +455,19 @@ func (s *store) PolicyList() Policies {
return p 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 { type fsmSnapshot struct {
data []byte data []byte
} }

View File

@@ -150,12 +150,6 @@ const docTemplate = `{
"schema": { "schema": {
"$ref": "#/definitions/api.ClusterAbout" "$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": { "/api/v3/cluster/iam/policies": {
"get": { "get": {
"security": [ "security": [
@@ -276,22 +308,22 @@ const docTemplate = `{
} }
} }
}, },
"/api/v3/cluster/iam/policies/reload": { "/api/v3/cluster/iam/reload": {
"get": { "get": {
"security": [ "security": [
{ {
"ApiKeyAuth": [] "ApiKeyAuth": []
} }
], ],
"description": "Reload policies", "description": "Reload identities and policies",
"produces": [ "produces": [
"application/json" "application/json"
], ],
"tags": [ "tags": [
"v16.?.?" "v16.?.?"
], ],
"summary": "Reload policies", "summary": "Reload identities and policies",
"operationId": "cluster-3-iam-reload-policies", "operationId": "cluster-3-iam-reload",
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",
@@ -377,31 +409,118 @@ const docTemplate = `{
"schema": { "schema": {
"$ref": "#/definitions/api.Error" "$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": { "get": {
"security": [ "security": [
{ {
"ApiKeyAuth": [] "ApiKeyAuth": []
} }
], ],
"description": "Reload identities", "description": "Identity in IAM",
"produces": [ "produces": [
"application/json" "application/json"
], ],
"tags": [ "tags": [
"v16.?.?" "v16.?.?"
], ],
"summary": "Reload identities", "summary": "Identity in IAM",
"operationId": "cluster-3-iam-reload-identities", "operationId": "cluster-3-iam-list-identity",
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "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": { "500": {
@@ -411,9 +530,7 @@ const docTemplate = `{
} }
} }
} }
} },
},
"/api/v3/cluster/iam/user/{name}": {
"delete": { "delete": {
"security": [ "security": [
{ {
@@ -445,6 +562,12 @@ const docTemplate = `{
"type": "string" "type": "string"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -516,6 +639,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -671,6 +800,12 @@ const docTemplate = `{
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -727,7 +862,7 @@ const docTemplate = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/api.ClusterNode" "$ref": "#/definitions/api.Version"
} }
}, },
"404": { "404": {
@@ -755,6 +890,14 @@ const docTemplate = `{
], ],
"summary": "List of processes in the cluster", "summary": "List of processes in the cluster",
"operationId": "cluster-3-list-all-node-processes", "operationId": "cluster-3-list-all-node-processes",
"parameters": [
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",
@@ -808,6 +951,12 @@ const docTemplate = `{
"schema": { "schema": {
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
} }
} }
} }
@@ -862,6 +1011,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -901,8 +1056,14 @@ const docTemplate = `{
"type": "string" "type": "string"
} }
}, },
"404": { "403": {
"description": "Not Found", "description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
@@ -930,7 +1091,7 @@ const docTemplate = `{
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "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", "summary": "List all known processes",
"operationId": "process-3-get-all", "operationId": "process-3-get-all",
"parameters": [ "parameters": [
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{ {
"type": "string", "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.", "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": { "schema": {
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
} }
} }
} }
@@ -1908,6 +2081,12 @@ const docTemplate = `{
"in": "path", "in": "path",
"required": true "required": true
}, },
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{ {
"type": "string", "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", "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" "$ref": "#/definitions/api.Process"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -1956,6 +2141,12 @@ const docTemplate = `{
"in": "path", "in": "path",
"required": true "required": true
}, },
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{ {
"description": "Process config", "description": "Process config",
"name": "config", "name": "config",
@@ -1979,6 +2170,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2009,6 +2206,12 @@ const docTemplate = `{
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2018,6 +2221,12 @@ const docTemplate = `{
"type": "string" "type": "string"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2054,6 +2263,12 @@ const docTemplate = `{
"in": "path", "in": "path",
"required": true "required": true
}, },
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{ {
"description": "Process command", "description": "Process command",
"name": "command", "name": "command",
@@ -2077,6 +2292,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2109,6 +2330,12 @@ const docTemplate = `{
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2124,6 +2351,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2163,6 +2396,12 @@ const docTemplate = `{
"name": "key", "name": "key",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2176,6 +2415,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2214,6 +2459,12 @@ const docTemplate = `{
"in": "path", "in": "path",
"required": true "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", "description": "Arbitrary JSON data. The null value will remove the key and its contents",
"name": "data", "name": "data",
@@ -2233,6 +2484,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2635,6 +2892,12 @@ const docTemplate = `{
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2643,6 +2906,12 @@ const docTemplate = `{
"schema": { "schema": {
"$ref": "#/definitions/api.Probe" "$ref": "#/definitions/api.Probe"
} }
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
} }
} }
} }
@@ -2670,6 +2939,12 @@ const docTemplate = `{
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2685,6 +2960,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2717,6 +2998,12 @@ const docTemplate = `{
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2732,6 +3019,12 @@ const docTemplate = `{
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "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": { "api.GraphQuery": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -3945,6 +4261,9 @@ const docTemplate = `{
"domain": { "domain": {
"type": "string" "type": "string"
}, },
"name": {
"type": "string"
},
"resource": { "resource": {
"type": "string" "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": { "value.Auth0Tenant": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -142,12 +142,6 @@
"schema": { "schema": {
"$ref": "#/definitions/api.ClusterAbout" "$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": { "/api/v3/cluster/iam/policies": {
"get": { "get": {
"security": [ "security": [
@@ -268,22 +300,22 @@
} }
} }
}, },
"/api/v3/cluster/iam/policies/reload": { "/api/v3/cluster/iam/reload": {
"get": { "get": {
"security": [ "security": [
{ {
"ApiKeyAuth": [] "ApiKeyAuth": []
} }
], ],
"description": "Reload policies", "description": "Reload identities and policies",
"produces": [ "produces": [
"application/json" "application/json"
], ],
"tags": [ "tags": [
"v16.?.?" "v16.?.?"
], ],
"summary": "Reload policies", "summary": "Reload identities and policies",
"operationId": "cluster-3-iam-reload-policies", "operationId": "cluster-3-iam-reload",
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",
@@ -369,31 +401,118 @@
"schema": { "schema": {
"$ref": "#/definitions/api.Error" "$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": { "get": {
"security": [ "security": [
{ {
"ApiKeyAuth": [] "ApiKeyAuth": []
} }
], ],
"description": "Reload identities", "description": "Identity in IAM",
"produces": [ "produces": [
"application/json" "application/json"
], ],
"tags": [ "tags": [
"v16.?.?" "v16.?.?"
], ],
"summary": "Reload identities", "summary": "Identity in IAM",
"operationId": "cluster-3-iam-reload-identities", "operationId": "cluster-3-iam-list-identity",
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "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": { "500": {
@@ -403,9 +522,7 @@
} }
} }
} }
} },
},
"/api/v3/cluster/iam/user/{name}": {
"delete": { "delete": {
"security": [ "security": [
{ {
@@ -437,6 +554,12 @@
"type": "string" "type": "string"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -508,6 +631,12 @@
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -663,6 +792,12 @@
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -719,7 +854,7 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/api.ClusterNode" "$ref": "#/definitions/api.Version"
} }
}, },
"404": { "404": {
@@ -747,6 +882,14 @@
], ],
"summary": "List of processes in the cluster", "summary": "List of processes in the cluster",
"operationId": "cluster-3-list-all-node-processes", "operationId": "cluster-3-list-all-node-processes",
"parameters": [
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
}
],
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",
@@ -800,6 +943,12 @@
"schema": { "schema": {
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
} }
} }
} }
@@ -854,6 +1003,12 @@
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -893,8 +1048,14 @@
"type": "string" "type": "string"
} }
}, },
"404": { "403": {
"description": "Not Found", "description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"500": {
"description": "Internal Server Error",
"schema": { "schema": {
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
@@ -922,7 +1083,7 @@
"200": { "200": {
"description": "OK", "description": "OK",
"schema": { "schema": {
"$ref": "#/definitions/github_com_datarhei_core_v16_http_api.Config" "$ref": "#/definitions/api.GetConfig"
} }
} }
} }
@@ -1776,6 +1937,12 @@
"summary": "List all known processes", "summary": "List all known processes",
"operationId": "process-3-get-all", "operationId": "process-3-get-all",
"parameters": [ "parameters": [
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{ {
"type": "string", "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.", "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": { "schema": {
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
} }
} }
} }
@@ -1900,6 +2073,12 @@
"in": "path", "in": "path",
"required": true "required": true
}, },
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{ {
"type": "string", "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", "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" "$ref": "#/definitions/api.Process"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -1948,6 +2133,12 @@
"in": "path", "in": "path",
"required": true "required": true
}, },
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{ {
"description": "Process config", "description": "Process config",
"name": "config", "name": "config",
@@ -1971,6 +2162,12 @@
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2001,6 +2198,12 @@
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2010,6 +2213,12 @@
"type": "string" "type": "string"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2046,6 +2255,12 @@
"in": "path", "in": "path",
"required": true "required": true
}, },
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
},
{ {
"description": "Process command", "description": "Process command",
"name": "command", "name": "command",
@@ -2069,6 +2284,12 @@
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2101,6 +2322,12 @@
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2116,6 +2343,12 @@
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2155,6 +2388,12 @@
"name": "key", "name": "key",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2168,6 +2407,12 @@
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2206,6 +2451,12 @@
"in": "path", "in": "path",
"required": true "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", "description": "Arbitrary JSON data. The null value will remove the key and its contents",
"name": "data", "name": "data",
@@ -2225,6 +2476,12 @@
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2627,6 +2884,12 @@
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2635,6 +2898,12 @@
"schema": { "schema": {
"$ref": "#/definitions/api.Probe" "$ref": "#/definitions/api.Probe"
} }
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
} }
} }
} }
@@ -2662,6 +2931,12 @@
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2677,6 +2952,12 @@
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "schema": {
@@ -2709,6 +2990,12 @@
"name": "id", "name": "id",
"in": "path", "in": "path",
"required": true "required": true
},
{
"type": "string",
"description": "Domain to act on",
"name": "domain",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -2724,6 +3011,12 @@
"$ref": "#/definitions/api.Error" "$ref": "#/definitions/api.Error"
} }
}, },
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/api.Error"
}
},
"404": { "404": {
"description": "Not Found", "description": "Not Found",
"schema": { "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": { "api.GraphQuery": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -3937,6 +4253,9 @@
"domain": { "domain": {
"type": "string" "type": "string"
}, },
"name": {
"type": "string"
},
"resource": { "resource": {
"type": "string" "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": { "value.Auth0Tenant": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

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

View File

@@ -19,8 +19,8 @@ type ConfigData struct {
config.Data config.Data
} }
// Config is the config returned by the API // GetConfig is the config returned by the API
type Config struct { type GetConfig struct {
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
LoadedAt time.Time `json:"loaded_at"` LoadedAt time.Time `json:"loaded_at"`
UpdatedAt time.Time `json:"updated_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. // 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 { if cfg == nil {
return return
} }

View File

@@ -106,6 +106,7 @@ type IAMAuth0Tenant struct {
} }
type IAMPolicy struct { type IAMPolicy struct {
Name string `json:"name,omitempty"`
Domain string `json:"domain"` Domain string `json:"domain"`
Resource string `json:"resource"` Resource string `json:"resource"`
Actions []string `json:"actions"` 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 // @ID cluster-3-get-cluster
// @Produce json // @Produce json
// @Success 200 {object} api.ClusterAbout // @Success 200 {object} api.ClusterAbout
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster [get] // @Router /api/v3/cluster [get]
func (h *ClusterHandler) About(c echo.Context) error { func (h *ClusterHandler) About(c echo.Context) error {
@@ -91,15 +90,23 @@ func (h *ClusterHandler) About(c echo.Context) error {
// @Tags v16.?.? // @Tags v16.?.?
// @ID cluster-3-list-all-node-processes // @ID cluster-3-list-all-node-processes
// @Produce json // @Produce json
// @Param domain query string false "Domain to act on"
// @Success 200 {array} api.ClusterProcess // @Success 200 {array} api.ClusterProcess
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/process [get] // @Router /api/v3/cluster/process [get]
func (h *ClusterHandler) ListAllNodeProcesses(c echo.Context) error { func (h *ClusterHandler) ListAllNodeProcesses(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
procs := h.proxy.ListProcesses() procs := h.proxy.ListProcesses()
processes := []api.ClusterProcess{} processes := []api.ClusterProcess{}
for _, p := range procs { for _, p := range procs {
if !h.iam.Enforce(ctxuser, domain, "process:"+p.Config.ID, "read") {
continue
}
processes = append(processes, api.ClusterProcess{ processes = append(processes, api.ClusterProcess{
ProcessID: p.Config.ID, ProcessID: p.Config.ID,
NodeID: p.NodeID, NodeID: p.NodeID,
@@ -193,7 +200,7 @@ func (h *ClusterHandler) GetNode(c echo.Context) error {
// @ID cluster-3-get-node-version // @ID cluster-3-get-node-version
// @Produce json // @Produce json
// @Param id path string true "Node ID" // @Param id path string true "Node ID"
// @Success 200 {object} api.ClusterNode // @Success 200 {object} api.Version
// @Failure 404 {object} api.Error // @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/node/{id}/version [get] // @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) 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) return c.JSON(http.StatusOK, version)
} }
@@ -258,12 +274,15 @@ func (h *ClusterHandler) GetNodeFiles(c echo.Context) error {
// @ID cluster-3-list-node-processes // @ID cluster-3-list-node-processes
// @Produce json // @Produce json
// @Param id path string true "Node ID" // @Param id path string true "Node ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {array} api.ClusterProcess // @Success 200 {array} api.ClusterProcess
// @Failure 404 {object} api.Error // @Failure 404 {object} api.Error
// @Failure 500 {object} api.Error // @Failure 500 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/node/{id}/process [get] // @Router /api/v3/cluster/node/{id}/process [get]
func (h *ClusterHandler) ListNodeProcesses(c echo.Context) error { func (h *ClusterHandler) ListNodeProcesses(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
id := util.PathParam(c, "id") id := util.PathParam(c, "id")
peer, err := h.proxy.GetNode(id) peer, err := h.proxy.GetNode(id)
@@ -279,6 +298,10 @@ func (h *ClusterHandler) ListNodeProcesses(c echo.Context) error {
processes := []api.ClusterProcess{} processes := []api.ClusterProcess{}
for _, p := range procs { for _, p := range procs {
if !h.iam.Enforce(ctxuser, domain, "process:"+p.Config.ID, "read") {
continue
}
processes = append(processes, api.ClusterProcess{ processes = append(processes, api.ClusterProcess{
ProcessID: p.Config.ID, ProcessID: p.Config.ID,
NodeID: p.NodeID, NodeID: p.NodeID,
@@ -304,11 +327,18 @@ func (h *ClusterHandler) ListNodeProcesses(c echo.Context) error {
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/db/process [get] // @Router /api/v3/cluster/db/process [get]
func (h *ClusterHandler) ListStoreProcesses(c echo.Context) error { func (h *ClusterHandler) ListStoreProcesses(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "")
procs := h.cluster.ListProcesses() procs := h.cluster.ListProcesses()
processes := []api.Process{} processes := []api.Process{}
for _, p := range procs { for _, p := range procs {
if !h.iam.Enforce(ctxuser, domain, "process:"+p.Config.ID, "read") {
continue
}
process := api.Process{ process := api.Process{
ID: p.Config.ID, ID: p.Config.ID,
Type: "ffmpeg", Type: "ffmpeg",
@@ -338,11 +368,16 @@ func (h *ClusterHandler) ListStoreProcesses(c echo.Context) error {
// @Param config body api.ProcessConfig true "Process config" // @Param config body api.ProcessConfig true "Process config"
// @Success 200 {object} api.ProcessConfig // @Success 200 {object} api.ProcessConfig
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/process [post] // @Router /api/v3/cluster/process [post]
func (h *ClusterHandler) AddProcess(c echo.Context) error { func (h *ClusterHandler) AddProcess(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
process := api.ProcessConfig{ process := api.ProcessConfig{
ID: shortuuid.New(), ID: shortuuid.New(),
Owner: ctxuser,
Type: "ffmpeg", Type: "ffmpeg",
Autostart: true, Autostart: true,
} }
@@ -351,6 +386,16 @@ func (h *ClusterHandler) AddProcess(c echo.Context) error {
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) 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" { if process.Type != "ffmpeg" {
return api.Err(http.StatusBadRequest, "Unsupported process type", "Supported process types are: 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" // @Param config body api.ProcessConfig true "Process config"
// @Success 200 {object} api.ProcessConfig // @Success 200 {object} api.ProcessConfig
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error // @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/process/{id} [put] // @Router /api/v3/cluster/process/{id} [put]
func (h *ClusterHandler) UpdateProcess(c echo.Context) error { 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") id := util.PathParam(c, "id")
process := api.ProcessConfig{ process := api.ProcessConfig{
ID: id, ID: id,
Owner: ctxuser,
Domain: domain,
Type: "ffmpeg", Type: "ffmpeg",
Autostart: true, Autostart: true,
} }
if !h.iam.Enforce(ctxuser, domain, "process:"+id, "write") {
return api.Err(http.StatusForbidden, "Forbidden")
}
current, err := h.cluster.GetProcess(id) current, err := h.cluster.GetProcess(id)
if err != nil { if err != nil {
return api.Err(http.StatusNotFound, "Process not found", "%s", id) 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) 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() config := process.Marshal()
if err := h.cluster.UpdateProcess("", id, config); err != nil { if err := h.cluster.UpdateProcess("", id, config); err != nil {
@@ -424,12 +489,19 @@ func (h *ClusterHandler) UpdateProcess(c echo.Context) error {
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @Param id path string true "Process ID"
// @Success 200 {string} string // @Success 200 {string} string
// @Failure 404 {object} api.Error // @Failure 403 {object} api.Error
// @Failure 500 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/process/{id} [delete] // @Router /api/v3/cluster/process/{id} [delete]
func (h *ClusterHandler) DeleteProcess(c echo.Context) error { func (h *ClusterHandler) DeleteProcess(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "$none")
id := util.PathParam(c, "id") 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 { if err := h.cluster.RemoveProcess("", id); err != nil {
return api.Err(http.StatusInternalServerError, "Process can't be deleted", "%s", err) 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" // @Param config body api.IAMUser true "Identity"
// @Success 200 {object} api.IAMUser // @Success 200 {object} api.IAMUser
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/user [post] // @Router /api/v3/cluster/iam/user [post]
func (h *ClusterHandler) AddIdentity(c echo.Context) error { 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{} user := api.IAMUser{}
if err := util.ShouldBindJSON(c, &user); err != nil { if err := util.ShouldBindJSON(c, &user); err != nil {
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) 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()) 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 api.Err(http.StatusBadRequest, "Invalid policies", "%s", err.Error())
} }
return c.JSON(http.StatusOK, user) 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 // UpdateIdentityPolicies replaces existing user policies
// @Summary Replace policies of an user // @Summary Replace policies of an user
// @Description 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" // @Param user body []api.IAMPolicy true "Policy definitions"
// @Success 200 {array} api.IAMPolicy // @Success 200 {array} api.IAMPolicy
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error // @Failure 404 {object} api.Error
// @Failure 500 {object} api.Error // @Failure 500 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
@@ -559,9 +732,111 @@ func (h *ClusterHandler) UpdateIdentityPolicies(c echo.Context) error {
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/db/user [get] // @Router /api/v3/cluster/db/user [get]
func (h *ClusterHandler) ListStoreIdentities(c echo.Context) error { 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 // ListIdentities returns the list of identities stored in IAM
@@ -574,28 +849,80 @@ func (h *ClusterHandler) ListStoreIdentities(c echo.Context) error {
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/user [get] // @Router /api/v3/cluster/iam/user [get]
func (h *ClusterHandler) ListIdentities(c echo.Context) error { func (h *ClusterHandler) ListIdentities(c echo.Context) error {
ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "$none")
identities := h.iam.ListIdentities() 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 for i, iamuser := range identities {
// @Summary Reload identities if !h.iam.Enforce(ctxuser, domain, "iam:"+iamuser.Name, "read") {
// @Description Reload identities continue
// @Tags v16.?.? }
// @ID cluster-3-iam-reload-identities
// @Produce json if !h.iam.Enforce(ctxuser, domain, "iam:"+iamuser.Name, "write") {
// @Success 200 {string} string iamuser = identity.User{
// @Success 500 {object} api.Error Name: iamuser.Name,
// @Security ApiKeyAuth }
// @Router /api/v3/cluster/iam/user/reload [get] }
func (h *ClusterHandler) ReloadIdentities(c echo.Context) error {
err := h.iam.ReloadIndentities() policies := h.iam.ListPolicies(iamuser.Name, "", "", nil)
if err != nil {
return api.Err(http.StatusInternalServerError, "", "reload identities: %w", err) 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 // 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 // @Security ApiKeyAuth
// @Router /api/v3/cluster/db/policies [get] // @Router /api/v3/cluster/db/policies [get]
func (h *ClusterHandler) ListStorePolicies(c echo.Context) error { 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) return c.JSON(http.StatusOK, policies)
} }
@@ -623,28 +963,20 @@ func (h *ClusterHandler) ListStorePolicies(c echo.Context) error {
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/policies [get] // @Router /api/v3/cluster/iam/policies [get]
func (h *ClusterHandler) ListPolicies(c echo.Context) error { 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 for _, pol := range iampolicies {
// @Summary Reload policies policies = append(policies, api.IAMPolicy{
// @Description Reload policies Name: pol.Name,
// @Tags v16.?.? Domain: pol.Domain,
// @ID cluster-3-iam-reload-policies Resource: pol.Resource,
// @Produce json Actions: pol.Actions,
// @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)
} }
return c.JSON(http.StatusOK, "OK") return c.JSON(http.StatusOK, policies)
} }
// Delete deletes the identity with the given name from the cluster // 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 // @Produce json
// @Param name path string true "Identity name" // @Param name path string true "Identity name"
// @Success 200 {string} string // @Success 200 {string} string
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error // @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/iam/user/{name} [delete] // @Router /api/v3/cluster/iam/user/{name} [delete]
func (h *ClusterHandler) RemoveIdentity(c echo.Context) error { 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") 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 { if err := h.cluster.RemoveIdentity("", name); err != nil {
return api.Err(http.StatusBadRequest, "Invalid identity", "%s", err.Error()) 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 // @Tags v16.7.2
// @ID config-3-get // @ID config-3-get
// @Produce json // @Produce json
// @Success 200 {object} api.Config // @Success 200 {object} api.GetConfig
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/config [get] // @Router /api/v3/config [get]
func (p *ConfigHandler) Get(c echo.Context) error { func (p *ConfigHandler) Get(c echo.Context) error {
cfg := p.store.GetActive() cfg := p.store.GetActive()
apicfg := api.Config{} apicfg := api.GetConfig{}
apicfg.Unmarshal(cfg) apicfg.Unmarshal(cfg)
return c.JSON(http.StatusOK, apicfg) 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 // @Summary Add a new user
// @Description Add a new user // @Description Add a new user
// @Tags v16.?.? // @Tags v16.?.?
@@ -35,7 +35,7 @@ func NewIAM(iam iam.IAM) *IAMHandler {
// @Failure 500 {object} api.Error // @Failure 500 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/iam/user [post] // @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", "") ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false) superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none") domain := util.DefaultQuery(c, "domain", "$none")
@@ -74,7 +74,7 @@ func (h *IAMHandler) AddUser(c echo.Context) error {
return c.JSON(http.StatusOK, user) 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 // @Summary Delete an user by its name
// @Description Delete an user by its name // @Description Delete an user by its name
// @Tags v16.?.? // @Tags v16.?.?
@@ -87,7 +87,7 @@ func (h *IAMHandler) AddUser(c echo.Context) error {
// @Failure 500 {object} api.Error // @Failure 500 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/iam/user/{name} [delete] // @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", "") ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false) superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none") domain := util.DefaultQuery(c, "domain", "$none")
@@ -118,7 +118,7 @@ func (h *IAMHandler) RemoveUser(c echo.Context) error {
return c.JSON(http.StatusOK, "OK") return c.JSON(http.StatusOK, "OK")
} }
// UpdateUser replaces an existing user // UpdateIdentity replaces an existing user
// @Summary Replace an existing user // @Summary Replace an existing user
// @Description Replace an existing user. // @Description Replace an existing user.
// @Tags v16.?.? // @Tags v16.?.?
@@ -134,7 +134,7 @@ func (h *IAMHandler) RemoveUser(c echo.Context) error {
// @Failure 500 {object} api.Error // @Failure 500 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/iam/user/{name} [put] // @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", "") ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false) superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none") domain := util.DefaultQuery(c, "domain", "$none")
@@ -199,7 +199,7 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error {
return c.JSON(http.StatusOK, user) return c.JSON(http.StatusOK, user)
} }
// UpdateUserPolicies replaces existing user policies // UpdateIdentityPolicies replaces existing user policies
// @Summary Replace policies of an user // @Summary Replace policies of an user
// @Description Replace policies of an user // @Description Replace policies of an user
// @Tags v16.?.? // @Tags v16.?.?
@@ -215,7 +215,7 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error {
// @Failure 500 {object} api.Error // @Failure 500 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/iam/user/{name}/policy [put] // @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", "") ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false) superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none") domain := util.DefaultQuery(c, "domain", "$none")
@@ -271,7 +271,7 @@ func (h *IAMHandler) UpdateUserPolicies(c echo.Context) error {
return c.JSON(http.StatusOK, policies) 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 // @Summary List an user by its name
// @Description List aa user by its name // @Description List aa user by its name
// @Tags v16.?.? // @Tags v16.?.?
@@ -283,9 +283,8 @@ func (h *IAMHandler) UpdateUserPolicies(c echo.Context) error {
// @Failure 404 {object} api.Error // @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/iam/user/{name} [get] // @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", "") ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "$none") domain := util.DefaultQuery(c, "domain", "$none")
name := util.PathParam(c, "name") 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) 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") { if !h.iam.Enforce(ctxuser, domain, "iam:"+name, "write") {
iamuser = identity.User{ iamuser = identity.User{
Name: iamuser.Name, 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" // @Param config body api.ProcessConfig true "Process config"
// @Success 200 {object} api.ProcessConfig // @Success 200 {object} api.ProcessConfig
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process [post] // @Router /api/v3/process [post]
func (h *RestreamHandler) Add(c echo.Context) error { func (h *RestreamHandler) Add(c echo.Context) error {
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false) superuser := util.DefaultContext(c, "superuser", false)
process := api.ProcessConfig{ process := api.ProcessConfig{
ID: shortuuid.New(), ID: shortuuid.New(),
Owner: user, Owner: ctxuser,
Type: "ffmpeg", Type: "ffmpeg",
Autostart: true, Autostart: true,
} }
@@ -54,6 +55,10 @@ func (h *RestreamHandler) Add(c echo.Context) error {
return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) 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 !superuser {
if !h.iam.Enforce(process.Owner, process.Domain, "process:"+process.ID, "write") { if !h.iam.Enforce(process.Owner, process.Domain, "process:"+process.ID, "write") {
return api.Err(http.StatusForbidden, "Forbidden") return api.Err(http.StatusForbidden, "Forbidden")
@@ -90,6 +95,7 @@ func (h *RestreamHandler) Add(c echo.Context) error {
// @Tags v16.7.2 // @Tags v16.7.2
// @ID process-3-get-all // @ID process-3-get-all
// @Produce json // @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 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 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." // @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 // @Security ApiKeyAuth
// @Router /api/v3/process [get] // @Router /api/v3/process [get]
func (h *RestreamHandler) GetAll(c echo.Context) error { func (h *RestreamHandler) GetAll(c echo.Context) error {
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
filter := util.DefaultQuery(c, "filter", "") filter := util.DefaultQuery(c, "filter", "")
reference := util.DefaultQuery(c, "reference", "") reference := util.DefaultQuery(c, "reference", "")
wantids := strings.FieldsFunc(util.DefaultQuery(c, "id", ""), func(r rune) bool { 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{} ids := []restream.TaskID{}
for _, id := range preids { 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 continue
} }
@@ -157,18 +163,20 @@ func (h *RestreamHandler) GetAll(c echo.Context) error {
// @ID process-3-get // @ID process-3-get
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @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" // @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 // @Success 200 {object} api.Process
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error // @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process/{id} [get] // @Router /api/v3/process/{id} [get]
func (h *RestreamHandler) Get(c echo.Context) error { func (h *RestreamHandler) Get(c echo.Context) error {
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
id := util.PathParam(c, "id") id := util.PathParam(c, "id")
filter := util.DefaultQuery(c, "filter", "") filter := util.DefaultQuery(c, "filter", "")
domain := util.DefaultQuery(c, "domain", "") 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") return api.Err(http.StatusForbidden, "Forbidden")
} }
@@ -192,12 +200,14 @@ func (h *RestreamHandler) Get(c echo.Context) error {
// @ID process-3-delete // @ID process-3-delete
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {string} string // @Success 200 {string} string
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error // @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process/{id} [delete] // @Router /api/v3/process/{id} [delete]
func (h *RestreamHandler) Delete(c echo.Context) error { func (h *RestreamHandler) Delete(c echo.Context) error {
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
superuser := util.DefaultContext(c, "superuser", false) superuser := util.DefaultContext(c, "superuser", false)
id := util.PathParam(c, "id") id := util.PathParam(c, "id")
domain := util.DefaultQuery(c, "domain", "") domain := util.DefaultQuery(c, "domain", "")
@@ -208,7 +218,7 @@ func (h *RestreamHandler) Delete(c echo.Context) error {
} }
if !superuser { 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") return api.Err(http.StatusForbidden, "Forbidden")
} }
} }
@@ -232,24 +242,28 @@ func (h *RestreamHandler) Delete(c echo.Context) error {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Param config body api.ProcessConfig true "Process config" // @Param config body api.ProcessConfig true "Process config"
// @Success 200 {object} api.ProcessConfig // @Success 200 {object} api.ProcessConfig
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error // @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process/{id} [put] // @Router /api/v3/process/{id} [put]
func (h *RestreamHandler) Update(c echo.Context) error { func (h *RestreamHandler) Update(c echo.Context) error {
id := util.PathParam(c, "id") ctxuser := util.DefaultContext(c, "user", "")
user := util.DefaultContext(c, "user", "") superuser := util.DefaultContext(c, "superuser", false)
domain := util.DefaultQuery(c, "domain", "") domain := util.DefaultQuery(c, "domain", "")
id := util.PathParam(c, "id")
process := api.ProcessConfig{ process := api.ProcessConfig{
ID: id, ID: id,
Owner: ctxuser,
Type: "ffmpeg", Type: "ffmpeg",
Autostart: true, 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") 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) return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err)
} }
config := process.Marshal() if !h.iam.Enforce(ctxuser, process.Domain, "process:"+process.ID, "write") {
config.Owner = user
if !h.iam.Enforce(user, config.Domain, "process:"+config.ID, "write") {
return api.Err(http.StatusForbidden, "Forbidden") 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{ tid = restream.TaskID{
ID: id, ID: id,
Domain: domain, Domain: domain,
@@ -308,18 +327,20 @@ func (h *RestreamHandler) Update(c echo.Context) error {
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Param command body api.Command true "Process command" // @Param command body api.Command true "Process command"
// @Success 200 {string} string // @Success 200 {string} string
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error // @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process/{id}/command [put] // @Router /api/v3/process/{id}/command [put]
func (h *RestreamHandler) Command(c echo.Context) error { func (h *RestreamHandler) Command(c echo.Context) error {
id := util.PathParam(c, "id") id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "") 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") return api.Err(http.StatusForbidden, "Forbidden")
} }
@@ -361,17 +382,19 @@ func (h *RestreamHandler) Command(c echo.Context) error {
// @ID process-3-get-config // @ID process-3-get-config
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {object} api.ProcessConfig // @Success 200 {object} api.ProcessConfig
// @Failure 404 {object} api.Error
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process/{id}/config [get] // @Router /api/v3/process/{id}/config [get]
func (h *RestreamHandler) GetConfig(c echo.Context) error { func (h *RestreamHandler) GetConfig(c echo.Context) error {
id := util.PathParam(c, "id") id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "") 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") return api.Err(http.StatusForbidden, "Forbidden")
} }
@@ -398,17 +421,19 @@ func (h *RestreamHandler) GetConfig(c echo.Context) error {
// @ID process-3-get-state // @ID process-3-get-state
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {object} api.ProcessState // @Success 200 {object} api.ProcessState
// @Failure 404 {object} api.Error
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process/{id}/state [get] // @Router /api/v3/process/{id}/state [get]
func (h *RestreamHandler) GetState(c echo.Context) error { func (h *RestreamHandler) GetState(c echo.Context) error {
id := util.PathParam(c, "id") id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "") 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") return api.Err(http.StatusForbidden, "Forbidden")
} }
@@ -435,17 +460,19 @@ func (h *RestreamHandler) GetState(c echo.Context) error {
// @ID process-3-get-report // @ID process-3-get-report
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {object} api.ProcessReport // @Success 200 {object} api.ProcessReport
// @Failure 404 {object} api.Error
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process/{id}/report [get] // @Router /api/v3/process/{id}/report [get]
func (h *RestreamHandler) GetReport(c echo.Context) error { func (h *RestreamHandler) GetReport(c echo.Context) error {
id := util.PathParam(c, "id") id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "") 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") return api.Err(http.StatusForbidden, "Forbidden")
} }
@@ -472,15 +499,17 @@ func (h *RestreamHandler) GetReport(c echo.Context) error {
// @ID process-3-probe // @ID process-3-probe
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @Param id path string true "Process ID"
// @Param domain query string false "Domain to act on"
// @Success 200 {object} api.Probe // @Success 200 {object} api.Probe
// @Failure 403 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process/{id}/probe [get] // @Router /api/v3/process/{id}/probe [get]
func (h *RestreamHandler) Probe(c echo.Context) error { func (h *RestreamHandler) Probe(c echo.Context) error {
id := util.PathParam(c, "id") id := util.PathParam(c, "id")
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "") 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") return api.Err(http.StatusForbidden, "Forbidden")
} }
@@ -542,18 +571,20 @@ func (h *RestreamHandler) ReloadSkills(c echo.Context) error {
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @Param id path string true "Process ID"
// @Param key path string true "Key for data store" // @Param key path string true "Key for data store"
// @Param domain query string false "Domain to act on"
// @Success 200 {object} api.Metadata // @Success 200 {object} api.Metadata
// @Failure 404 {object} api.Error
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process/{id}/metadata/{key} [get] // @Router /api/v3/process/{id}/metadata/{key} [get]
func (h *RestreamHandler) GetProcessMetadata(c echo.Context) error { func (h *RestreamHandler) GetProcessMetadata(c echo.Context) error {
id := util.PathParam(c, "id") id := util.PathParam(c, "id")
key := util.PathParam(c, "key") key := util.PathParam(c, "key")
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "") 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") return api.Err(http.StatusForbidden, "Forbidden")
} }
@@ -578,19 +609,21 @@ func (h *RestreamHandler) GetProcessMetadata(c echo.Context) error {
// @Produce json // @Produce json
// @Param id path string true "Process ID" // @Param id path string true "Process ID"
// @Param key path string true "Key for data store" // @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" // @Param data body api.Metadata true "Arbitrary JSON data. The null value will remove the key and its contents"
// @Success 200 {object} api.Metadata // @Success 200 {object} api.Metadata
// @Failure 404 {object} api.Error
// @Failure 400 {object} api.Error // @Failure 400 {object} api.Error
// @Failure 403 {object} api.Error
// @Failure 404 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/process/{id}/metadata/{key} [put] // @Router /api/v3/process/{id}/metadata/{key} [put]
func (h *RestreamHandler) SetProcessMetadata(c echo.Context) error { func (h *RestreamHandler) SetProcessMetadata(c echo.Context) error {
id := util.PathParam(c, "id") id := util.PathParam(c, "id")
key := util.PathParam(c, "key") key := util.PathParam(c, "key")
user := util.DefaultContext(c, "user", "") ctxuser := util.DefaultContext(c, "user", "")
domain := util.DefaultQuery(c, "domain", "") 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") 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 // 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. // 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. // first path element after a mountpoint for filesystem requests.
// //
// If the domain can't be detected, the domain "$none" will be used. // 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() username = config.IAM.GetDefaultVerifier().Name()
} }
domain = c.QueryParam("group") domain = c.QueryParam("domain")
resource = "api:" + resource resource = "api:" + resource
} else { } else {
identity, err = mw.findIdentityFromBasicAuth(c) identity, err = mw.findIdentityFromBasicAuth(c)

View File

@@ -549,11 +549,11 @@ func (s *server) setRoutesV3(v3 *echo.Group) {
// v3 IAM // v3 IAM
if s.v3handler.iam != nil { if s.v3handler.iam != nil {
v3.POST("/iam/user", s.v3handler.iam.AddUser) v3.POST("/iam/user", s.v3handler.iam.AddIdentity)
v3.GET("/iam/user/:name", s.v3handler.iam.GetUser) v3.GET("/iam/user/:name", s.v3handler.iam.GetIdentity)
v3.PUT("/iam/user/:name", s.v3handler.iam.UpdateUser) v3.PUT("/iam/user/:name", s.v3handler.iam.UpdateIdentity)
v3.PUT("/iam/user/:name/policy", s.v3handler.iam.UpdateUserPolicies) v3.PUT("/iam/user/:name/policy", s.v3handler.iam.UpdateIdentityPolicies)
v3.DELETE("/iam/user/:name", s.v3handler.iam.RemoveUser) v3.DELETE("/iam/user/:name", s.v3handler.iam.RemoveIdentity)
} }
// v3 Restreamer // v3 Restreamer
@@ -658,10 +658,12 @@ func (s *server) setRoutesV3(v3 *echo.Group) {
v3.GET("/cluster", s.v3handler.cluster.About) v3.GET("/cluster", s.v3handler.cluster.About)
v3.GET("/cluster/db/process", s.v3handler.cluster.ListStoreProcesses) 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", 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", 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/iam/policies", s.v3handler.cluster.ListPolicies)
v3.GET("/cluster/process", s.v3handler.cluster.ListAllNodeProcesses) 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.PUT("/cluster/process/:id", s.v3handler.cluster.UpdateProcess)
v3.DELETE("/cluster/process/:id", s.v3handler.cluster.DeleteProcess) 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.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.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 { func (i *iam) ListIdentities() []identity.User {
return nil return i.im.List()
} }
func (i *iam) ReloadIndentities() error { func (i *iam) ReloadIndentities() error {