diff --git a/app/api/api.go b/app/api/api.go index f0b5d371..aba706f0 100644 --- a/app/api/api.go +++ b/app/api/api.go @@ -453,6 +453,7 @@ func (a *api) start() error { if !cfg.API.Auth.Enable { iam.AddPolicy("$anon", "$none", "api:/api/**", []string{"ANY"}) iam.AddPolicy("$anon", "$none", "process:*", []string{"ANY"}) + iam.AddPolicy("$anon", "$none", "iam:*", []string{"ANY"}) } else { if cfg.API.Auth.DisableLocalhost { iam.AddPolicy("$localhost", "$none", "api:/api", []string{"GET", "HEAD", "OPTIONS"}) @@ -460,6 +461,7 @@ func (a *api) start() error { iam.AddPolicy("$localhost", "$none", "api:/api/**", []string{"ANY"}) iam.AddPolicy("$localhost", "$none", "process:*", []string{"ANY"}) + iam.AddPolicy("$localhost", "$none", "iam:*", []string{"ANY"}) } } diff --git a/docs/docs.go b/docs/docs.go index 05431c32..e287b3b9 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -468,6 +468,487 @@ const docTemplate = `{ } } }, + "/api/v3/iam/group": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "List all groups", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "List all groups", + "operationId": "iam-3-list-groups", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Create a group with admins", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Create a group with admins", + "operationId": "iam-3-add-group", + "parameters": [ + { + "description": "Group to add", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.IAMGroup" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.IAMGroup" + } + }, + "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" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/api/v3/iam/group/{group}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get details of a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Get details of a group", + "operationId": "iam-3-get-group", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.IAMGroup" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Remove a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Remove a group", + "operationId": "iam-3-remove-group", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.IAMGroup" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/api/v3/iam/group/{group}/user": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "List all users in a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "List all users in a group", + "operationId": "iam-3-get-group-users", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Add an user to a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Add an user to a group", + "operationId": "iam-3-add-group-user", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + }, + { + "description": "User to add", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.IAMGroupUser" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "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" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/api/v3/iam/group/{group}/user/{name}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get the details of a user in a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Get the details of a user in a group", + "operationId": "iam-3-get-group-user", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "user name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Set the policies of a user in a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Set the policies of a user in a group", + "operationId": "iam-3-update-group-user", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "user name", + "name": "name", + "in": "path", + "required": true + }, + { + "description": "User to add", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.IAMPolicy" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Remove a user from a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Remove a user from a group", + "operationId": "iam-3-remove-group-user", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "user name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/api/v3/iam/user": { "post": { "security": [ @@ -2823,6 +3304,37 @@ const docTemplate = `{ } } }, + "api.IAMGroup": { + "type": "object", + "properties": { + "admins": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + } + } + }, + "api.IAMGroupUser": { + "type": "object", + "properties": { + "admin": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/api.IAMPolicy" + } + } + } + }, "api.IAMPolicy": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index e380b060..f9ae935f 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -461,6 +461,487 @@ } } }, + "/api/v3/iam/group": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "List all groups", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "List all groups", + "operationId": "iam-3-list-groups", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Create a group with admins", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Create a group with admins", + "operationId": "iam-3-add-group", + "parameters": [ + { + "description": "Group to add", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.IAMGroup" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.IAMGroup" + } + }, + "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" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/api/v3/iam/group/{group}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get details of a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Get details of a group", + "operationId": "iam-3-get-group", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.IAMGroup" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Remove a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Remove a group", + "operationId": "iam-3-remove-group", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.IAMGroup" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/api/v3/iam/group/{group}/user": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "List all users in a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "List all users in a group", + "operationId": "iam-3-get-group-users", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Add an user to a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Add an user to a group", + "operationId": "iam-3-add-group-user", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + }, + { + "description": "User to add", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.IAMGroupUser" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "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" + } + }, + "409": { + "description": "Conflict", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, + "/api/v3/iam/group/{group}/user/{name}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get the details of a user in a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Get the details of a user in a group", + "operationId": "iam-3-get-group-user", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "user name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Set the policies of a user in a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Set the policies of a user in a group", + "operationId": "iam-3-update-group-user", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "user name", + "name": "name", + "in": "path", + "required": true + }, + { + "description": "User to add", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.IAMPolicy" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Remove a user from a group", + "produces": [ + "application/json" + ], + "tags": [ + "v16.?.?" + ], + "summary": "Remove a user from a group", + "operationId": "iam-3-remove-group-user", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "group", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "user name", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/api/v3/iam/user": { "post": { "security": [ @@ -2816,6 +3297,37 @@ } } }, + "api.IAMGroup": { + "type": "object", + "properties": { + "admins": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + } + } + }, + "api.IAMGroupUser": { + "type": "object", + "properties": { + "admin": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/api.IAMPolicy" + } + } + } + }, "api.IAMPolicy": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 9481a3f2..44c487a3 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -478,6 +478,26 @@ definitions: domain: type: string type: object + api.IAMGroup: + properties: + admins: + items: + type: string + type: array + name: + type: string + type: object + api.IAMGroupUser: + properties: + admin: + type: boolean + name: + type: string + policies: + items: + $ref: '#/definitions/api.IAMPolicy' + type: array + type: object api.IAMPolicy: properties: actions: @@ -2266,6 +2286,317 @@ paths: security: - ApiKeyAuth: [] summary: Add a file to a filesystem + /api/v3/iam/group: + get: + description: List all groups + operationId: iam-3-list-groups + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.Error' + security: + - ApiKeyAuth: [] + summary: List all groups + tags: + - v16.?.? + post: + description: Create a group with admins + operationId: iam-3-add-group + parameters: + - description: Group to add + in: body + name: config + required: true + schema: + $ref: '#/definitions/api.IAMGroup' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.IAMGroup' + "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' + "409": + description: Conflict + schema: + $ref: '#/definitions/api.Error' + security: + - ApiKeyAuth: [] + summary: Create a group with admins + tags: + - v16.?.? + /api/v3/iam/group/{group}: + delete: + description: Remove a group + operationId: iam-3-remove-group + parameters: + - description: group name + in: path + name: group + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.IAMGroup' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.Error' + "404": + description: Not Found + schema: + $ref: '#/definitions/api.Error' + security: + - ApiKeyAuth: [] + summary: Remove a group + tags: + - v16.?.? + get: + description: Get details of a group + operationId: iam-3-get-group + parameters: + - description: group name + in: path + name: group + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.IAMGroup' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.Error' + "404": + description: Not Found + schema: + $ref: '#/definitions/api.Error' + security: + - ApiKeyAuth: [] + summary: Get details of a group + tags: + - v16.?.? + /api/v3/iam/group/{group}/user: + get: + description: List all users in a group + operationId: iam-3-get-group-users + parameters: + - description: group name + in: path + name: group + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.Error' + "404": + description: Not Found + schema: + $ref: '#/definitions/api.Error' + security: + - ApiKeyAuth: [] + summary: List all users in a group + tags: + - v16.?.? + post: + description: Add an user to a group + operationId: iam-3-add-group-user + parameters: + - description: group name + in: path + name: group + required: true + type: string + - description: User to add + in: body + name: config + required: true + schema: + $ref: '#/definitions/api.IAMGroupUser' + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "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' + "409": + description: Conflict + schema: + $ref: '#/definitions/api.Error' + security: + - ApiKeyAuth: [] + summary: Add an user to a group + tags: + - v16.?.? + /api/v3/iam/group/{group}/user/{name}: + delete: + description: Remove a user from a group + operationId: iam-3-remove-group-user + parameters: + - description: group name + in: path + name: group + required: true + type: string + - description: user name + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.Error' + security: + - ApiKeyAuth: [] + summary: Remove a user from a group + tags: + - v16.?.? + get: + description: Get the details of a user in a group + operationId: iam-3-get-group-user + parameters: + - description: group name + in: path + name: group + required: true + type: string + - description: user name + in: path + name: name + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.Error' + "404": + description: Not Found + schema: + $ref: '#/definitions/api.Error' + security: + - ApiKeyAuth: [] + summary: Get the details of a user in a group + tags: + - v16.?.? + put: + description: Set the policies of a user in a group + operationId: iam-3-update-group-user + parameters: + - description: group name + in: path + name: group + required: true + type: string + - description: user name + in: path + name: name + required: true + type: string + - description: User to add + in: body + name: config + required: true + schema: + $ref: '#/definitions/api.IAMPolicy' + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.Error' + security: + - ApiKeyAuth: [] + summary: Set the policies of a user in a group + tags: + - v16.?.? /api/v3/iam/user: post: consumes: diff --git a/http/api/iam.go b/http/api/iam.go index 0a8e5417..6f9ce464 100644 --- a/http/api/iam.go +++ b/http/api/iam.go @@ -128,3 +128,9 @@ type IAMGroup struct { Name string `json:"name"` Admins []string `json:"admins"` } + +type IAMGroupUser struct { + Name string `json:"name"` + Admin bool `json:"admin"` + Policies []IAMPolicy `json:"policies"` +} diff --git a/http/handler/api/iam.go b/http/handler/api/iam.go index 0cb98685..6a7a77bc 100644 --- a/http/handler/api/iam.go +++ b/http/handler/api/iam.go @@ -20,7 +20,7 @@ func NewIAM(iam iam.IAM) *IAMHandler { } } -// Add adds a new user +// AddUser adds a new user // @Summary Add a new user // @Description Add a new user // @Tags v16.?.? @@ -34,7 +34,11 @@ func NewIAM(iam iam.IAM) *IAMHandler { // @Security ApiKeyAuth // @Router /api/v3/iam/user [post] func (h *IAMHandler) AddUser(c echo.Context) error { - //user := util.DefaultContext(c, "user", "") + ctxuser := util.DefaultContext(c, "user", "") + + if !h.iam.Enforce(ctxuser, "$none", "iam:/user", "write") { + return api.Err(http.StatusForbidden, "Forbidden") + } user := api.IAMUser{} @@ -54,7 +58,7 @@ func (h *IAMHandler) AddUser(c echo.Context) error { continue } - h.iam.AddPolicy(p.Name, "", p.Resource, p.Actions) + h.iam.AddPolicy(p.Name, "$none", p.Resource, p.Actions) } err = h.iam.SaveIdentities() @@ -65,7 +69,7 @@ func (h *IAMHandler) AddUser(c echo.Context) error { return c.JSON(http.StatusOK, user) } -// Delete deletes the user with the given name +// RemoveUser deletes the user with the given name // @Summary Delete an user by its name // @Description Delete an user by its name // @Tags v16.?.? @@ -78,8 +82,13 @@ func (h *IAMHandler) AddUser(c echo.Context) error { // @Security ApiKeyAuth // @Router /api/v3/iam/user/{name} [delete] func (h *IAMHandler) RemoveUser(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") name := util.PathParam(c, "name") + if !h.iam.Enforce(ctxuser, "$none", "iam:/user", "write") { + return api.Err(http.StatusForbidden, "Forbidden") + } + // Remove all policies of that user h.iam.RemovePolicy(name, "", "", nil) @@ -97,7 +106,7 @@ func (h *IAMHandler) RemoveUser(c echo.Context) error { return c.JSON(http.StatusOK, "OK") } -// Update replaces an existing user +// UpdateUser replaces an existing user // @Summary Replace an existing user // @Description Replace an existing user. // @Tags v16.?.? @@ -113,8 +122,13 @@ func (h *IAMHandler) RemoveUser(c echo.Context) error { // @Security ApiKeyAuth // @Router /api/v3/iam/user/{name} [put] func (h *IAMHandler) UpdateUser(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") name := util.PathParam(c, "name") + if !h.iam.Enforce(ctxuser, "$none", "iam:/user", "write") { + return api.Err(http.StatusForbidden, "Forbidden") + } + iamuser, err := h.iam.GetIdentity(name) if err != nil { return api.Err(http.StatusNotFound, "Not found", "%s", err) @@ -143,7 +157,7 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error { continue } - h.iam.AddPolicy(p.Name, "", p.Resource, p.Actions) + h.iam.AddPolicy(p.Name, "$none", p.Resource, p.Actions) } err = h.iam.SaveIdentities() @@ -154,7 +168,7 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error { return c.JSON(http.StatusOK, user) } -// Get returns the user with the given name +// GetUser returns the user with the given name // @Summary List an user by its name // @Description List aa user by its name // @Tags v16.?.? @@ -166,8 +180,13 @@ func (h *IAMHandler) UpdateUser(c echo.Context) error { // @Security ApiKeyAuth // @Router /api/v3/iam/user/{name} [get] func (h *IAMHandler) GetUser(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") name := util.PathParam(c, "name") + if !h.iam.Enforce(ctxuser, "$none", "iam:/user", "read") { + return api.Err(http.StatusForbidden, "Forbidden") + } + iamuser, err := h.iam.GetIdentity(name) if err != nil { return api.Err(http.StatusNotFound, "Not found", "%s", err) @@ -181,9 +200,26 @@ func (h *IAMHandler) GetUser(c echo.Context) error { return c.JSON(http.StatusOK, user) } +// AddGroup creates a group with admins +// @Summary Create a group with admins +// @Description Create a group with admins +// @Tags v16.?.? +// @ID iam-3-add-group +// @Produce json +// @Param config body api.IAMGroup true "Group to add" +// @Success 200 {object} api.IAMGroup +// @Failure 400 {object} api.Error +// @Failure 403 {object} api.Error +// @Failure 404 {object} api.Error +// @Failure 409 {object} api.Error +// @Security ApiKeyAuth // @Router /api/v3/iam/group [post] func (h *IAMHandler) AddGroup(c echo.Context) error { - user := util.DefaultContext(c, "user", "") + ctxuser := util.DefaultContext(c, "user", "") + + if !h.iam.Enforce(ctxuser, "$none", "iam:/group", "write") { + return api.Err(http.StatusForbidden, "Forbidden") + } group := api.IAMGroup{} @@ -195,6 +231,10 @@ func (h *IAMHandler) AddGroup(c echo.Context) error { return api.Err(http.StatusConflict, "Conflict", "this group already exists") } + if len(group.Admins) == 0 { + return api.Err(http.StatusBadRequest, "Bad request", "at leas one admin must be defined") + } + for _, admin := range group.Admins { _, err := h.iam.GetIdentity(admin) if err != nil { @@ -202,51 +242,139 @@ func (h *IAMHandler) AddGroup(c echo.Context) error { } } - if !h.iam.Enforce(user, group.Name, "iam:/group", "write") { - return api.Err(http.StatusForbidden, "Forbidden") - } - for _, admin := range group.Admins { - h.iam.AddPolicy(admin, group.Name, "iam:/group/**", []string{"read", "write"}) + h.iam.AddPolicy(admin, group.Name, "api:/api/**", []string{"get", "options", "head"}) + h.iam.AddPolicy(admin, group.Name, "api:/api/v3/process", []string{"ANY"}) + h.iam.AddPolicy(admin, group.Name, "api:/api/v3/process/**", []string{"ANY"}) + h.iam.AddPolicy(admin, group.Name, "process:*", []string{"ANY"}) + h.iam.AddPolicy(admin, group.Name, "fs:/"+group.Name+"/**", []string{"ANY"}) + h.iam.AddPolicy(admin, group.Name, "fs:/memfs/"+group.Name+"/**", []string{"ANY"}) + h.iam.AddPolicy(admin, group.Name, "rtmp:/"+group.Name+"/**", []string{"ANY"}) + h.iam.AddPolicy(admin, group.Name, "srt:"+group.Name+"/**", []string{"ANY"}) + h.iam.AddPolicy(admin, group.Name, "iam:/group/"+group.Name, []string{"ANY"}) } return c.JSON(http.StatusOK, group) } -// @Router /api/v3/iam/group/{name} [delete] -func (h *IAMHandler) RemoveGroup(c echo.Context) error { - user := util.DefaultContext(c, "user", "") - name := util.PathParam(c, "name") +// ListGroups lists all groups +// @Summary List all groups +// @Description List all groups +// @Tags v16.?.? +// @ID iam-3-list-groups +// @Produce json +// @Success 200 {array} string +// @Failure 403 {object} api.Error +// @Security ApiKeyAuth +// @Router /api/v3/iam/group [get] +func (h *IAMHandler) ListGroups(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") - if !h.iam.HasDomain(name) { - return api.Err(http.StatusNotFound, "Not found") - } - - if !h.iam.Enforce(user, name, "iam:/group", "write") { + if !h.iam.Enforce(ctxuser, "$none", "iam:/group", "read") { return api.Err(http.StatusForbidden, "Forbidden") } - h.iam.RemovePolicy("", name, "", nil) + return c.JSON(http.StatusOK, h.iam.ListDomains()) +} + +// RemoveGroup removes a group +// @Summary Remove a group +// @Description Remove a group +// @Tags v16.?.? +// @ID iam-3-remove-group +// @Produce json +// @Param group path string true "group name" +// @Success 200 {object} api.IAMGroup +// @Failure 403 {object} api.Error +// @Failure 404 {object} api.Error +// @Security ApiKeyAuth +// @Router /api/v3/iam/group/{group} [delete] +func (h *IAMHandler) RemoveGroup(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") + group := util.PathParam(c, "group") + + if !h.iam.Enforce(ctxuser, group, "iam:/group/"+group, "admin") { + return api.Err(http.StatusForbidden, "Forbidden", "only group admin can remove a group") + } + + if !h.iam.HasDomain(group) { + return api.Err(http.StatusNotFound, "Not found") + } + + h.iam.RemovePolicy("", group, "", nil) return c.JSON(http.StatusOK, "OK") } -// @Router /api/v3/iam/group/{name} [get] +// GetGroup returns details of a group +// @Summary Get details of a group +// @Description Get details of a group +// @Tags v16.?.? +// @ID iam-3-get-group +// @Produce json +// @Param group path string true "group name" +// @Success 200 {object} api.IAMGroup +// @Failure 403 {object} api.Error +// @Failure 404 {object} api.Error +// @Security ApiKeyAuth +// @Router /api/v3/iam/group/{group} [get] func (h *IAMHandler) GetGroup(c echo.Context) error { - user := util.DefaultContext(c, "user", "") - name := util.PathParam(c, "name") + ctxuser := util.DefaultContext(c, "user", "") + group := util.PathParam(c, "group") - if !h.iam.HasDomain(name) { + if !h.iam.Enforce(ctxuser, group, "iam:/group/"+group, "read") { + return api.Err(http.StatusForbidden, "Forbidden") + } + + if !h.iam.HasDomain(group) { return api.Err(http.StatusNotFound, "Not found") } - if !h.iam.Enforce(user, name, "iam:/group", "read") { + g := api.IAMGroup{ + Name: group, + } + + admins := map[string]struct{}{} + + policies := h.iam.ListPolicies("", group, "iam:/group/"+g.Name, []string{"any"}) + for _, p := range policies { + admins[p.Name] = struct{}{} + } + + for name := range admins { + g.Admins = append(g.Admins, name) + } + + return c.JSON(http.StatusOK, g) +} + +// ListGroupUsers lists all users of a group +// @Summary List all users in a group +// @Description List all users in a group +// @Tags v16.?.? +// @ID iam-3-get-group-users +// @Produce json +// @Param group path string true "group name" +// @Success 200 {array} string +// @Failure 403 {object} api.Error +// @Failure 404 {object} api.Error +// @Security ApiKeyAuth +// @Router /api/v3/iam/group/{group}/user [get] +func (h *IAMHandler) ListGroupUsers(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") + group := util.PathParam(c, "group") + + if !h.iam.Enforce(ctxuser, group, "iam:/group/"+group, "read") { return api.Err(http.StatusForbidden, "Forbidden") } + if !h.iam.HasDomain(group) { + return api.Err(http.StatusNotFound, "Not found") + } + members := map[string]struct{}{} - policies := h.iam.ListPolicies("", name, "", nil) + policies := h.iam.ListPolicies("", group, "", nil) for _, p := range policies { members[p.Name] = struct{}{} } @@ -259,3 +387,178 @@ func (h *IAMHandler) GetGroup(c echo.Context) error { return c.JSON(http.StatusOK, list) } + +// AddGroupUser adds an user to a group +// @Summary Add an user to a group +// @Description Add an user to a group +// @Tags v16.?.? +// @ID iam-3-add-group-user +// @Produce json +// @Param group path string true "group name" +// @Param config body api.IAMGroupUser true "User to add" +// @Success 200 {array} string +// @Failure 400 {object} api.Error +// @Failure 403 {object} api.Error +// @Failure 404 {object} api.Error +// @Failure 409 {object} api.Error +// @Security ApiKeyAuth +// @Router /api/v3/iam/group/{group}/user [post] +func (h *IAMHandler) AddGroupUser(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") + group := util.PathParam(c, "group") + + if !h.iam.Enforce(ctxuser, group, "iam:/group/"+group, "write") { + return api.Err(http.StatusForbidden, "Forbidden") + } + + if !h.iam.HasDomain(group) { + return api.Err(http.StatusNotFound, "Not found", "this group doesn't exists") + } + + user := api.IAMGroupUser{} + + if err := util.ShouldBindJSON(c, &user); err != nil { + return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) + } + + policies := h.iam.ListPolicies(user.Name, group, "", nil) + if len(policies) != 0 { + return api.Err(http.StatusConflict, "Conflict", "this user is already in the group") + } + + // Check if admin and add admin rights if required + if user.Admin { + if !h.iam.Enforce(ctxuser, group, "iam:/group/"+group, "admin") { + return api.Err(http.StatusForbidden, "Forbidden", "you can't add admins to this group") + } + + h.iam.AddPolicy(user.Name, group, "iam:/group/"+group, []string{"ANY"}) + } + + for _, p := range user.Policies { + if p.Domain != group { + continue + } + + h.iam.AddPolicy(user.Name, group, p.Resource, p.Actions) + } + + return c.JSON(http.StatusOK, "OK") +} + +// GetGroupUser returns the details of a user in a group +// @Summary Get the details of a user in a group +// @Description Get the details of a user in a group +// @Tags v16.?.? +// @ID iam-3-get-group-user +// @Produce json +// @Param group path string true "group name" +// @Param name path string true "user name" +// @Success 200 {array} string +// @Failure 403 {object} api.Error +// @Failure 404 {object} api.Error +// @Security ApiKeyAuth +// @Router /api/v3/iam/group/{group}/user/{name} [get] +func (h *IAMHandler) GetGroupUser(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") + group := util.PathParam(c, "group") + name := util.PathParam(c, "name") + + if !h.iam.Enforce(ctxuser, group, "iam:/group/"+group, "read") { + return api.Err(http.StatusForbidden, "Forbidden") + } + + if !h.iam.HasDomain(group) { + return api.Err(http.StatusNotFound, "Not found") + } + + policies := h.iam.ListPolicies(name, group, "", nil) + if len(policies) == 0 { + return api.Err(http.StatusNotFound, "Not found") + } + + user := api.IAMGroupUser{ + Name: name, + Admin: h.iam.HasPolicy(name, group, "iam:/group/"+group, []string{"any"}), + } + + for _, p := range policies { + user.Policies = append(user.Policies, api.IAMPolicy{ + Domain: group, + Resource: p.Resource, + Actions: p.Actions, + }) + } + + return c.JSON(http.StatusOK, user) +} + +// UpdateGroupUser sets the policies of a user in a group +// @Summary Set the policies of a user in a group +// @Description Set the policies of a user in a group +// @Tags v16.?.? +// @ID iam-3-update-group-user +// @Produce json +// @Param group path string true "group name" +// @Param name path string true "user name" +// @Param config body api.IAMPolicy true "User to add" +// @Success 200 {array} string +// @Failure 400 {object} api.Error +// @Failure 403 {object} api.Error +// @Security ApiKeyAuth +// @Router /api/v3/iam/group/{group}/user/{name} [put] +func (h *IAMHandler) UpdateGroupUser(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") + group := util.PathParam(c, "group") + //name := util.PathParam(c, "name") + + if !h.iam.Enforce(ctxuser, group, "iam:/group/"+group, "write") { + return api.Err(http.StatusForbidden, "Forbidden") + } + + policies := []api.IAMPolicy{} + + if err := util.ShouldBindJSON(c, &policies); err != nil { + return api.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// RemoveGroupUser removes a user from a group +// @Summary Remove a user from a group +// @Description Remove a user from a group +// @Tags v16.?.? +// @ID iam-3-remove-group-user +// @Produce json +// @Param group path string true "group name" +// @Param name path string true "user name" +// @Success 200 {array} string +// @Failure 400 {object} api.Error +// @Failure 403 {object} api.Error +// @Security ApiKeyAuth +// @Router /api/v3/iam/group/{group}/user/{name} [delete] +func (h *IAMHandler) RemoveGroupUser(c echo.Context) error { + ctxuser := util.DefaultContext(c, "user", "") + group := util.PathParam(c, "group") + name := util.PathParam(c, "name") + + if !h.iam.Enforce(ctxuser, group, "iam:/group/"+group, "write") { + return api.Err(http.StatusForbidden, "Forbidden") + } + + // Check if the user to be deleted is an admin. If yes, you have to be an admin too. + if h.iam.HasPolicy(name, group, "iam:/group/"+group, []string{"any"}) { + if !h.iam.Enforce(ctxuser, group, "iam:/group/"+group, "admin") { + return api.Err(http.StatusForbidden, "Forbidden") + } + } + + if len(h.iam.ListPolicies(name, group, "", nil)) == 0 { + return api.Err(http.StatusNotFound, "Not found") + } + + h.iam.RemovePolicy(name, group, "", nil) + + return c.JSON(http.StatusOK, "OK") +} diff --git a/http/server.go b/http/server.go index b71a2c00..1bc9c7d2 100644 --- a/http/server.go +++ b/http/server.go @@ -538,6 +538,17 @@ func (s *server) setRoutesV3(v3 *echo.Group) { v3.GET("/iam/user/:name", s.v3handler.iam.GetUser) v3.PUT("/iam/user/:name", s.v3handler.iam.UpdateUser) v3.DELETE("/iam/user/:name", s.v3handler.iam.RemoveUser) + + v3.POST("/iam/group", s.v3handler.iam.AddGroup) + v3.GET("/iam/group", s.v3handler.iam.ListGroups) + v3.GET("/iam/group/:group", s.v3handler.iam.GetGroup) + v3.DELETE("/iam/group/:group", s.v3handler.iam.RemoveGroup) + + v3.POST("/iam/group/:group/user", s.v3handler.iam.AddGroupUser) + v3.GET("/iam/group/:group/user", s.v3handler.iam.ListGroupUsers) + v3.GET("/iam/group/:group/user/:name", s.v3handler.iam.GetGroupUser) + v3.PUT("/iam/group/:group/user/:name", s.v3handler.iam.UpdateGroupUser) + v3.DELETE("/iam/group/:group/user/:name", s.v3handler.iam.RemoveGroupUser) } // v3 Restreamer diff --git a/iam/access.go b/iam/access.go index 353c2339..73acb791 100644 --- a/iam/access.go +++ b/iam/access.go @@ -20,12 +20,15 @@ type Policy struct { type AccessEnforcer interface { Enforce(name, domain, resource, action string) (bool, string) - HasGroup(name string) bool + + HasDomain(name string) bool + ListDomains() []string } type AccessManager interface { AccessEnforcer + HasPolicy(name, domain, resource string, actions []string) bool AddPolicy(name, domain, resource string, actions []string) bool RemovePolicy(name, domain, resource string, actions []string) bool ListPolicies(name, domain, resource string, actions []string) []Policy @@ -84,6 +87,12 @@ func NewAccessManager(config AccessConfig) (AccessManager, error) { return am, nil } +func (am *access) HasPolicy(name, domain, resource string, actions []string) bool { + policy := []string{name, domain, resource, strings.Join(actions, "|")} + + return am.enforcer.HasPolicy(policy) +} + func (am *access) AddPolicy(name, domain, resource string, actions []string) bool { policy := []string{name, domain, resource, strings.Join(actions, "|")} @@ -120,7 +129,7 @@ func (am *access) ListPolicies(name, domain, resource string, actions []string) return policies } -func (am *access) HasGroup(name string) bool { +func (am *access) HasDomain(name string) bool { groups := am.adapter.getAllGroups() for _, g := range groups { @@ -132,6 +141,10 @@ func (am *access) HasGroup(name string) bool { return false } +func (am *access) ListDomains() []string { + return am.adapter.getAllGroups() +} + func (am *access) Enforce(name, domain, resource, action string) (bool, string) { ok, rule, _ := am.enforcer.EnforceEx(name, domain, resource, action) diff --git a/iam/access_test.go b/iam/access_test.go index 3dc30779..01983091 100644 --- a/iam/access_test.go +++ b/iam/access_test.go @@ -57,9 +57,9 @@ func TestAccessManager(t *testing.T) { }, }, policies) - require.True(t, am.HasGroup("igelcamp")) - require.True(t, am.HasGroup("group")) - require.False(t, am.HasGroup("$none")) + require.True(t, am.HasDomain("igelcamp")) + require.True(t, am.HasDomain("group")) + require.False(t, am.HasDomain("$none")) am.RemovePolicy("ingo", "", "", nil) @@ -73,9 +73,9 @@ func TestAccessManager(t *testing.T) { }, }, policies) - require.False(t, am.HasGroup("igelcamp")) - require.True(t, am.HasGroup("group")) - require.False(t, am.HasGroup("$none")) + require.False(t, am.HasDomain("igelcamp")) + require.True(t, am.HasDomain("group")) + require.False(t, am.HasDomain("$none")) ok, _ := am.Enforce("foobar", "group", "bla:/", "read") require.False(t, ok) diff --git a/iam/iam.go b/iam/iam.go index 7346de4d..28b2ec28 100644 --- a/iam/iam.go +++ b/iam/iam.go @@ -7,8 +7,11 @@ import ( type IAM interface { Enforce(name, domain, resource, action string) bool - HasDomain(domain string) bool + HasDomain(domain string) bool + ListDomains() []string + + HasPolicy(name, domain, resource string, actions []string) bool AddPolicy(name, domain, resource string, actions []string) bool RemovePolicy(name, domain, resource string, actions []string) bool @@ -104,6 +107,10 @@ func (i *iam) Enforce(name, domain, resource, action string) bool { } } + //if name == "$localhost" { + // superuser = true + //} + l := i.logger.Debug().WithFields(log.Fields{ "subject": name, "domain": domain, @@ -168,13 +175,29 @@ func (i *iam) CreateJWT(name string) (string, string, error) { } func (i *iam) HasDomain(domain string) bool { - return i.am.HasGroup(domain) + return i.am.HasDomain(domain) +} + +func (i *iam) ListDomains() []string { + return i.am.ListDomains() } func (i *iam) Validators() []string { return i.im.Validators() } +func (i *iam) HasPolicy(name, domain, resource string, actions []string) bool { + if len(name) == 0 { + name = "$anon" + } + + if len(domain) == 0 { + domain = "$none" + } + + return i.am.HasPolicy(name, domain, resource, actions) +} + func (i *iam) AddPolicy(name, domain, resource string, actions []string) bool { if len(name) == 0 { name = "$anon"