From cda46141cc639890b8ab68c586f790d976edc9df Mon Sep 17 00:00:00 2001 From: Jan Eitzinger Date: Fri, 18 Aug 2023 13:03:11 +0200 Subject: [PATCH 1/3] Cleanup and add wildcard for IP Filter --- internal/api/rest.go | 69 +++----------------------------------------- 1 file changed, 4 insertions(+), 65 deletions(-) diff --git a/internal/api/rest.go b/internal/api/rest.go index 0716514..23ae53a 100644 --- a/internal/api/rest.go +++ b/internal/api/rest.go @@ -193,6 +193,10 @@ func securedCheck(r *http.Request) error { return fmt.Errorf("missing configuration key ApiAllowedIPs") } + if config.Keys.ApiAllowedIPs[0] == "*" { + return nil + } + // extract IP address IPAddress := r.Header.Get("X-Real-Ip") if IPAddress == "" { @@ -1130,71 +1134,6 @@ func (api *RestApi) updateUser(rw http.ResponseWriter, r *http.Request) { } } -// func (api *RestApi) secureUpdateUser(rw http.ResponseWriter, r *http.Request) { -// if user := auth.GetUser(r.Context()); user != nil && !user.HasRole(auth.RoleApi) { -// handleError(fmt.Errorf("missing role: %v", auth.GetRoleString(auth.RoleApi)), http.StatusForbidden, rw) -// return -// } -// -// // IP CHECK HERE (WIP) -// // Probably better as private routine -// IPAddress := r.Header.Get("X-Real-Ip") -// if IPAddress == "" { -// IPAddress = r.Header.Get("X-Forwarded-For") -// } -// if IPAddress == "" { -// IPAddress = r.RemoteAddr -// } -// -// // Also This -// ipOk := false -// for _, a := range config.Keys.ApiAllowedAddrs { -// if a == IPAddress { -// ipOk = true -// } -// } -// -// if IPAddress == "" || ipOk == false { -// handleError(fmt.Errorf("unknown ip: %v", IPAddress), http.StatusForbidden, rw) -// return -// } -// // IP CHECK END -// -// // Get Values -// id := mux.Vars(r)["id"] -// newproj := mux.Vars(r)["project"] -// newrole := mux.Vars(r)["role"] -// -// // TODO: Handle anything but roles... -// if newrole != "" { -// if err := api.Authentication.AddRole(r.Context(), id, newrole); err != nil { -// handleError(errors.New(err.Error()), http.StatusUnprocessableEntity, rw) -// return -// } -// -// rw.Header().Add("Content-Type", "application/json") -// rw.WriteHeader(http.StatusOK) -// json.NewEncoder(rw).Encode(UpdateUserApiResponse{ -// Message: fmt.Sprintf("Successfully added role %s to %s", newrole, id), -// }) -// -// } else if newproj != "" { -// if err := api.Authentication.AddProject(r.Context(), id, newproj); err != nil { -// handleError(errors.New(err.Error()), http.StatusUnprocessableEntity, rw) -// return -// } -// -// rw.Header().Add("Content-Type", "application/json") -// rw.WriteHeader(http.StatusOK) -// json.NewEncoder(rw).Encode(UpdateUserApiResponse{ -// Message: fmt.Sprintf("Successfully added project %s to %s", newproj, id), -// }) -// -// } else { -// handleError(errors.New("Not Add [role|project]?"), http.StatusBadRequest, rw) -// } -// } - func (api *RestApi) updateConfiguration(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("Content-Type", "text/plain") key, value := r.FormValue("key"), r.FormValue("value") From f6c4c963ec1360c51d05d8b3f504f5d435c6fd0a Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Fri, 18 Aug 2023 17:18:31 +0200 Subject: [PATCH 2/3] feat: Add users rest endpoint swagger docs --- README.md | 2 +- api/swagger.json | 312 ++++++++++++++++++++++++++++++++++++++++++- api/swagger.yaml | 213 ++++++++++++++++++++++++++++- internal/api/docs.go | 312 ++++++++++++++++++++++++++++++++++++++++++- internal/api/rest.go | 185 +++++++++++++++++-------- 5 files changed, 959 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 81bc017..de3a2ea 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ If you start `cc-backend` with the `-dev` flag, the GraphQL Playground UI is ava This project integrates [swagger ui] (https://swagger.io/tools/swagger-ui/) to document and test its REST API. The swagger documentation files can be found in `./api/`. You can generate the swagger-ui configuration by running `go run github.com/swaggo/swag/cmd/swag init -d ./internal/api,./pkg/schema -g rest.go -o ./api `. -You need to move the created `./api/doc.go` to `./internal/api/doc.go`. +You need to move the created `./api/docs.go` to `./internal/api/docs.go`. If you start cc-backend with the `-dev` flag, the Swagger interface is available at http://localhost:8080/swagger/. You must enter a JWT key for a user with the API role. diff --git a/api/swagger.json b/api/swagger.json index 87a7de5..2a4c43a 100644 --- a/api/swagger.json +++ b/api/swagger.json @@ -12,7 +12,7 @@ "name": "MIT License", "url": "https://opensource.org/licenses/MIT" }, - "version": "1" + "version": "1.0.0" }, "host": "localhost:8080", "basePath": "/api", @@ -707,6 +707,314 @@ } } } + }, + "/user/{id}": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Modifies user defined by username (id) in one of four possible ways.\nIf more than one formValue is set then only the highest priority field is used.", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "add and modify" + ], + "summary": "Updates an existing user", + "parameters": [ + { + "type": "string", + "description": "Database ID of User", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Priority 1: Role to add, one of: [admin, support, manager, user, api]", + "name": "add-role", + "in": "formData" + }, + { + "type": "string", + "description": "Priority 2: Role to remove, one of: [admin, support, manager, user, api]", + "name": "remove-role", + "in": "formData" + }, + { + "type": "string", + "description": "Priority 3: Project to add", + "name": "add-project", + "in": "formData" + }, + { + "type": "string", + "description": "Priority 4: Project to remove", + "name": "remove-project", + "in": "formData" + } + ], + "responses": { + "200": { + "description": "Task successful", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity: The user could not be updated", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + } + }, + "/users/": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a JSON-encoded list of users.\nRequired query-parameter defines if all users or only users with additional special roles are returned.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "query" + ], + "summary": "Returns a list of users", + "parameters": [ + { + "type": "boolean", + "description": "If returned list should contain all users or only users with additional special roles", + "name": "not-just-user", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "Users returned successfully", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "User specified in form data will be saved to database.", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "add and modify" + ], + "summary": "Adds a new user", + "parameters": [ + { + "type": "string", + "description": "Unique user ID", + "name": "username", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "User password", + "name": "password", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "User role, one of: [admin, support, manager, user, api]", + "name": "role", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Managed project, required for new manager role user", + "name": "project", + "in": "formData" + }, + { + "type": "string", + "description": "Users name", + "name": "name", + "in": "formData" + }, + { + "type": "string", + "description": "Users email", + "name": "email", + "in": "formData" + } + ], + "responses": { + "200": { + "description": "User added successfully", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity: creating user failed", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "User defined by username in form data will be deleted from database.", + "consumes": [ + "multipart/form-data" + ], + "tags": [ + "remove" + ], + "summary": "Deletes a user", + "parameters": [ + { + "type": "string", + "description": "User ID to delete", + "name": "username", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "User deleted successfully" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity: deleting user failed", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + } } }, "definitions": { @@ -1366,7 +1674,7 @@ "type": "object", "properties": { "id": { - "description": "The unique DB identifier of a tag\nThe unique DB identifier of a tag", + "description": "The unique DB identifier of a tag", "type": "integer" }, "name": { diff --git a/api/swagger.yaml b/api/swagger.yaml index 093266d..d19f79b 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -495,9 +495,7 @@ definitions: description: Defines a tag using name and type. properties: id: - description: |- - The unique DB identifier of a tag - The unique DB identifier of a tag + description: The unique DB identifier of a tag type: integer name: description: Tag Name @@ -526,7 +524,7 @@ info: name: MIT License url: https://opensource.org/licenses/MIT title: ClusterCockpit REST API - version: "1" + version: 1.0.0 paths: /jobs/: get: @@ -996,6 +994,213 @@ paths: summary: Adds one or more tags to a job tags: - add and modify + /user/{id}: + post: + consumes: + - multipart/form-data + description: |- + Modifies user defined by username (id) in one of four possible ways. + If more than one formValue is set then only the highest priority field is used. + parameters: + - description: Database ID of User + in: path + name: id + required: true + type: string + - description: 'Priority 1: Role to add, one of: [admin, support, manager, user, + api]' + in: formData + name: add-role + type: string + - description: 'Priority 2: Role to remove, one of: [admin, support, manager, + user, api]' + in: formData + name: remove-role + type: string + - description: 'Priority 3: Project to add' + in: formData + name: add-project + type: string + - description: 'Priority 4: Project to remove' + in: formData + name: remove-project + type: string + produces: + - text/plain + responses: + "200": + description: Task successful + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/api.ErrorResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.ErrorResponse' + "422": + description: 'Unprocessable Entity: The user could not be updated' + schema: + $ref: '#/definitions/api.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: Updates an existing user + tags: + - add and modify + /users/: + delete: + consumes: + - multipart/form-data + description: User defined by username in form data will be deleted from database. + parameters: + - description: User ID to delete + in: formData + name: username + required: true + type: string + responses: + "200": + description: User deleted successfully + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/api.ErrorResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.ErrorResponse' + "422": + description: 'Unprocessable Entity: deleting user failed' + schema: + $ref: '#/definitions/api.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: Deletes a user + tags: + - remove + get: + consumes: + - application/json + description: |- + Returns a JSON-encoded list of users. + Required query-parameter defines if all users or only users with additional special roles are returned. + parameters: + - description: If returned list should contain all users or only users with + additional special roles + in: query + name: not-just-user + required: true + type: boolean + produces: + - application/json + responses: + "200": + description: Users returned successfully + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/api.ErrorResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: Returns a list of users + tags: + - query + post: + consumes: + - multipart/form-data + description: User specified in form data will be saved to database. + parameters: + - description: Unique user ID + in: formData + name: username + required: true + type: string + - description: User password + in: formData + name: password + required: true + type: string + - description: 'User role, one of: [admin, support, manager, user, api]' + in: formData + name: role + required: true + type: string + - description: Managed project, required for new manager role user + in: formData + name: project + type: string + - description: Users name + in: formData + name: name + type: string + - description: Users email + in: formData + name: email + type: string + produces: + - text/plain + responses: + "200": + description: User added successfully + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.ErrorResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/api.ErrorResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/api.ErrorResponse' + "422": + description: 'Unprocessable Entity: creating user failed' + schema: + $ref: '#/definitions/api.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.ErrorResponse' + security: + - ApiKeyAuth: [] + summary: Adds a new user + tags: + - add and modify securityDefinitions: ApiKeyAuth: in: header diff --git a/internal/api/docs.go b/internal/api/docs.go index 85acc92..4f161e6 100644 --- a/internal/api/docs.go +++ b/internal/api/docs.go @@ -713,6 +713,314 @@ const docTemplate = `{ } } } + }, + "/user/{id}": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Modifies user defined by username (id) in one of four possible ways.\nIf more than one formValue is set then only the highest priority field is used.", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "add and modify" + ], + "summary": "Updates an existing user", + "parameters": [ + { + "type": "string", + "description": "Database ID of User", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Priority 1: Role to add, one of: [admin, support, manager, user, api]", + "name": "add-role", + "in": "formData" + }, + { + "type": "string", + "description": "Priority 2: Role to remove, one of: [admin, support, manager, user, api]", + "name": "remove-role", + "in": "formData" + }, + { + "type": "string", + "description": "Priority 3: Project to add", + "name": "add-project", + "in": "formData" + }, + { + "type": "string", + "description": "Priority 4: Project to remove", + "name": "remove-project", + "in": "formData" + } + ], + "responses": { + "200": { + "description": "Task successful", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity: The user could not be updated", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + } + }, + "/users/": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Returns a JSON-encoded list of users.\nRequired query-parameter defines if all users or only users with additional special roles are returned.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "query" + ], + "summary": "Returns a list of users", + "parameters": [ + { + "type": "boolean", + "description": "If returned list should contain all users or only users with additional special roles", + "name": "not-just-user", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "Users returned successfully", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "User specified in form data will be saved to database.", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "text/plain" + ], + "tags": [ + "add and modify" + ], + "summary": "Adds a new user", + "parameters": [ + { + "type": "string", + "description": "Unique user ID", + "name": "username", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "User password", + "name": "password", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "User role, one of: [admin, support, manager, user, api]", + "name": "role", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "Managed project, required for new manager role user", + "name": "project", + "in": "formData" + }, + { + "type": "string", + "description": "Users name", + "name": "name", + "in": "formData" + }, + { + "type": "string", + "description": "Users email", + "name": "email", + "in": "formData" + } + ], + "responses": { + "200": { + "description": "User added successfully", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity: creating user failed", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "User defined by username in form data will be deleted from database.", + "consumes": [ + "multipart/form-data" + ], + "tags": [ + "remove" + ], + "summary": "Deletes a user", + "parameters": [ + { + "type": "string", + "description": "User ID to delete", + "name": "username", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "User deleted successfully" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "422": { + "description": "Unprocessable Entity: deleting user failed", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.ErrorResponse" + } + } + } + } } }, "definitions": { @@ -1372,7 +1680,7 @@ const docTemplate = `{ "type": "object", "properties": { "id": { - "description": "The unique DB identifier of a tag\nThe unique DB identifier of a tag", + "description": "The unique DB identifier of a tag", "type": "integer" }, "name": { @@ -1415,7 +1723,7 @@ const docTemplate = `{ // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ - Version: "1", + Version: "1.0.0", Host: "localhost:8080", BasePath: "/api", Schemes: []string{}, diff --git a/internal/api/rest.go b/internal/api/rest.go index 23ae53a..67e4dbe 100644 --- a/internal/api/rest.go +++ b/internal/api/rest.go @@ -77,8 +77,6 @@ func (api *RestApi) MountRoutes(r *mux.Router) { r.HandleFunc("/jobs/delete_job/", api.deleteJobByRequest).Methods(http.MethodDelete) r.HandleFunc("/jobs/delete_job/{id}", api.deleteJobById).Methods(http.MethodDelete) r.HandleFunc("/jobs/delete_job_before/{ts}", api.deleteJobBefore).Methods(http.MethodDelete) - // r.HandleFunc("/secured/addProject/{id}/{project}", api.secureUpdateUser).Methods(http.MethodPost) - // r.HandleFunc("/secured/addRole/{id}/{role}", api.secureUpdateUser).Methods(http.MethodPost) if api.MachineStateDir != "" { r.HandleFunc("/machine_state/{cluster}/{host}", api.getMachineState).Methods(http.MethodGet) @@ -947,43 +945,31 @@ func (api *RestApi) getJobMetrics(rw http.ResponseWriter, r *http.Request) { }) } -func (api *RestApi) getJWT(rw http.ResponseWriter, r *http.Request) { - err := securedCheck(r) - if err != nil { - http.Error(rw, err.Error(), http.StatusForbidden) - } - - rw.Header().Set("Content-Type", "text/plain") - username := r.FormValue("username") - me := repository.GetUserFromContext(r.Context()) - if !me.HasRole(schema.RoleAdmin) { - if username != me.Username { - http.Error(rw, "Only admins are allowed to sign JWTs not for themselves", - http.StatusForbidden) - return - } - } - - user, err := repository.GetUserRepository().GetUser(username) - if err != nil { - http.Error(rw, err.Error(), http.StatusUnprocessableEntity) - return - } - - jwt, err := api.Authentication.JwtAuth.ProvideJWT(user) - if err != nil { - http.Error(rw, err.Error(), http.StatusUnprocessableEntity) - return - } - - rw.WriteHeader(http.StatusOK) - rw.Write([]byte(jwt)) -} - +// createUser godoc +// @summary Adds a new user +// @tags add and modify +// @description User specified in form data will be saved to database. +// @accept mpfd +// @produce plain +// @param username formData string true "Unique user ID" +// @param password formData string true "User password" +// @param role formData string true "User role, one of: [admin, support, manager, user, api]" +// @param project formData string false "Managed project, required for new manager role user" +// @param name formData string false "Users name" +// @param email formData string false "Users email" +// @success 200 {string} string "User added successfully" +// @failure 400 {object} api.ErrorResponse "Bad Request" +// @failure 401 {object} api.ErrorResponse "Unauthorized" +// @failure 403 {object} api.ErrorResponse "Forbidden" +// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: creating user failed" +// @failure 500 {object} api.ErrorResponse "Internal Server Error" +// @security ApiKeyAuth +// @router /users/ [post] func (api *RestApi) createUser(rw http.ResponseWriter, r *http.Request) { err := securedCheck(r) if err != nil { http.Error(rw, err.Error(), http.StatusForbidden) + return } rw.Header().Set("Content-Type", "text/plain") @@ -1026,10 +1012,25 @@ func (api *RestApi) createUser(rw http.ResponseWriter, r *http.Request) { rw.Write([]byte(fmt.Sprintf("User %v successfully created!\n", username))) } +// deleteUser godoc +// @summary Deletes a user +// @tags remove +// @description User defined by username in form data will be deleted from database. +// @accept mpfd +// @param username formData string true "User ID to delete" +// @success 200 "User deleted successfully" +// @failure 400 {object} api.ErrorResponse "Bad Request" +// @failure 401 {object} api.ErrorResponse "Unauthorized" +// @failure 403 {object} api.ErrorResponse "Forbidden" +// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: deleting user failed" +// @failure 500 {object} api.ErrorResponse "Internal Server Error" +// @security ApiKeyAuth +// @router /users/ [delete] func (api *RestApi) deleteUser(rw http.ResponseWriter, r *http.Request) { err := securedCheck(r) if err != nil { http.Error(rw, err.Error(), http.StatusForbidden) + return } if user := repository.GetUserFromContext(r.Context()); !user.HasRole(schema.RoleAdmin) { @@ -1046,10 +1047,26 @@ func (api *RestApi) deleteUser(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusOK) } +// getUsers godoc +// @summary Returns a list of users +// @tags query +// @description Returns a JSON-encoded list of users. +// @description Required query-parameter defines if all users or only users with additional special roles are returned. +// @accept json +// @produce json +// @param not-just-user query bool true "If returned list should contain all users or only users with additional special roles" +// @success 200 {string} json "Users returned successfully" +// @failure 400 {object} api.ErrorResponse "Bad Request" +// @failure 401 {object} api.ErrorResponse "Unauthorized" +// @failure 403 {object} api.ErrorResponse "Forbidden" +// @failure 500 {object} api.ErrorResponse "Internal Server Error" +// @security ApiKeyAuth +// @router /users/ [get] func (api *RestApi) getUsers(rw http.ResponseWriter, r *http.Request) { err := securedCheck(r) if err != nil { http.Error(rw, err.Error(), http.StatusForbidden) + return } if user := repository.GetUserFromContext(r.Context()); !user.HasRole(schema.RoleAdmin) { @@ -1066,31 +1083,31 @@ func (api *RestApi) getUsers(rw http.ResponseWriter, r *http.Request) { json.NewEncoder(rw).Encode(users) } -func (api *RestApi) getRoles(rw http.ResponseWriter, r *http.Request) { - err := securedCheck(r) - if err != nil { - http.Error(rw, err.Error(), http.StatusForbidden) - } - - user := repository.GetUserFromContext(r.Context()) - if !user.HasRole(schema.RoleAdmin) { - http.Error(rw, "only admins are allowed to fetch a list of roles", http.StatusForbidden) - return - } - - roles, err := schema.GetValidRoles(user) - if err != nil { - http.Error(rw, err.Error(), http.StatusInternalServerError) - return - } - - json.NewEncoder(rw).Encode(roles) -} - +// updateUser godoc +// @summary Updates an existing user +// @tags add and modify +// @description Modifies user defined by username (id) in one of four possible ways. +// @description If more than one formValue is set then only the highest priority field is used. +// @accept mpfd +// @produce plain +// @param id path string true "Database ID of User" +// @param add-role formData string false "Priority 1: Role to add, one of: [admin, support, manager, user, api]" +// @param remove-role formData string false "Priority 2: Role to remove, one of: [admin, support, manager, user, api]" +// @param add-project formData string false "Priority 3: Project to add" +// @param remove-project formData string false "Priority 4: Project to remove" +// @success 200 {string} string "Task successful" +// @failure 400 {object} api.ErrorResponse "Bad Request" +// @failure 401 {object} api.ErrorResponse "Unauthorized" +// @failure 403 {object} api.ErrorResponse "Forbidden" +// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: The user could not be updated" +// @failure 500 {object} api.ErrorResponse "Internal Server Error" +// @security ApiKeyAuth +// @router /user/{id} [post] func (api *RestApi) updateUser(rw http.ResponseWriter, r *http.Request) { err := securedCheck(r) if err != nil { http.Error(rw, err.Error(), http.StatusForbidden) + return } if user := repository.GetUserFromContext(r.Context()); !user.HasRole(schema.RoleAdmin) { @@ -1134,6 +1151,62 @@ func (api *RestApi) updateUser(rw http.ResponseWriter, r *http.Request) { } } +func (api *RestApi) getJWT(rw http.ResponseWriter, r *http.Request) { + err := securedCheck(r) + if err != nil { + http.Error(rw, err.Error(), http.StatusForbidden) + return + } + + rw.Header().Set("Content-Type", "text/plain") + username := r.FormValue("username") + me := repository.GetUserFromContext(r.Context()) + if !me.HasRole(schema.RoleAdmin) { + if username != me.Username { + http.Error(rw, "Only admins are allowed to sign JWTs not for themselves", + http.StatusForbidden) + return + } + } + + user, err := repository.GetUserRepository().GetUser(username) + if err != nil { + http.Error(rw, err.Error(), http.StatusUnprocessableEntity) + return + } + + jwt, err := api.Authentication.JwtAuth.ProvideJWT(user) + if err != nil { + http.Error(rw, err.Error(), http.StatusUnprocessableEntity) + return + } + + rw.WriteHeader(http.StatusOK) + rw.Write([]byte(jwt)) +} + +func (api *RestApi) getRoles(rw http.ResponseWriter, r *http.Request) { + err := securedCheck(r) + if err != nil { + http.Error(rw, err.Error(), http.StatusForbidden) + return + } + + user := repository.GetUserFromContext(r.Context()) + if !user.HasRole(schema.RoleAdmin) { + http.Error(rw, "only admins are allowed to fetch a list of roles", http.StatusForbidden) + return + } + + roles, err := schema.GetValidRoles(user) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + json.NewEncoder(rw).Encode(roles) +} + func (api *RestApi) updateConfiguration(rw http.ResponseWriter, r *http.Request) { rw.Header().Set("Content-Type", "text/plain") key, value := r.FormValue("key"), r.FormValue("value") From f36f62fb4776fc7a530fbb31bc0883964cb07530 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Mon, 21 Aug 2023 12:12:28 +0200 Subject: [PATCH 3/3] Improve user endpoint swagger docs --- api/swagger.json | 108 +++++++++++++++++++++++++++++++------------ api/swagger.yaml | 95 +++++++++++++++++++++++++------------ internal/api/docs.go | 108 +++++++++++++++++++++++++++++++------------ internal/api/rest.go | 68 +++++++++++++++------------ 4 files changed, 261 insertions(+), 118 deletions(-) diff --git a/api/swagger.json b/api/swagger.json index 2a4c43a..dfc2f75 100644 --- a/api/swagger.json +++ b/api/swagger.json @@ -735,14 +735,28 @@ "required": true }, { + "enum": [ + "admin", + "support", + "manager", + "user", + "api" + ], "type": "string", - "description": "Priority 1: Role to add, one of: [admin, support, manager, user, api]", + "description": "Priority 1: Role to add", "name": "add-role", "in": "formData" }, { + "enum": [ + "admin", + "support", + "manager", + "user", + "api" + ], "type": "string", - "description": "Priority 2: Role to remove, one of: [admin, support, manager, user, api]", + "description": "Priority 2: Role to remove", "name": "remove-role", "in": "formData" }, @@ -761,7 +775,7 @@ ], "responses": { "200": { - "description": "Task successful", + "description": "Success Response Message", "schema": { "type": "string" } @@ -769,31 +783,31 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "401": { "description": "Unauthorized", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "422": { "description": "Unprocessable Entity: The user could not be updated", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } } } @@ -807,9 +821,6 @@ } ], "description": "Returns a JSON-encoded list of users.\nRequired query-parameter defines if all users or only users with additional special roles are returned.", - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], @@ -828,33 +839,36 @@ ], "responses": { "200": { - "description": "Users returned successfully", + "description": "List of users returned successfully", "schema": { - "type": "string" + "type": "array", + "items": { + "$ref": "#/definitions/api.ApiReturnedUser" + } } }, "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "401": { "description": "Unauthorized", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } } } @@ -892,8 +906,15 @@ "required": true }, { + "enum": [ + "admin", + "support", + "manager", + "user", + "api" + ], "type": "string", - "description": "User role, one of: [admin, support, manager, user, api]", + "description": "User role", "name": "role", "in": "formData", "required": true @@ -919,7 +940,7 @@ ], "responses": { "200": { - "description": "User added successfully", + "description": "Success Response", "schema": { "type": "string" } @@ -927,31 +948,31 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "401": { "description": "Unauthorized", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "422": { "description": "Unprocessable Entity: creating user failed", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } } } @@ -966,6 +987,9 @@ "consumes": [ "multipart/form-data" ], + "produces": [ + "text/plain" + ], "tags": [ "remove" ], @@ -986,31 +1010,31 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "401": { "description": "Unauthorized", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "422": { "description": "Unprocessable Entity: deleting user failed", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } } } @@ -1018,6 +1042,32 @@ } }, "definitions": { + "api.ApiReturnedUser": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projects": { + "type": "array", + "items": { + "type": "string" + } + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "username": { + "type": "string" + } + } + }, "api.ApiTag": { "type": "object", "properties": { diff --git a/api/swagger.yaml b/api/swagger.yaml index d19f79b..2ba47a0 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -1,5 +1,22 @@ basePath: /api definitions: + api.ApiReturnedUser: + properties: + email: + type: string + name: + type: string + projects: + items: + type: string + type: array + roles: + items: + type: string + type: array + username: + type: string + type: object api.ApiTag: properties: name: @@ -1007,13 +1024,23 @@ paths: name: id required: true type: string - - description: 'Priority 1: Role to add, one of: [admin, support, manager, user, - api]' + - description: 'Priority 1: Role to add' + enum: + - admin + - support + - manager + - user + - api in: formData name: add-role type: string - - description: 'Priority 2: Role to remove, one of: [admin, support, manager, - user, api]' + - description: 'Priority 2: Role to remove' + enum: + - admin + - support + - manager + - user + - api in: formData name: remove-role type: string @@ -1029,29 +1056,29 @@ paths: - text/plain responses: "200": - description: Task successful + description: Success Response Message schema: type: string "400": description: Bad Request schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "401": description: Unauthorized schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "403": description: Forbidden schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "422": description: 'Unprocessable Entity: The user could not be updated' schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "500": description: Internal Server Error schema: - $ref: '#/definitions/api.ErrorResponse' + type: string security: - ApiKeyAuth: [] summary: Updates an existing user @@ -1068,37 +1095,37 @@ paths: name: username required: true type: string + produces: + - text/plain responses: "200": description: User deleted successfully "400": description: Bad Request schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "401": description: Unauthorized schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "403": description: Forbidden schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "422": description: 'Unprocessable Entity: deleting user failed' schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "500": description: Internal Server Error schema: - $ref: '#/definitions/api.ErrorResponse' + type: string security: - ApiKeyAuth: [] summary: Deletes a user tags: - remove get: - consumes: - - application/json description: |- Returns a JSON-encoded list of users. Required query-parameter defines if all users or only users with additional special roles are returned. @@ -1113,25 +1140,27 @@ paths: - application/json responses: "200": - description: Users returned successfully + description: List of users returned successfully schema: - type: string + items: + $ref: '#/definitions/api.ApiReturnedUser' + type: array "400": description: Bad Request schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "401": description: Unauthorized schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "403": description: Forbidden schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "500": description: Internal Server Error schema: - $ref: '#/definitions/api.ErrorResponse' + type: string security: - ApiKeyAuth: [] summary: Returns a list of users @@ -1152,7 +1181,13 @@ paths: name: password required: true type: string - - description: 'User role, one of: [admin, support, manager, user, api]' + - description: User role + enum: + - admin + - support + - manager + - user + - api in: formData name: role required: true @@ -1173,29 +1208,29 @@ paths: - text/plain responses: "200": - description: User added successfully + description: Success Response schema: type: string "400": description: Bad Request schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "401": description: Unauthorized schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "403": description: Forbidden schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "422": description: 'Unprocessable Entity: creating user failed' schema: - $ref: '#/definitions/api.ErrorResponse' + type: string "500": description: Internal Server Error schema: - $ref: '#/definitions/api.ErrorResponse' + type: string security: - ApiKeyAuth: [] summary: Adds a new user diff --git a/internal/api/docs.go b/internal/api/docs.go index 4f161e6..f3bcf5e 100644 --- a/internal/api/docs.go +++ b/internal/api/docs.go @@ -741,14 +741,28 @@ const docTemplate = `{ "required": true }, { + "enum": [ + "admin", + "support", + "manager", + "user", + "api" + ], "type": "string", - "description": "Priority 1: Role to add, one of: [admin, support, manager, user, api]", + "description": "Priority 1: Role to add", "name": "add-role", "in": "formData" }, { + "enum": [ + "admin", + "support", + "manager", + "user", + "api" + ], "type": "string", - "description": "Priority 2: Role to remove, one of: [admin, support, manager, user, api]", + "description": "Priority 2: Role to remove", "name": "remove-role", "in": "formData" }, @@ -767,7 +781,7 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "Task successful", + "description": "Success Response Message", "schema": { "type": "string" } @@ -775,31 +789,31 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "401": { "description": "Unauthorized", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "422": { "description": "Unprocessable Entity: The user could not be updated", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } } } @@ -813,9 +827,6 @@ const docTemplate = `{ } ], "description": "Returns a JSON-encoded list of users.\nRequired query-parameter defines if all users or only users with additional special roles are returned.", - "consumes": [ - "application/json" - ], "produces": [ "application/json" ], @@ -834,33 +845,36 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "Users returned successfully", + "description": "List of users returned successfully", "schema": { - "type": "string" + "type": "array", + "items": { + "$ref": "#/definitions/api.ApiReturnedUser" + } } }, "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "401": { "description": "Unauthorized", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } } } @@ -898,8 +912,15 @@ const docTemplate = `{ "required": true }, { + "enum": [ + "admin", + "support", + "manager", + "user", + "api" + ], "type": "string", - "description": "User role, one of: [admin, support, manager, user, api]", + "description": "User role", "name": "role", "in": "formData", "required": true @@ -925,7 +946,7 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "User added successfully", + "description": "Success Response", "schema": { "type": "string" } @@ -933,31 +954,31 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "401": { "description": "Unauthorized", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "422": { "description": "Unprocessable Entity: creating user failed", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } } } @@ -972,6 +993,9 @@ const docTemplate = `{ "consumes": [ "multipart/form-data" ], + "produces": [ + "text/plain" + ], "tags": [ "remove" ], @@ -992,31 +1016,31 @@ const docTemplate = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "401": { "description": "Unauthorized", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "403": { "description": "Forbidden", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "422": { "description": "Unprocessable Entity: deleting user failed", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } }, "500": { "description": "Internal Server Error", "schema": { - "$ref": "#/definitions/api.ErrorResponse" + "type": "string" } } } @@ -1024,6 +1048,32 @@ const docTemplate = `{ } }, "definitions": { + "api.ApiReturnedUser": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projects": { + "type": "array", + "items": { + "type": "string" + } + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "username": { + "type": "string" + } + } + }, "api.ApiTag": { "type": "object", "properties": { diff --git a/internal/api/rest.go b/internal/api/rest.go index 67e4dbe..0d4ddb4 100644 --- a/internal/api/rest.go +++ b/internal/api/rest.go @@ -163,6 +163,14 @@ type JobMetricWithName struct { Metric *schema.JobMetric `json:"metric"` } +type ApiReturnedUser struct { + Username string `json:"username"` + Name string `json:"name"` + Roles []string `json:"roles"` + Email string `json:"email"` + Projects []string `json:"projects"` +} + func handleError(err error, statusCode int, rw http.ResponseWriter) { log.Warnf("REST ERROR : %s", err.Error()) rw.Header().Add("Content-Type", "application/json") @@ -953,16 +961,16 @@ func (api *RestApi) getJobMetrics(rw http.ResponseWriter, r *http.Request) { // @produce plain // @param username formData string true "Unique user ID" // @param password formData string true "User password" -// @param role formData string true "User role, one of: [admin, support, manager, user, api]" +// @param role formData string true "User role" Enums(admin, support, manager, user, api) // @param project formData string false "Managed project, required for new manager role user" // @param name formData string false "Users name" // @param email formData string false "Users email" -// @success 200 {string} string "User added successfully" -// @failure 400 {object} api.ErrorResponse "Bad Request" -// @failure 401 {object} api.ErrorResponse "Unauthorized" -// @failure 403 {object} api.ErrorResponse "Forbidden" -// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: creating user failed" -// @failure 500 {object} api.ErrorResponse "Internal Server Error" +// @success 200 {string} string "Success Response" +// @failure 400 {string} string "Bad Request" +// @failure 401 {string} string "Unauthorized" +// @failure 403 {string} string "Forbidden" +// @failure 422 {string} string "Unprocessable Entity: creating user failed" +// @failure 500 {string} string "Internal Server Error" // @security ApiKeyAuth // @router /users/ [post] func (api *RestApi) createUser(rw http.ResponseWriter, r *http.Request) { @@ -1017,13 +1025,14 @@ func (api *RestApi) createUser(rw http.ResponseWriter, r *http.Request) { // @tags remove // @description User defined by username in form data will be deleted from database. // @accept mpfd -// @param username formData string true "User ID to delete" +// @produce plain +// @param username formData string true "User ID to delete" // @success 200 "User deleted successfully" -// @failure 400 {object} api.ErrorResponse "Bad Request" -// @failure 401 {object} api.ErrorResponse "Unauthorized" -// @failure 403 {object} api.ErrorResponse "Forbidden" -// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: deleting user failed" -// @failure 500 {object} api.ErrorResponse "Internal Server Error" +// @failure 400 {string} string "Bad Request" +// @failure 401 {string} string "Unauthorized" +// @failure 403 {string} string "Forbidden" +// @failure 422 {string} string "Unprocessable Entity: deleting user failed" +// @failure 500 {string} string "Internal Server Error" // @security ApiKeyAuth // @router /users/ [delete] func (api *RestApi) deleteUser(rw http.ResponseWriter, r *http.Request) { @@ -1052,14 +1061,13 @@ func (api *RestApi) deleteUser(rw http.ResponseWriter, r *http.Request) { // @tags query // @description Returns a JSON-encoded list of users. // @description Required query-parameter defines if all users or only users with additional special roles are returned. -// @accept json // @produce json // @param not-just-user query bool true "If returned list should contain all users or only users with additional special roles" -// @success 200 {string} json "Users returned successfully" -// @failure 400 {object} api.ErrorResponse "Bad Request" -// @failure 401 {object} api.ErrorResponse "Unauthorized" -// @failure 403 {object} api.ErrorResponse "Forbidden" -// @failure 500 {object} api.ErrorResponse "Internal Server Error" +// @success 200 {array} api.ApiReturnedUser "List of users returned successfully" +// @failure 400 {string} string "Bad Request" +// @failure 401 {string} string "Unauthorized" +// @failure 403 {string} string "Forbidden" +// @failure 500 {string} string "Internal Server Error" // @security ApiKeyAuth // @router /users/ [get] func (api *RestApi) getUsers(rw http.ResponseWriter, r *http.Request) { @@ -1090,17 +1098,17 @@ func (api *RestApi) getUsers(rw http.ResponseWriter, r *http.Request) { // @description If more than one formValue is set then only the highest priority field is used. // @accept mpfd // @produce plain -// @param id path string true "Database ID of User" -// @param add-role formData string false "Priority 1: Role to add, one of: [admin, support, manager, user, api]" -// @param remove-role formData string false "Priority 2: Role to remove, one of: [admin, support, manager, user, api]" -// @param add-project formData string false "Priority 3: Project to add" -// @param remove-project formData string false "Priority 4: Project to remove" -// @success 200 {string} string "Task successful" -// @failure 400 {object} api.ErrorResponse "Bad Request" -// @failure 401 {object} api.ErrorResponse "Unauthorized" -// @failure 403 {object} api.ErrorResponse "Forbidden" -// @failure 422 {object} api.ErrorResponse "Unprocessable Entity: The user could not be updated" -// @failure 500 {object} api.ErrorResponse "Internal Server Error" +// @param id path string true "Database ID of User" +// @param add-role formData string false "Priority 1: Role to add" Enums(admin, support, manager, user, api) +// @param remove-role formData string false "Priority 2: Role to remove" Enums(admin, support, manager, user, api) +// @param add-project formData string false "Priority 3: Project to add" +// @param remove-project formData string false "Priority 4: Project to remove" +// @success 200 {string} string "Success Response Message" +// @failure 400 {string} string "Bad Request" +// @failure 401 {string} string "Unauthorized" +// @failure 403 {string} string "Forbidden" +// @failure 422 {string} string "Unprocessable Entity: The user could not be updated" +// @failure 500 {string} string "Internal Server Error" // @security ApiKeyAuth // @router /user/{id} [post] func (api *RestApi) updateUser(rw http.ResponseWriter, r *http.Request) {