-
Notifications
You must be signed in to change notification settings - Fork 11.2k
feat: add Casbin admin permissions #5755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
a23080c
feat: add casbin admin permissions
Calcium-Ion 2f5565e
feat: improve audit logging to associate logs with actual operators a…
Calcium-Ion a6de6c7
feat: enhance admin permissions and UI interactions for sensitive act…
Calcium-Ion cdff2f7
Refactor authz RBAC and tighten channel permissions
Calcium-Ion a23b7e4
Split channel authz field policy
Calcium-Ion 4689230
Address channel authz review findings
Calcium-Ion File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package controller | ||
|
|
||
| import ( | ||
| "net/http" | ||
|
|
||
| "github.com/QuantumNous/new-api/service/authz" | ||
|
|
||
| "github.com/gin-gonic/gin" | ||
| ) | ||
|
|
||
| // GetPermissionCatalog returns the permission schema used by the client to | ||
| // render the permission editor: the registry of resources with their actions | ||
| // and display label keys, plus the roles with their baseline grant matrices. | ||
| // Defining it in the authz package keeps the schema in a single place. | ||
| func GetPermissionCatalog(c *gin.Context) { | ||
| c.JSON(http.StatusOK, gin.H{ | ||
| "success": true, | ||
| "message": "", | ||
| "data": gin.H{ | ||
| "resources": authz.Catalog(), | ||
| "roles": authz.Roles(), | ||
| }, | ||
| }) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| package controller | ||
|
|
||
| import "github.com/QuantumNous/new-api/model" | ||
|
|
||
| func channelHasSensitiveChanges(channel *PatchChannel, origin *model.Channel, requestData map[string]any) bool { | ||
| if _, ok := requestData["type"]; ok && channel.Type != origin.Type { | ||
| return true | ||
| } | ||
| if _, ok := requestData["key"]; ok && channel.Key != "" && channel.Key != origin.Key { | ||
| return true | ||
| } | ||
| if _, ok := requestData["base_url"]; ok && !equalStringPtr(channel.BaseURL, origin.BaseURL) { | ||
| return true | ||
| } | ||
| if _, ok := requestData["openai_organization"]; ok && !equalStringPtr(channel.OpenAIOrganization, origin.OpenAIOrganization) { | ||
| return true | ||
| } | ||
| if _, ok := requestData["header_override"]; ok && !equalStringPtr(channel.HeaderOverride, origin.HeaderOverride) { | ||
| return true | ||
| } | ||
| if _, ok := requestData["param_override"]; ok && !equalStringPtr(channel.ParamOverride, origin.ParamOverride) { | ||
| return true | ||
| } | ||
| if _, ok := requestData["setting"]; ok && !equalStringPtr(channel.Setting, origin.Setting) { | ||
| return true | ||
| } | ||
| if _, ok := requestData["other"]; ok && channel.Other != origin.Other { | ||
| return true | ||
| } | ||
| if _, ok := requestData["settings"]; ok && channel.OtherSettings != origin.OtherSettings { | ||
| return true | ||
| } | ||
| if _, ok := requestData["key_mode"]; ok && channel.KeyMode != nil { | ||
| return true | ||
| } | ||
| // Fail closed: any field present in the request that is neither a known | ||
| // sensitive field (gated above) nor an explicitly classified non-sensitive | ||
| // field must be treated as sensitive. This keeps a newly added channel field | ||
| // from silently becoming editable by ChannelWrite-only admins until it is | ||
| // consciously classified in channelNonSensitiveFields. | ||
| for field := range requestData { | ||
| if _, ok := channelSensitiveFields[field]; ok { | ||
| continue | ||
| } | ||
| if _, ok := channelNonSensitiveFields[field]; ok { | ||
| continue | ||
| } | ||
| if _, ok := channelOperationalFields[field]; ok { | ||
| continue | ||
| } | ||
| if _, ok := channelReadOnlyFields[field]; ok { | ||
| continue | ||
| } | ||
| return true | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| // channelSensitiveFields lists the channel fields whose modification requires | ||
| // ChannelSensitiveWrite. They are each checked individually in | ||
| // channelHasSensitiveChanges with a precise old-vs-new comparison; this set is | ||
| // used to exclude them from the fail-closed scan for unknown fields. | ||
| var channelSensitiveFields = map[string]struct{}{ | ||
| "type": {}, | ||
| "key": {}, | ||
| "base_url": {}, | ||
| "openai_organization": {}, | ||
| "header_override": {}, | ||
| "param_override": {}, | ||
| "setting": {}, | ||
| "other": {}, | ||
| "settings": {}, | ||
| "key_mode": {}, | ||
| } | ||
|
|
||
| // channelOperationalFields lists fields managed by operation endpoints instead | ||
| // of the general channel edit endpoint. | ||
| var channelOperationalFields = map[string]struct{}{ | ||
| "status": {}, | ||
| } | ||
|
|
||
| // channelReadOnlyFields lists server-managed/accounting fields that the general | ||
| // channel edit endpoint must ignore even if a client sends them. | ||
| var channelReadOnlyFields = map[string]struct{}{ | ||
| "created_time": {}, | ||
| "test_time": {}, | ||
| "response_time": {}, | ||
| "balance": {}, | ||
| "balance_updated_time": {}, | ||
| "used_quota": {}, | ||
| } | ||
|
|
||
| func clearChannelReadOnlyFields(channel *PatchChannel, requestData map[string]any) { | ||
| if _, ok := requestData["created_time"]; ok { | ||
| channel.CreatedTime = 0 | ||
| } | ||
| if _, ok := requestData["test_time"]; ok { | ||
| channel.TestTime = 0 | ||
| } | ||
| if _, ok := requestData["response_time"]; ok { | ||
| channel.ResponseTime = 0 | ||
| } | ||
| if _, ok := requestData["balance"]; ok { | ||
| channel.Balance = 0 | ||
| } | ||
| if _, ok := requestData["balance_updated_time"]; ok { | ||
| channel.BalanceUpdatedTime = 0 | ||
| } | ||
| if _, ok := requestData["used_quota"]; ok { | ||
| channel.UsedQuota = 0 | ||
| } | ||
| } | ||
|
|
||
| // channelNonSensitiveFields lists routing / server-managed channel | ||
| // fields a ChannelWrite admin may edit without ChannelSensitiveWrite. When a new | ||
| // field is added to model.Channel it must be added to either this set or | ||
| // channelSensitiveFields or channelOperationalFields; otherwise it falls through | ||
| // to the fail-closed branch and is treated as sensitive. The | ||
| // TestChannelFieldsAreClassified guard test enforces this. | ||
| var channelNonSensitiveFields = map[string]struct{}{ | ||
| "id": {}, | ||
| "test_model": {}, | ||
| "name": {}, | ||
| "weight": {}, | ||
| "models": {}, | ||
| "group": {}, | ||
| "model_mapping": {}, | ||
| "status_code_mapping": {}, | ||
| "priority": {}, | ||
| "auto_ban": {}, | ||
| "other_info": {}, | ||
| "tag": {}, | ||
| "remark": {}, | ||
| "channel_info": {}, | ||
| "multi_key_mode": {}, | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.