3 Commits

Author SHA1 Message Date
9c6075ebb5 Update README to reflect main branch naming 2026-06-07 08:18:46 +02:00
af7528c8b2 Update CLAUDE.md
Entire-Checkpoint: 306db138cb4c
2026-06-07 08:16:10 +02:00
01fb4d53f1 Fix broken link in README 2026-06-07 08:12:25 +02:00
8 changed files with 131 additions and 232 deletions

View File

@@ -341,6 +341,21 @@ records, archives) at scale. All code changes must prioritize maximum throughput
and minimal latency. Avoid unnecessary allocations, prefer streaming over and minimal latency. Avoid unnecessary allocations, prefer streaming over
buffering, and be mindful of lock contention. When in doubt, benchmark. buffering, and be mindful of lock contention. When in doubt, benchmark.
### Commit Message Convention
Commits must use conventional commit prefixes so goreleaser can generate the
changelog automatically. Only commits with these prefixes appear in releases:
| Prefix | Changelog group |
|---------|------------------------|
| `feat:` | New Features |
| `fix:` | Bug fixes |
| `sec:` | Security updates |
| `docs:` | Documentation updates |
Scoped variants are also recognised, e.g. `feat(api):`, `fix(deps):`.
Commits without one of these prefixes are excluded from the changelog.
### Change Impact Analysis ### Change Impact Analysis
For any significant change, you MUST: For any significant change, you MUST:

View File

@@ -1,10 +1,9 @@
# NOTE # NOTE
While we do our best to keep the master branch in a usable state, there is no guarantee the master branch works. While we do our best to keep the main branch in a usable state, there is no
Please do not use it for production! guarantee the main branch works. Please do not use it for production!
Please have a look at the [Release Please have a look at the [Release Notes](https://github.com/ClusterCockpit/cc-backend/blob/main/ReleaseNotes.md)
Notes](https://github.com/ClusterCockpit/cc-backend/blob/master/ReleaseNotes.md)
for breaking changes! for breaking changes!
# ClusterCockpit REST and GraphQL API backend # ClusterCockpit REST and GraphQL API backend
@@ -41,7 +40,7 @@ For real-time integration with HPC systems, the backend can subscribe to
state updates, providing an alternative to REST API polling. state updates, providing an alternative to REST API polling.
Completed batch jobs are stored in a file-based job archive according to Completed batch jobs are stored in a file-based job archive according to
[this specification](https://github.com/ClusterCockpit/cc-specifications/tree/master/job-archive). [this specification](https://github.com/ClusterCockpit/cc-specifications/tree/main/job-archive).
The backend supports authentication via local accounts, an external LDAP The backend supports authentication via local accounts, an external LDAP
directory, and JWT tokens. Authorization for APIs is implemented with directory, and JWT tokens. Authorization for APIs is implemented with
[JWT](https://jwt.io/) tokens created with public/private key encryption. [JWT](https://jwt.io/) tokens created with public/private key encryption.
@@ -243,73 +242,73 @@ The effective configuration is logged at startup for verification.
## Project file structure ## Project file structure
- [`.github/`](https://github.com/ClusterCockpit/cc-backend/tree/master/.github) - [`.github/`](https://github.com/ClusterCockpit/cc-backend/tree/main/.github)
GitHub Actions workflows and dependabot configuration for CI/CD. GitHub Actions workflows and dependabot configuration for CI/CD.
- [`api/`](https://github.com/ClusterCockpit/cc-backend/tree/master/api) - [`api/`](https://github.com/ClusterCockpit/cc-backend/tree/main/api)
contains the API schema files for the REST and GraphQL APIs. The REST API is contains the API schema files for the REST and GraphQL APIs. The REST API is
documented in the OpenAPI 3.0 format in documented in the OpenAPI 3.0 format in
[./api/swagger.yaml](./api/swagger.yaml). The GraphQL schema is in [./api/swagger.yaml](./api/swagger.yaml). The GraphQL schema is in
[./api/schema.graphqls](./api/schema.graphqls). [./api/schema.graphqls](./api/schema.graphqls).
- [`cmd/cc-backend`](https://github.com/ClusterCockpit/cc-backend/tree/master/cmd/cc-backend) - [`cmd/cc-backend`](https://github.com/ClusterCockpit/cc-backend/tree/main/cmd/cc-backend)
contains the main application entry point and CLI implementation. contains the main application entry point and CLI implementation.
- [`configs/`](https://github.com/ClusterCockpit/cc-backend/tree/master/configs) - [`configs/`](https://github.com/ClusterCockpit/cc-backend/tree/main/configs)
contains documentation about configuration and command line options and required contains documentation about configuration and command line options and required
environment variables. Sample configuration files are provided. environment variables. Sample configuration files are provided.
- [`init/`](https://github.com/ClusterCockpit/cc-backend/tree/master/init) - [`init/`](https://github.com/ClusterCockpit/cc-backend/tree/main/init)
contains an example of setting up systemd for production use. contains an example of setting up systemd for production use.
- [`internal/`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal) - [`internal/`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal)
contains library source code that is not intended for use by others. contains library source code that is not intended for use by others.
- [`api`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/api) - [`api`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/api)
REST API handlers and NATS integration REST API handlers and NATS integration
- [`archiver`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/archiver) - [`archiver`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/archiver)
Job archiving functionality Job archiving functionality
- [`auth`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/auth) - [`auth`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/auth)
Authentication (local, LDAP, OIDC) and JWT token handling Authentication (local, LDAP, OIDC) and JWT token handling
- [`config`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/config) - [`config`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/config)
Configuration management and validation Configuration management and validation
- [`graph`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/graph) - [`graph`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/graph)
GraphQL schema and resolvers GraphQL schema and resolvers
- [`importer`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/importer) - [`importer`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/importer)
Job data import and database initialization Job data import and database initialization
- [`metricdispatch`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/metricdispatch) - [`metricdispatch`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/metricdispatch)
Dispatches metric data loading to appropriate backends Dispatches metric data loading to appropriate backends
- [`repository`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/repository) - [`repository`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/repository)
Database repository layer for jobs and metadata Database repository layer for jobs and metadata
- [`routerConfig`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/routerConfig) - [`routerConfig`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/routerConfig)
HTTP router configuration and middleware HTTP router configuration and middleware
- [`tagger`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/tagger) - [`tagger`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/tagger)
Job classification and application detection Job classification and application detection
- [`taskmanager`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/taskmanager) - [`taskmanager`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/taskmanager)
Background task management and scheduled jobs Background task management and scheduled jobs
- [`metricstoreclient`](https://github.com/ClusterCockpit/cc-backend/tree/master/internal/metricstoreclient) - [`metricstoreclient`](https://github.com/ClusterCockpit/cc-backend/tree/main/internal/metricstoreclient)
Client for cc-metric-store queries Client for cc-metric-store queries
- [`pkg/`](https://github.com/ClusterCockpit/cc-backend/tree/master/pkg) - [`pkg/`](https://github.com/ClusterCockpit/cc-backend/tree/main/pkg)
contains Go packages that can be used by other projects. contains Go packages that can be used by other projects.
- [`archive`](https://github.com/ClusterCockpit/cc-backend/tree/master/pkg/archive) - [`archive`](https://github.com/ClusterCockpit/cc-backend/tree/main/pkg/archive)
Job archive backend implementations (filesystem, S3, SQLite) Job archive backend implementations (filesystem, S3, SQLite)
- [`metricstore`](https://github.com/ClusterCockpit/cc-backend/tree/master/pkg/metricstore) - [`metricstore`](https://github.com/ClusterCockpit/cc-backend/tree/main/pkg/metricstore)
In-memory metric data store with checkpointing and metric loading In-memory metric data store with checkpointing and metric loading
- [`tools/`](https://github.com/ClusterCockpit/cc-backend/tree/master/tools) - [`tools/`](https://github.com/ClusterCockpit/cc-backend/tree/main/tools)
Additional command line helper tools. Additional command line helper tools.
- [`archive-manager`](https://github.com/ClusterCockpit/cc-backend/tree/master/tools/archive-manager) - [`archive-manager`](https://github.com/ClusterCockpit/cc-backend/tree/main/tools/archive-manager)
Commands for getting infos about an existing job archive, importing jobs Commands for getting infos about an existing job archive, importing jobs
between archive backends, and converting archives between JSON and Parquet formats. between archive backends, and converting archives between JSON and Parquet formats.
- [`archive-migration`](https://github.com/ClusterCockpit/cc-backend/tree/master/tools/archive-migration) - [`archive-migration`](https://github.com/ClusterCockpit/cc-backend/tree/main/tools/archive-migration)
Tool for migrating job archives between formats. Tool for migrating job archives between formats.
- [`convert-pem-pubkey`](https://github.com/ClusterCockpit/cc-backend/tree/master/tools/convert-pem-pubkey) - [`convert-pem-pubkey`](https://github.com/ClusterCockpit/cc-backend/tree/main/tools/convert-pem-pubkey)
Tool to convert external pubkey for use in `cc-backend`. Tool to convert external pubkey for use in `cc-backend`.
- [`gen-keypair`](https://github.com/ClusterCockpit/cc-backend/tree/master/tools/gen-keypair) - [`gen-keypair`](https://github.com/ClusterCockpit/cc-backend/tree/main/tools/gen-keypair)
contains a small application to generate a compatible JWT keypair. You find contains a small application to generate a compatible JWT keypair. You find
documentation on how to use it documentation on how to use it
[here](https://github.com/ClusterCockpit/cc-backend/blob/master/docs/JWT-Handling.md). [here](https://github.com/ClusterCockpit/cc-backend/blob/main/docs/JWT-Handling.md).
- [`web/`](https://github.com/ClusterCockpit/cc-backend/tree/master/web) - [`web/`](https://github.com/ClusterCockpit/cc-backend/tree/main/web)
Server-side templates and frontend-related files: Server-side templates and frontend-related files:
- [`frontend`](https://github.com/ClusterCockpit/cc-backend/tree/master/web/frontend) - [`frontend`](https://github.com/ClusterCockpit/cc-backend/tree/main/web/frontend)
Svelte components and static assets for the frontend UI Svelte components and static assets for the frontend UI
- [`templates`](https://github.com/ClusterCockpit/cc-backend/tree/master/web/templates) - [`templates`](https://github.com/ClusterCockpit/cc-backend/tree/main/web/templates)
Server-side Go templates, including monitoring views Server-side Go templates, including monitoring views
- [`gqlgen.yml`](https://github.com/ClusterCockpit/cc-backend/blob/master/gqlgen.yml) - [`gqlgen.yml`](https://github.com/ClusterCockpit/cc-backend/blob/main/gqlgen.yml)
Configures the behaviour and generation of Configures the behaviour and generation of
[gqlgen](https://github.com/99designs/gqlgen). [gqlgen](https://github.com/99designs/gqlgen).
- [`startDemo.sh`](https://github.com/ClusterCockpit/cc-backend/blob/master/startDemo.sh) - [`startDemo.sh`](https://github.com/ClusterCockpit/cc-backend/blob/main/startDemo.sh)
is a shell script that sets up demo data, and builds and starts `cc-backend`. is a shell script that sets up demo data, and builds and starts `cc-backend`.

View File

@@ -1016,11 +1016,8 @@
"/api/user/{id}": { "/api/user/{id}": {
"post": { "post": {
"description": "Allows admins to add/remove roles and projects for a user", "description": "Allows admins to add/remove roles and projects for a user",
"consumes": [
"application/json"
],
"produces": [ "produces": [
"application/json" "text/plain"
], ],
"tags": [ "tags": [
"User" "User"
@@ -1035,26 +1032,35 @@
"required": true "required": true
}, },
{ {
"description": "Single Field Changes", "type": "string",
"name": "request", "description": "Role to add",
"in": "body", "name": "add-role",
"required": true, "in": "formData"
"schema": { },
"$ref": "#/definitions/api.UpdateUserAPIRequest" {
} "type": "string",
"description": "Role to remove",
"name": "remove-role",
"in": "formData"
},
{
"type": "string",
"description": "Project to add",
"name": "add-project",
"in": "formData"
},
{
"type": "string",
"description": "Project to remove",
"name": "remove-project",
"in": "formData"
} }
], ],
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "Success message",
"schema": { "schema": {
"$ref": "#/definitions/api.DefaultAPIResponse" "type": "string"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.ErrorResponse"
} }
}, },
"403": { "403": {
@@ -1928,31 +1934,6 @@
} }
} }
}, },
"api.UpdateUserAPIRequest": {
"type": "object",
"properties": {
"add-role": {
"description": "Role to add to user $ID",
"type": "string",
"example": "user"
},
"remove-role": {
"description": "Role to remove from user $ID",
"type": "string",
"example": "user"
},
"add-project": {
"description": "Project to add to user $ID managed array",
"type": "string",
"example": "abcd100"
},
"remove-project": {
"description": "Project to remove from user $ID managed array",
"type": "string",
"example": "abcd100"
}
}
},
"api.DefaultAPIResponse": { "api.DefaultAPIResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -31,25 +31,6 @@ definitions:
example: Debug example: Debug
type: string type: string
type: object type: object
api.UpdateUserAPIRequest:
properties:
add-project:
description: Project to add to user $ID managed array
example: abcd100
type: string
add-role:
description: Role to add to user $ID
example: user
type: string
remove-project:
description: Project to remove from user $ID managed array
example: abcd100
type: string
remove-role:
description: Role to remove from user $ID
example: user
type: string
type: object
api.DefaultAPIResponse: api.DefaultAPIResponse:
properties: properties:
msg: msg:
@@ -1449,8 +1430,6 @@ paths:
- Nodestates - Nodestates
/api/user/{id}: /api/user/{id}:
post: post:
consumes:
- application/json
description: Allows admins to add/remove roles and projects for a user description: Allows admins to add/remove roles and projects for a user
parameters: parameters:
- description: Username - description: Username
@@ -1458,23 +1437,29 @@ paths:
name: id name: id
required: true required: true
type: string type: string
- description: Single Field Changes - description: Role to add
in: body in: formData
name: request name: add-role
required: true type: string
schema: - description: Role to remove
$ref: '#/definitions/api.UpdateUserAPIRequest' in: formData
name: remove-role
type: string
- description: Project to add
in: formData
name: add-project
type: string
- description: Project to remove
in: formData
name: remove-project
type: string
produces: produces:
- application/json - text/plain
responses: responses:
"200": "200":
description: OK description: Success message
schema: schema:
$ref: '#/definitions/api.DefaultAPIResponse' type: string
"400":
description: Bad Request
schema:
$ref: '#/definitions/api.ErrorResponse'
"403": "403":
description: Forbidden description: Forbidden
schema: schema:

View File

@@ -1023,11 +1023,8 @@ const docTemplate = `{
"/api/user/{id}": { "/api/user/{id}": {
"post": { "post": {
"description": "Allows admins to add/remove roles and projects for a user", "description": "Allows admins to add/remove roles and projects for a user",
"consumes": [
"application/json"
],
"produces": [ "produces": [
"application/json" "text/plain"
], ],
"tags": [ "tags": [
"User" "User"
@@ -1042,26 +1039,35 @@ const docTemplate = `{
"required": true "required": true
}, },
{ {
"description": "Single Field Changes", "type": "string",
"name": "request", "description": "Role to add",
"in": "body", "name": "add-role",
"required": true, "in": "formData"
"schema": { },
"$ref": "#/definitions/api.UpdateUserAPIRequest" {
} "type": "string",
"description": "Role to remove",
"name": "remove-role",
"in": "formData"
},
{
"type": "string",
"description": "Project to add",
"name": "add-project",
"in": "formData"
},
{
"type": "string",
"description": "Project to remove",
"name": "remove-project",
"in": "formData"
} }
], ],
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "Success message",
"schema": { "schema": {
"$ref": "#/definitions/api.DefaultAPIResponse" "type": "string"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.ErrorResponse"
} }
}, },
"403": { "403": {
@@ -1935,31 +1941,6 @@ const docTemplate = `{
} }
} }
}, },
"api.UpdateUserAPIRequest": {
"type": "object",
"properties": {
"add-role": {
"description": "Role to add to user $ID",
"type": "string",
"example": "user"
},
"remove-role": {
"description": "Role to remove from user $ID",
"type": "string",
"example": "user"
},
"add-project": {
"description": "Project to add to user $ID managed array",
"type": "string",
"example": "abcd100"
},
"remove-project": {
"description": "Project to remove from user $ID managed array",
"type": "string",
"example": "abcd100"
}
}
},
"api.DefaultAPIResponse": { "api.DefaultAPIResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -79,8 +79,6 @@ func (api *RestAPI) MountAPIRoutes(r chi.Router) {
// REST API Uses TokenAuth // REST API Uses TokenAuth
// User List // User List
r.Get("/users/", api.getUsers) r.Get("/users/", api.getUsers)
// User Edit
r.Post("/user/{id}", api.updateUserByRequest)
// Cluster List // Cluster List
r.Get("/clusters/", api.getClusters) r.Get("/clusters/", api.getClusters)
// Slurm node state // Slurm node state
@@ -154,7 +152,7 @@ func (api *RestAPI) MountConfigAPIRoutes(r chi.Router) {
r.Put("/config/users/", api.createUser) r.Put("/config/users/", api.createUser)
r.Get("/config/users/", api.getUsers) r.Get("/config/users/", api.getUsers)
r.Delete("/config/users/", api.deleteUser) r.Delete("/config/users/", api.deleteUser)
r.Post("/config/user/{id}", api.updateUserByForm) r.Post("/config/user/{id}", api.updateUser)
r.Post("/config/notice/", api.editNotice) r.Post("/config/notice/", api.editNotice)
r.Get("/config/taggers/", api.getTaggers) r.Get("/config/taggers/", api.getTaggers)
r.Post("/config/taggers/run/", api.runTagger) r.Post("/config/taggers/run/", api.runTagger)

View File

@@ -24,14 +24,6 @@ type APIReturnedUser struct {
Projects []string `json:"projects"` Projects []string `json:"projects"`
} }
// UpdateUserAPIRequest model
type UpdateUserAPIRequest struct {
NewRole string `json:"add-role" example:"user"` // Role to add to user $ID
DelRole string `json:"remove-role" example:"user"` // Role to remove from user $ID
NewProj string `json:"add-project" example:"abcd100"` // Project to add to user $ID managed array
DelProj string `json:"remove-project" example:"abcd100"` // Project to remove from user $ID managed array
}
// getUsers godoc // getUsers godoc
// @summary Returns a list of users // @summary Returns a list of users
// @tags User // @tags User
@@ -66,74 +58,22 @@ func (api *RestAPI) getUsers(rw http.ResponseWriter, r *http.Request) {
} }
} }
// updateUserByRequest godoc // updateUser godoc
// @summary Update user roles and projects // @summary Update user roles and projects
// @tags User // @tags User
// @description Allows admins to add/remove roles and projects for a user // @description Allows admins to add/remove roles and projects for a user
// @accept json // @produce plain
// @produce json // @param id path string true "Username"
// @param id path string true "Username" // @param add-role formData string false "Role to add"
// @param request body api.UpdateUserAPIRequest true "Single Field Changes" // @param remove-role formData string false "Role to remove"
// @param add-project formData string false "Project to add"
// @param remove-project formData string false "Project to remove"
// @success 200 {string} string "Success message" // @success 200 {string} string "Success message"
// @failure 403 {object} api.ErrorResponse "Forbidden" // @failure 403 {object} api.ErrorResponse "Forbidden"
// @failure 422 {object} api.ErrorResponse "Unprocessable Entity" // @failure 422 {object} api.ErrorResponse "Unprocessable Entity"
// @security ApiKeyAuth // @security ApiKeyAuth
// @router /api/user/{id} [post] // @router /api/user/{id} [post]
func (api *RestAPI) updateUserByRequest(rw http.ResponseWriter, r *http.Request) { func (api *RestAPI) updateUser(rw http.ResponseWriter, r *http.Request) {
if user := repository.GetUserFromContext(r.Context()); !user.HasRole(schema.RoleAdmin) {
handleError(fmt.Errorf("only admins are allowed to update a user"), http.StatusForbidden, rw)
return
}
// Get Values
var req UpdateUserAPIRequest
if err := decode(r.Body, &req); err != nil {
handleError(fmt.Errorf("decoding request failed: %w", err), http.StatusBadRequest, rw)
return
}
rw.Header().Set("Content-Type", "application/json")
// Handle role updates
if req.NewRole != "" {
if err := repository.GetUserRepository().AddRole(r.Context(), chi.URLParam(r, "id"), req.NewRole); err != nil {
handleError(fmt.Errorf("adding role failed: %w", err), http.StatusUnprocessableEntity, rw)
return
}
if err := json.NewEncoder(rw).Encode(DefaultAPIResponse{Message: fmt.Sprintf("Add Role Success for user %s", chi.URLParam(r, "id"))}); err != nil {
cclog.Errorf("Failed to encode response: %v", err)
}
} else if req.DelRole != "" {
if err := repository.GetUserRepository().RemoveRole(r.Context(), chi.URLParam(r, "id"), req.DelRole); err != nil {
handleError(fmt.Errorf("removing role failed: %w", err), http.StatusUnprocessableEntity, rw)
return
}
if err := json.NewEncoder(rw).Encode(DefaultAPIResponse{Message: fmt.Sprintf("Remove Role Success for user %s", chi.URLParam(r, "id"))}); err != nil {
cclog.Errorf("Failed to encode response: %v", err)
}
} else if req.NewProj != "" {
if err := repository.GetUserRepository().AddProject(r.Context(), chi.URLParam(r, "id"), req.NewProj); err != nil {
handleError(fmt.Errorf("adding project failed: %w", err), http.StatusUnprocessableEntity, rw)
return
}
if err := json.NewEncoder(rw).Encode(DefaultAPIResponse{Message: fmt.Sprintf("Add Project Success for user %s", chi.URLParam(r, "id"))}); err != nil {
cclog.Errorf("Failed to encode response: %v", err)
}
} else if req.DelProj != "" {
if err := repository.GetUserRepository().RemoveProject(r.Context(), chi.URLParam(r, "id"), req.DelProj); err != nil {
handleError(fmt.Errorf("removing project failed: %w", err), http.StatusUnprocessableEntity, rw)
return
}
if err := json.NewEncoder(rw).Encode(DefaultAPIResponse{Message: fmt.Sprintf("Remove Project Success for user %s", chi.URLParam(r, "id"))}); err != nil {
cclog.Errorf("Failed to encode response: %v", err)
}
} else {
handleError(fmt.Errorf("no operation specified: must provide add-role, remove-role, add-project, or remove-project"), http.StatusBadRequest, rw)
}
}
func (api *RestAPI) updateUserByForm(rw http.ResponseWriter, r *http.Request) {
// SecuredCheck() only worked with TokenAuth: Removed // SecuredCheck() only worked with TokenAuth: Removed
if user := repository.GetUserFromContext(r.Context()); !user.HasRole(schema.RoleAdmin) { if user := repository.GetUserFromContext(r.Context()); !user.HasRole(schema.RoleAdmin) {

View File

@@ -650,7 +650,7 @@ func securedCheck(user *schema.User, r *http.Request) error {
} }
// If SplitHostPort fails, IPAddress is already just a host (no port) // If SplitHostPort fails, IPAddress is already just a host (no port)
// If nothing declared in config: Continue // FIXME: Allow All If Not Declared? // If nothing declared in config: Continue
if len(config.Keys.APIAllowedIPs) == 0 { if len(config.Keys.APIAllowedIPs) == 0 {
return nil return nil
} }