From e5a189e0f416ae757bef44ffdeaabdc66aebe4a1 Mon Sep 17 00:00:00 2001 From: Yaodong Yu <2814461814@qq.com> Date: Sat, 19 Aug 2023 12:23:15 +0800 Subject: [PATCH] fix: remove isGlobalAdmin field in user (#2235) * refactor: remove isGlobalAdmin field in user * fix: upload xlsx * fix: remove field in account table --- controllers/account.go | 1 - controllers/auth.go | 3 +- controllers/base.go | 2 +- controllers/link.go | 4 +- init_data.json.template | 1 - ldap/server.go | 2 +- object/check.go | 4 +- object/check_util.go | 4 +- object/init.go | 2 - object/mfa.go | 4 +- object/oidc_discovery.go | 2 +- object/syncer_util.go | 3 - object/token.go | 1 - object/token_jwt.go | 2 - object/user.go | 13 +- object/user_upload.go | 13 +- object/user_util.go | 7 +- swagger/swagger.json | 517 +++++------------------- swagger/swagger.yml | 353 ++++------------ web/src/OrganizationListPage.js | 1 - web/src/Setting.js | 2 +- web/src/UserEditPage.js | 13 - web/src/UserListPage.js | 13 - web/src/WebhookEditPage.js | 1 - web/src/common/OAuthWidget.js | 2 +- web/src/table/AccountTable.js | 3 +- web/src/table/SyncerTableColumnTable.js | 2 +- xlsx/user_test.xlsx | Bin 10977 -> 10948 bytes 28 files changed, 212 insertions(+), 763 deletions(-) diff --git a/controllers/account.go b/controllers/account.go index 21adfde9..cf41e429 100644 --- a/controllers/account.go +++ b/controllers/account.go @@ -171,7 +171,6 @@ func (c *ApiController) Signup() { Region: authForm.Region, Score: initScore, IsAdmin: false, - IsGlobalAdmin: false, IsForbidden: false, IsDeleted: false, SignupApplication: application.Name, diff --git a/controllers/auth.go b/controllers/auth.go index 5897f140..dfa5275b 100644 --- a/controllers/auth.go +++ b/controllers/auth.go @@ -70,7 +70,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob } // check user's tag - if !user.IsGlobalAdmin && !user.IsAdmin && len(application.Tags) > 0 { + if !user.IsGlobalAdmin() && !user.IsAdmin && len(application.Tags) > 0 { // only users with the tag that is listed in the application tags can login if !util.InSlice(application.Tags, user.Tag) { c.ResponseError(fmt.Sprintf(c.T("auth:User's tag: %s is not listed in the application's tags"), user.Tag)) @@ -589,7 +589,6 @@ func (c *ApiController) Login() { Region: userInfo.CountryCode, Score: initScore, IsAdmin: false, - IsGlobalAdmin: false, IsForbidden: false, IsDeleted: false, SignupApplication: application.Name, diff --git a/controllers/base.go b/controllers/base.go index cd1e3082..25e79639 100644 --- a/controllers/base.go +++ b/controllers/base.go @@ -79,7 +79,7 @@ func (c *ApiController) isGlobalAdmin() (bool, *object.User) { return false, nil } - return user.Owner == "built-in" || user.IsGlobalAdmin, user + return user.IsGlobalAdmin(), user } func (c *ApiController) getCurrentUser() *object.User { diff --git a/controllers/link.go b/controllers/link.go index 1fe96b4c..aa4c81ae 100644 --- a/controllers/link.go +++ b/controllers/link.go @@ -45,13 +45,13 @@ func (c *ApiController) Unlink() { // the user will be unlinked from the provider unlinkedUser := form.User - if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin { + if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin() { // if the user is not the same as the one we are unlinking, we need to make sure the user is the global admin. c.ResponseError(c.T("link:You are not the global admin, you can't unlink other users")) return } - if user.Id == unlinkedUser.Id && !user.IsGlobalAdmin { + if user.Id == unlinkedUser.Id && !user.IsGlobalAdmin() { // if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error. application, err := object.GetApplicationByUser(user) if err != nil { diff --git a/init_data.json.template b/init_data.json.template index e33cee8e..6330a074 100644 --- a/init_data.json.template +++ b/init_data.json.template @@ -123,7 +123,6 @@ "score": 2000, "ranking": 1, "isAdmin": true, - "isGlobalAdmin": true, "isForbidden": false, "isDeleted": false, "signupApplication": "", diff --git a/ldap/server.go b/ldap/server.go index 4ac99c5a..bee379a1 100644 --- a/ldap/server.go +++ b/ldap/server.go @@ -62,7 +62,7 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) { return } - if bindOrg == "built-in" || bindUser.IsGlobalAdmin { + if bindOrg == "built-in" || bindUser.IsGlobalAdmin() { m.Client.IsGlobalAdmin, m.Client.IsOrgAdmin = true, true } else if bindUser.IsAdmin { m.Client.IsOrgAdmin = true diff --git a/object/check.go b/object/check.go index 365071f9..22e42abf 100644 --- a/object/check.go +++ b/object/check.go @@ -141,7 +141,7 @@ func checkSigninErrorTimes(user *User, lang string) string { // reset the error times user.SigninWrongTimes = 0 - UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, user.IsGlobalAdmin) + UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, false) } return "" @@ -319,7 +319,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool, lang string) if requestUser == nil { return false, fmt.Errorf(i18n.Translate(lang, "check:Session outdated, please login again")) } - if requestUser.IsGlobalAdmin { + if requestUser.IsGlobalAdmin() { hasPermission = true } else if requestUserId == userId { hasPermission = true diff --git a/object/check_util.go b/object/check_util.go index 85d7935e..822cfd0d 100644 --- a/object/check_util.go +++ b/object/check_util.go @@ -42,7 +42,7 @@ func resetUserSigninErrorTimes(user *User) { return } user.SigninWrongTimes = 0 - UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin) + UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false) } func recordSigninErrorInfo(user *User, lang string, options ...bool) string { @@ -61,7 +61,7 @@ func recordSigninErrorInfo(user *User, lang string, options ...bool) string { } // update user - UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin) + UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false) leftChances := SigninWrongTimesLimit - user.SigninWrongTimes if leftChances == 0 && enableCaptcha { return fmt.Sprint(i18n.Translate(lang, "check:password or code is incorrect")) diff --git a/object/init.go b/object/init.go index 63c353b4..fd90fca6 100644 --- a/object/init.go +++ b/object/init.go @@ -73,7 +73,6 @@ func getBuiltInAccountItems() []*AccountItem { {Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"}, {Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, - {Name: "Is global admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"}, @@ -145,7 +144,6 @@ func initBuiltInUser() { Score: 2000, Ranking: 1, IsAdmin: true, - IsGlobalAdmin: true, IsForbidden: false, IsDeleted: false, SignupApplication: "app-built-in", diff --git a/object/mfa.go b/object/mfa.go index 3e17e16c..acaaad7a 100644 --- a/object/mfa.go +++ b/object/mfa.go @@ -84,7 +84,7 @@ func MfaRecover(user *User, recoveryCode string) error { return fmt.Errorf("recovery code not found") } - _, err := UpdateUser(user.GetId(), user, []string{"recovery_codes"}, user.IsAdminUser()) + _, err := UpdateUser(user.GetId(), user, []string{"recovery_codes"}, false) if err != nil { return err } @@ -181,7 +181,7 @@ func DisabledMultiFactorAuth(user *User) error { func SetPreferredMultiFactorAuth(user *User, mfaType string) error { user.PreferredMfaType = mfaType - _, err := UpdateUser(user.GetId(), user, []string{"preferred_mfa_type"}, user.IsAdminUser()) + _, err := UpdateUser(user.GetId(), user, []string{"preferred_mfa_type"}, false) if err != nil { return err } diff --git a/object/oidc_discovery.go b/object/oidc_discovery.go index 3d10e293..e1da6a8f 100644 --- a/object/oidc_discovery.go +++ b/object/oidc_discovery.go @@ -103,7 +103,7 @@ func GetOidcDiscovery(host string) OidcDiscovery { SubjectTypesSupported: []string{"public"}, IdTokenSigningAlgValuesSupported: []string{"RS256"}, ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"}, - ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isGlobalAdmin", "isForbidden", "signupApplication", "ldap"}, + ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isForbidden", "signupApplication", "ldap"}, RequestParameterSupported: true, RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"}, EndSessionEndpoint: fmt.Sprintf("%s/api/logout", originBackend), diff --git a/object/syncer_util.go b/object/syncer_util.go index e6c0c4b3..f872cea4 100644 --- a/object/syncer_util.go +++ b/object/syncer_util.go @@ -154,8 +154,6 @@ func (syncer *Syncer) setUserByKeyValue(user *User, key string, value string) { user.IsOnline = util.ParseBool(value) case "IsAdmin": user.IsAdmin = util.ParseBool(value) - case "IsGlobalAdmin": - user.IsGlobalAdmin = util.ParseBool(value) case "IsForbidden": user.IsForbidden = util.ParseBool(value) case "IsDeleted": @@ -289,7 +287,6 @@ func (syncer *Syncer) getMapFromOriginalUser(user *OriginalUser) map[string]stri m["IsDefaultAvatar"] = util.BoolToString(user.IsDefaultAvatar) m["IsOnline"] = util.BoolToString(user.IsOnline) m["IsAdmin"] = util.BoolToString(user.IsAdmin) - m["IsGlobalAdmin"] = util.BoolToString(user.IsGlobalAdmin) m["IsForbidden"] = util.BoolToString(user.IsForbidden) m["IsDeleted"] = util.BoolToString(user.IsDeleted) m["CreatedIp"] = user.CreatedIp diff --git a/object/token.go b/object/token.go index fa8be8e3..3a81c765 100644 --- a/object/token.go +++ b/object/token.go @@ -824,7 +824,6 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin Type: "normal-user", CreatedTime: util.GetCurrentTime(), IsAdmin: false, - IsGlobalAdmin: false, IsForbidden: false, IsDeleted: false, Properties: map[string]string{ diff --git a/object/token_jwt.go b/object/token_jwt.go index d0063fbb..4a5785a2 100644 --- a/object/token_jwt.go +++ b/object/token_jwt.go @@ -73,7 +73,6 @@ type UserWithoutThirdIdp struct { IsDefaultAvatar bool `json:"isDefaultAvatar"` IsOnline bool `json:"isOnline"` IsAdmin bool `json:"isAdmin"` - IsGlobalAdmin bool `json:"isGlobalAdmin"` IsForbidden bool `json:"isForbidden"` IsDeleted bool `json:"isDeleted"` SignupApplication string `xorm:"varchar(100)" json:"signupApplication"` @@ -154,7 +153,6 @@ func getUserWithoutThirdIdp(user *User) *UserWithoutThirdIdp { IsDefaultAvatar: user.IsDefaultAvatar, IsOnline: user.IsOnline, IsAdmin: user.IsAdmin, - IsGlobalAdmin: user.IsGlobalAdmin, IsForbidden: user.IsForbidden, IsDeleted: user.IsDeleted, SignupApplication: user.SignupApplication, diff --git a/object/user.go b/object/user.go index 3d7e404a..6ebda178 100644 --- a/object/user.go +++ b/object/user.go @@ -83,7 +83,6 @@ type User struct { IsDefaultAvatar bool `json:"isDefaultAvatar"` IsOnline bool `json:"isOnline"` IsAdmin bool `json:"isAdmin"` - IsGlobalAdmin bool `json:"isGlobalAdmin"` IsForbidden bool `json:"isForbidden"` IsDeleted bool `json:"isDeleted"` SignupApplication string `xorm:"varchar(100)" json:"signupApplication"` @@ -530,7 +529,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er columns = []string{ "owner", "display_name", "avatar", "location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application", - "is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", + "is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret", "github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs", "baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon", @@ -891,5 +890,13 @@ func (user *User) IsApplicationAdmin(application *Application) bool { return false } - return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin + return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin() +} + +func (user *User) IsGlobalAdmin() bool { + if user == nil { + return false + } + + return user.Owner == "built-in" } diff --git a/object/user_upload.go b/object/user_upload.go index 2e8537cc..8bdd71bd 100644 --- a/object/user_upload.go +++ b/object/user_upload.go @@ -124,15 +124,14 @@ func UploadUsers(owner string, fileId string) (bool, error) { IsDefaultAvatar: false, IsOnline: parseLineItemBool(&line, 31), IsAdmin: parseLineItemBool(&line, 32), - IsGlobalAdmin: parseLineItemBool(&line, 33), - IsForbidden: parseLineItemBool(&line, 34), - IsDeleted: parseLineItemBool(&line, 35), - SignupApplication: parseLineItem(&line, 36), + IsForbidden: parseLineItemBool(&line, 33), + IsDeleted: parseLineItemBool(&line, 34), + SignupApplication: parseLineItem(&line, 35), Hash: "", PreHash: "", - CreatedIp: parseLineItem(&line, 37), - LastSigninTime: parseLineItem(&line, 38), - LastSigninIp: parseLineItem(&line, 39), + CreatedIp: parseLineItem(&line, 36), + LastSigninTime: parseLineItem(&line, 37), + LastSigninIp: parseLineItem(&line, 38), Ldap: "", Properties: map[string]string{}, } diff --git a/object/user_util.go b/object/user_util.go index 2122f5b1..44185c7c 100644 --- a/object/user_util.go +++ b/object/user_util.go @@ -310,10 +310,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str item := GetAccountItemByName("Is admin", organization) itemsChanged = append(itemsChanged, item) } - if oldUser.IsGlobalAdmin != newUser.IsGlobalAdmin { - item := GetAccountItemByName("Is global admin", organization) - itemsChanged = append(itemsChanged, item) - } + if oldUser.IsForbidden != newUser.IsForbidden { item := GetAccountItemByName("Is forbidden", organization) itemsChanged = append(itemsChanged, item) @@ -351,5 +348,5 @@ func (user *User) IsAdminUser() bool { return false } - return user.IsAdmin || user.IsGlobalAdmin + return user.IsAdmin || user.IsGlobalAdmin() } diff --git a/swagger/swagger.json b/swagger/swagger.json index c9e61e14..d87081aa 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -131,34 +131,6 @@ } } }, - "/api/add-chat": { - "post": { - "tags": [ - "Chat API" - ], - "description": "add chat", - "operationId": "ApiController.AddChat", - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The details of the chat", - "required": true, - "schema": { - "$ref": "#/definitions/object.Chat" - } - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "$ref": "#/definitions/controllers.Response" - } - } - } - } - }, "/api/add-enforcer": { "post": { "tags": [ @@ -243,34 +215,6 @@ } } }, - "/api/add-message": { - "post": { - "tags": [ - "Message API" - ], - "description": "add message", - "operationId": "ApiController.AddMessage", - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The details of the message", - "required": true, - "schema": { - "$ref": "#/definitions/object.Message" - } - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "$ref": "#/definitions/controllers.Response" - } - } - } - } - }, "/api/add-model": { "post": { "tags": [ @@ -1077,34 +1021,6 @@ } } }, - "/api/delete-chat": { - "post": { - "tags": [ - "Chat API" - ], - "description": "delete chat", - "operationId": "ApiController.DeleteChat", - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The details of the chat", - "required": true, - "schema": { - "$ref": "#/definitions/object.Chat" - } - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "$ref": "#/definitions/controllers.Response" - } - } - } - } - }, "/api/delete-enforcer": { "post": { "tags": [ @@ -1189,34 +1105,6 @@ } } }, - "/api/delete-message": { - "post": { - "tags": [ - "Message API" - ], - "description": "delete message", - "operationId": "ApiController.DeleteMessage", - "parameters": [ - { - "in": "body", - "name": "body", - "description": "The details of the message", - "required": true, - "schema": { - "$ref": "#/definitions/object.Message" - } - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "$ref": "#/definitions/controllers.Response" - } - } - } - } - }, "/api/delete-mfa/": { "post": { "tags": [ @@ -1964,56 +1852,18 @@ } } }, - "/api/get-chat": { + "/api/get-dashboard": { "get": { "tags": [ - "Chat API" - ], - "description": "get chat", - "operationId": "ApiController.GetChat", - "parameters": [ - { - "in": "query", - "name": "id", - "description": "The id ( owner/name ) of the chat", - "required": true, - "type": "string" - } + "GetDashboard API" ], + "description": "get information of dashboard", + "operationId": "ApiController.GetDashboard", "responses": { "200": { "description": "The Response object", "schema": { - "$ref": "#/definitions/object.Chat" - } - } - } - } - }, - "/api/get-chats": { - "get": { - "tags": [ - "Chat API" - ], - "description": "get chats", - "operationId": "ApiController.GetChats", - "parameters": [ - { - "in": "query", - "name": "owner", - "description": "The owner of chats", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/object.Chat" - } + "$ref": "#/definitions/controllers.Response" } } } @@ -2319,87 +2169,6 @@ } } }, - "/api/get-message": { - "get": { - "tags": [ - "Message API" - ], - "description": "get message", - "operationId": "ApiController.GetMessage", - "parameters": [ - { - "in": "query", - "name": "id", - "description": "The id ( owner/name ) of the message", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "$ref": "#/definitions/object.Message" - } - } - } - } - }, - "/api/get-message-answer": { - "get": { - "tags": [ - "Message API" - ], - "description": "get message answer", - "operationId": "ApiController.GetMessageAnswer", - "parameters": [ - { - "in": "query", - "name": "id", - "description": "The id ( owner/name ) of the message", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "$ref": "#/definitions/object.Message" - } - } - } - } - }, - "/api/get-messages": { - "get": { - "tags": [ - "Message API" - ], - "description": "get messages", - "operationId": "ApiController.GetMessages", - "parameters": [ - { - "in": "query", - "name": "owner", - "description": "The owner of messages", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/object.Message" - } - } - } - } - } - }, "/api/get-model": { "get": { "tags": [ @@ -4481,41 +4250,6 @@ } } }, - "/api/update-chat": { - "post": { - "tags": [ - "Chat API" - ], - "description": "update chat", - "operationId": "ApiController.UpdateChat", - "parameters": [ - { - "in": "query", - "name": "id", - "description": "The id ( owner/name ) of the chat", - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "body", - "description": "The details of the chat", - "required": true, - "schema": { - "$ref": "#/definitions/object.Chat" - } - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "$ref": "#/definitions/controllers.Response" - } - } - } - } - }, "/api/update-enforcer": { "post": { "tags": [ @@ -4614,41 +4348,6 @@ } } }, - "/api/update-message": { - "post": { - "tags": [ - "Message API" - ], - "description": "update message", - "operationId": "ApiController.UpdateMessage", - "parameters": [ - { - "in": "query", - "name": "id", - "description": "The id ( owner/name ) of the message", - "required": true, - "type": "string" - }, - { - "in": "body", - "name": "body", - "description": "The details of the message", - "required": true, - "schema": { - "$ref": "#/definitions/object.Message" - } - } - ], - "responses": { - "200": { - "description": "The Response object", - "schema": { - "$ref": "#/definitions/controllers.Response" - } - } - } - } - }, "/api/update-model": { "post": { "tags": [ @@ -5431,6 +5130,14 @@ } }, "definitions": { + "1183.0xc000639290.false": { + "title": "false", + "type": "object" + }, + "1217.0xc0006392c0.false": { + "title": "false", + "type": "object" + }, "LaravelResponse": { "title": "LaravelResponse", "type": "object" @@ -5484,16 +5191,10 @@ "type": "object", "properties": { "data": { - "additionalProperties": { - "description": "support string, struct or []struct", - "type": "string" - } + "$ref": "#/definitions/1183.0xc000639290.false" }, "data2": { - "additionalProperties": { - "description": "support string, struct or []struct", - "type": "string" - } + "$ref": "#/definitions/1217.0xc0006392c0.false" }, "msg": { "type": "string" @@ -5531,10 +5232,18 @@ "title": "JSONWebKey", "type": "object" }, + "model.Model": { + "title": "Model", + "type": "object" + }, "object": { "title": "object", "type": "object" }, + "object.\u0026{197049 0xc000a2cd50 false}": { + "title": "\u0026{197049 0xc000a2cd50 false}", + "type": "object" + }, "object.AccountItem": { "title": "AccountItem", "type": "object", @@ -5557,16 +5266,41 @@ "title": "Adapter", "type": "object", "properties": { - "Engine": { - "$ref": "#/definitions/xorm.Engine" - }, - "dataSourceName": { + "createdTime": { "type": "string" }, - "dbName": { + "database": { "type": "string" }, - "driverName": { + "databaseType": { + "type": "string" + }, + "host": { + "type": "string" + }, + "name": { + "type": "string" + }, + "owner": { + "type": "string" + }, + "password": { + "type": "string" + }, + "port": { + "type": "integer", + "format": "int64" + }, + "table": { + "type": "string" + }, + "tableNamePrefix": { + "type": "string" + }, + "type": { + "type": "string" + }, + "user": { "type": "string" } } @@ -5728,7 +5462,7 @@ "title": "CasbinRequest", "type": "array", "items": { - "$ref": "#/definitions/object.CasbinRequest" + "$ref": "#/definitions/object.\u0026{197049 0xc000a2cd50 false}" } }, "object.Cert": { @@ -5778,52 +5512,6 @@ } } }, - "object.Chat": { - "title": "Chat", - "type": "object", - "properties": { - "category": { - "type": "string" - }, - "createdTime": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "messageCount": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - }, - "organization": { - "type": "string" - }, - "owner": { - "type": "string" - }, - "type": { - "type": "string" - }, - "updatedTime": { - "type": "string" - }, - "user1": { - "type": "string" - }, - "user2": { - "type": "string" - }, - "users": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, "object.Enforce": { "title": "Enforce", "type": "object" @@ -5844,12 +5532,14 @@ "displayName": { "type": "string" }, - "isEnabled": { - "type": "boolean" - }, "model": { "type": "string" }, + "modelCfg": { + "additionalProperties": { + "type": "string" + } + }, "name": { "type": "string" }, @@ -6084,36 +5774,6 @@ } } }, - "object.Message": { - "title": "Message", - "type": "object", - "properties": { - "author": { - "type": "string" - }, - "chat": { - "type": "string" - }, - "createdTime": { - "type": "string" - }, - "name": { - "type": "string" - }, - "organization": { - "type": "string" - }, - "owner": { - "type": "string" - }, - "replyTo": { - "type": "string" - }, - "text": { - "type": "string" - } - } - }, "object.MfaItem": { "title": "MfaItem", "type": "object", @@ -6169,9 +5829,6 @@ "displayName": { "type": "string" }, - "isEnabled": { - "type": "boolean" - }, "modelText": { "type": "string" }, @@ -6349,6 +6006,24 @@ } } }, + "object.Ormer": { + "title": "Ormer", + "type": "object", + "properties": { + "Engine": { + "$ref": "#/definitions/xorm.Engine" + }, + "dataSourceName": { + "type": "string" + }, + "dbName": { + "type": "string" + }, + "driverName": { + "type": "string" + } + } + }, "object.Payment": { "title": "Payment", "type": "object", @@ -6386,7 +6061,7 @@ "name": { "type": "string" }, - "organization": { + "outOrderId": { "type": "string" }, "owner": { @@ -6424,7 +6099,7 @@ "type": "string" }, "state": { - "type": "string" + "$ref": "#/definitions/pp.PaymentState" }, "tag": { "type": "string" @@ -7124,9 +6799,6 @@ "$ref": "#/definitions/object.TableColumn" } }, - "tablePrimaryKey": { - "type": "string" - }, "type": { "type": "string" }, @@ -7145,6 +6817,9 @@ "isHashed": { "type": "boolean" }, + "isKey": { + "type": "boolean" + }, "name": { "type": "string" }, @@ -7464,9 +7139,6 @@ "isForbidden": { "type": "boolean" }, - "isGlobalAdmin": { - "type": "boolean" - }, "isOnline": { "type": "boolean" }, @@ -7692,6 +7364,9 @@ "vk": { "type": "string" }, + "web3onboard": { + "type": "string" + }, "webauthnCredentials": { "type": "array", "items": { @@ -7811,6 +7486,18 @@ } } }, + "pp.PaymentState": { + "title": "PaymentState", + "type": "string", + "enum": [ + "PaymentStatePaid = \"Paid\"", + "PaymentStateCreated = \"Created\"", + "PaymentStateCanceled = \"Canceled\"", + "PaymentStateTimeout = \"Timeout\"", + "PaymentStateError = \"Error\"" + ], + "example": "Paid" + }, "protocol.CredentialAssertion": { "title": "CredentialAssertion", "type": "object" @@ -7871,6 +7558,10 @@ "xorm.Engine": { "title": "Engine", "type": "object" + }, + "xormadapter.Adapter": { + "title": "Adapter", + "type": "object" } }, "securityDefinitions": { @@ -7879,9 +7570,5 @@ "name": "Authorization", "in": "header" } - }, - "externalDocs": { - "description": "Find out more about Casdoor", - "url": "https://casdoor.org/" } } \ No newline at end of file diff --git a/swagger/swagger.yml b/swagger/swagger.yml index 21ee9a40..4a22816a 100644 --- a/swagger/swagger.yml +++ b/swagger/swagger.yml @@ -85,24 +85,6 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' - /api/add-chat: - post: - tags: - - Chat API - description: add chat - operationId: ApiController.AddChat - parameters: - - in: body - name: body - description: The details of the chat - required: true - schema: - $ref: '#/definitions/object.Chat' - responses: - "200": - description: The Response object - schema: - $ref: '#/definitions/controllers.Response' /api/add-enforcer: post: tags: @@ -157,24 +139,6 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' - /api/add-message: - post: - tags: - - Message API - description: add message - operationId: ApiController.AddMessage - parameters: - - in: body - name: body - description: The details of the message - required: true - schema: - $ref: '#/definitions/object.Message' - responses: - "200": - description: The Response object - schema: - $ref: '#/definitions/controllers.Response' /api/add-model: post: tags: @@ -696,24 +660,6 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' - /api/delete-chat: - post: - tags: - - Chat API - description: delete chat - operationId: ApiController.DeleteChat - parameters: - - in: body - name: body - description: The details of the chat - required: true - schema: - $ref: '#/definitions/object.Chat' - responses: - "200": - description: The Response object - schema: - $ref: '#/definitions/controllers.Response' /api/delete-enforcer: post: tags: @@ -768,24 +714,6 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' - /api/delete-message: - post: - tags: - - Message API - description: delete message - operationId: ApiController.DeleteMessage - parameters: - - in: body - name: body - description: The details of the message - required: true - schema: - $ref: '#/definitions/object.Message' - responses: - "200": - description: The Response object - schema: - $ref: '#/definitions/controllers.Response' /api/delete-mfa/: post: tags: @@ -1271,42 +1199,17 @@ paths: type: array items: $ref: '#/definitions/object.Cert' - /api/get-chat: + /api/get-dashboard: get: tags: - - Chat API - description: get chat - operationId: ApiController.GetChat - parameters: - - in: query - name: id - description: The id ( owner/name ) of the chat - required: true - type: string + - GetDashboard API + description: get information of dashboard + operationId: ApiController.GetDashboard responses: "200": description: The Response object schema: - $ref: '#/definitions/object.Chat' - /api/get-chats: - get: - tags: - - Chat API - description: get chats - operationId: ApiController.GetChats - parameters: - - in: query - name: owner - description: The owner of chats - required: true - type: string - responses: - "200": - description: The Response object - schema: - type: array - items: - $ref: '#/definitions/object.Chat' + $ref: '#/definitions/controllers.Response' /api/get-default-application: get: tags: @@ -1503,59 +1406,6 @@ paths: type: array items: $ref: '#/definitions/object.Ldap' - /api/get-message: - get: - tags: - - Message API - description: get message - operationId: ApiController.GetMessage - parameters: - - in: query - name: id - description: The id ( owner/name ) of the message - required: true - type: string - responses: - "200": - description: The Response object - schema: - $ref: '#/definitions/object.Message' - /api/get-message-answer: - get: - tags: - - Message API - description: get message answer - operationId: ApiController.GetMessageAnswer - parameters: - - in: query - name: id - description: The id ( owner/name ) of the message - required: true - type: string - responses: - "200": - description: The Response object - schema: - $ref: '#/definitions/object.Message' - /api/get-messages: - get: - tags: - - Message API - description: get messages - operationId: ApiController.GetMessages - parameters: - - in: query - name: owner - description: The owner of messages - required: true - type: string - responses: - "200": - description: The Response object - schema: - type: array - items: - $ref: '#/definitions/object.Message' /api/get-model: get: tags: @@ -2925,29 +2775,6 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' - /api/update-chat: - post: - tags: - - Chat API - description: update chat - operationId: ApiController.UpdateChat - parameters: - - in: query - name: id - description: The id ( owner/name ) of the chat - required: true - type: string - - in: body - name: body - description: The details of the chat - required: true - schema: - $ref: '#/definitions/object.Chat' - responses: - "200": - description: The Response object - schema: - $ref: '#/definitions/controllers.Response' /api/update-enforcer: post: tags: @@ -3012,29 +2839,6 @@ paths: description: The Response object schema: $ref: '#/definitions/controllers.Response' - /api/update-message: - post: - tags: - - Message API - description: update message - operationId: ApiController.UpdateMessage - parameters: - - in: query - name: id - description: The id ( owner/name ) of the message - required: true - type: string - - in: body - name: body - description: The details of the message - required: true - schema: - $ref: '#/definitions/object.Message' - responses: - "200": - description: The Response object - schema: - $ref: '#/definitions/controllers.Response' /api/update-model: post: tags: @@ -3549,6 +3353,12 @@ paths: schema: $ref: '#/definitions/controllers.Response' definitions: + 1183.0xc000639290.false: + title: "false" + type: object + 1217.0xc0006392c0.false: + title: "false" + type: object LaravelResponse: title: LaravelResponse type: object @@ -3588,13 +3398,9 @@ definitions: type: object properties: data: - additionalProperties: - description: support string, struct or []struct - type: string + $ref: '#/definitions/1183.0xc000639290.false' data2: - additionalProperties: - description: support string, struct or []struct - type: string + $ref: '#/definitions/1217.0xc0006392c0.false' msg: type: string name: @@ -3618,9 +3424,15 @@ definitions: jose.JSONWebKey: title: JSONWebKey type: object + model.Model: + title: Model + type: object object: title: object type: object + object.&{197049 0xc000a2cd50 false}: + title: '&{197049 0xc000a2cd50 false}' + type: object object.AccountItem: title: AccountItem type: object @@ -3637,13 +3449,30 @@ definitions: title: Adapter type: object properties: - Engine: - $ref: '#/definitions/xorm.Engine' - dataSourceName: + createdTime: type: string - dbName: + database: type: string - driverName: + databaseType: + type: string + host: + type: string + name: + type: string + owner: + type: string + password: + type: string + port: + type: integer + format: int64 + table: + type: string + tableNamePrefix: + type: string + type: + type: string + user: type: string object.Application: title: Application @@ -3752,7 +3581,7 @@ definitions: title: CasbinRequest type: array items: - $ref: '#/definitions/object.CasbinRequest' + $ref: '#/definitions/object.&{197049 0xc000a2cd50 false}' object.Cert: title: Cert type: object @@ -3785,37 +3614,6 @@ definitions: type: string type: type: string - object.Chat: - title: Chat - type: object - properties: - category: - type: string - createdTime: - type: string - displayName: - type: string - messageCount: - type: integer - format: int64 - name: - type: string - organization: - type: string - owner: - type: string - type: - type: string - updatedTime: - type: string - user1: - type: string - user2: - type: string - users: - type: array - items: - type: string object.Enforce: title: Enforce type: object @@ -3831,10 +3629,11 @@ definitions: type: string displayName: type: string - isEnabled: - type: boolean model: type: string + modelCfg: + additionalProperties: + type: string name: type: string owner: @@ -3992,26 +3791,6 @@ definitions: type: string username: type: string - object.Message: - title: Message - type: object - properties: - author: - type: string - chat: - type: string - createdTime: - type: string - name: - type: string - organization: - type: string - owner: - type: string - replyTo: - type: string - text: - type: string object.MfaItem: title: MfaItem type: object @@ -4050,8 +3829,6 @@ definitions: type: string displayName: type: string - isEnabled: - type: boolean modelText: type: string name: @@ -4169,6 +3946,18 @@ definitions: $ref: '#/definitions/object.ThemeData' websiteUrl: type: string + object.Ormer: + title: Ormer + type: object + properties: + Engine: + $ref: '#/definitions/xorm.Engine' + dataSourceName: + type: string + dbName: + type: string + driverName: + type: string object.Payment: title: Payment type: object @@ -4195,7 +3984,7 @@ definitions: type: string name: type: string - organization: + outOrderId: type: string owner: type: string @@ -4221,7 +4010,7 @@ definitions: returnUrl: type: string state: - type: string + $ref: '#/definitions/pp.PaymentState' tag: type: string type: @@ -4692,8 +4481,6 @@ definitions: type: array items: $ref: '#/definitions/object.TableColumn' - tablePrimaryKey: - type: string type: type: string user: @@ -4706,6 +4493,8 @@ definitions: type: string isHashed: type: boolean + isKey: + type: boolean name: type: string type: @@ -4920,8 +4709,6 @@ definitions: type: boolean isForbidden: type: boolean - isGlobalAdmin: - type: boolean isOnline: type: boolean kakao: @@ -5073,6 +4860,8 @@ definitions: type: string vk: type: string + web3onboard: + type: string webauthnCredentials: type: array items: @@ -5151,6 +4940,16 @@ definitions: type: string url: type: string + pp.PaymentState: + title: PaymentState + type: string + enum: + - PaymentStatePaid = "Paid" + - PaymentStateCreated = "Created" + - PaymentStateCanceled = "Canceled" + - PaymentStateTimeout = "Timeout" + - PaymentStateError = "Error" + example: Paid protocol.CredentialAssertion: title: CredentialAssertion type: object @@ -5195,11 +4994,11 @@ definitions: xorm.Engine: title: Engine type: object + xormadapter.Adapter: + title: Adapter + type: object securityDefinitions: AccessToken: type: apiKey name: Authorization in: header -externalDocs: - description: Find out more about Casdoor - url: https://casdoor.org/ diff --git a/web/src/OrganizationListPage.js b/web/src/OrganizationListPage.js index 897f99f7..e346a23a 100644 --- a/web/src/OrganizationListPage.js +++ b/web/src/OrganizationListPage.js @@ -81,7 +81,6 @@ class OrganizationListPage extends BaseListPage { {name: "Properties", visible: false, viewRule: "Admin", modifyRule: "Admin"}, {name: "Is online", visible: true, viewRule: "Admin", modifyRule: "Admin"}, {name: "Is admin", visible: true, viewRule: "Admin", modifyRule: "Admin"}, - {name: "Is global admin", visible: true, viewRule: "Admin", modifyRule: "Admin"}, {name: "Is forbidden", visible: true, viewRule: "Admin", modifyRule: "Admin"}, {name: "Is deleted", visible: true, viewRule: "Admin", modifyRule: "Admin"}, {Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"}, diff --git a/web/src/Setting.js b/web/src/Setting.js index 22b63c0e..1fa48075 100644 --- a/web/src/Setting.js +++ b/web/src/Setting.js @@ -620,7 +620,7 @@ export function isAdminUser(account) { if (account === undefined || account === null) { return false; } - return account.owner === "built-in" || account.isGlobalAdmin === true; + return account.owner === "built-in"; } export function isLocalAdminUser(account) { diff --git a/web/src/UserEditPage.js b/web/src/UserEditPage.js index b7492f5d..f54fb7f8 100644 --- a/web/src/UserEditPage.js +++ b/web/src/UserEditPage.js @@ -842,19 +842,6 @@ class UserEditPage extends React.Component { ); - } else if (accountItem.name === "Is global admin") { - return ( - - - {Setting.getLabel(i18next.t("user:Is global admin"), i18next.t("user:Is global admin - Tooltip"))} : - - - { - this.updateUserField("isGlobalAdmin", checked); - }} /> - - - ); } else if (accountItem.name === "Is forbidden") { return ( diff --git a/web/src/UserListPage.js b/web/src/UserListPage.js index 2708cb89..0c0248d0 100644 --- a/web/src/UserListPage.js +++ b/web/src/UserListPage.js @@ -81,7 +81,6 @@ class UserListPage extends BaseListPage { tag: "staff", region: "", isAdmin: (owner === "built-in"), - isGlobalAdmin: (owner === "built-in"), IsForbidden: false, score: this.state.organization.initScore, isDeleted: false, @@ -354,18 +353,6 @@ class UserListPage extends BaseListPage { ); }, }, - { - title: i18next.t("user:Is global admin"), - dataIndex: "isGlobalAdmin", - key: "isGlobalAdmin", - width: "140px", - sorter: true, - render: (text, record, index) => { - return ( - - ); - }, - }, { title: i18next.t("user:Is forbidden"), dataIndex: "isForbidden", diff --git a/web/src/WebhookEditPage.js b/web/src/WebhookEditPage.js index 9f5d6990..72590b23 100644 --- a/web/src/WebhookEditPage.js +++ b/web/src/WebhookEditPage.js @@ -79,7 +79,6 @@ const userTemplate = { "ranking": 10, "isOnline": false, "isAdmin": true, - "isGlobalAdmin": false, "isForbidden": false, "isDeleted": false, "signupApplication": "app-casnode", diff --git a/web/src/common/OAuthWidget.js b/web/src/common/OAuthWidget.js index 5bae8305..4b5785b6 100644 --- a/web/src/common/OAuthWidget.js +++ b/web/src/common/OAuthWidget.js @@ -188,7 +188,7 @@ class OAuthWidget extends React.Component { ) ) : ( - + ) } diff --git a/web/src/table/AccountTable.js b/web/src/table/AccountTable.js index a347115d..6c2e0f23 100644 --- a/web/src/table/AccountTable.js +++ b/web/src/table/AccountTable.js @@ -100,7 +100,6 @@ class AccountTable extends React.Component { {name: "Properties", label: i18next.t("user:Properties")}, {name: "Is online", label: i18next.t("user:Is online")}, {name: "Is admin", label: i18next.t("user:Is admin")}, - {name: "Is global admin", label: i18next.t("user:Is global admin")}, {name: "Is forbidden", label: i18next.t("user:Is forbidden")}, {name: "Is deleted", label: i18next.t("user:Is deleted")}, {name: "Multi-factor authentication", label: i18next.t("user:Multi-factor authentication")}, @@ -179,7 +178,7 @@ class AccountTable extends React.Component { } let options; - if (record.viewRule === "Admin" || record.name === "Is admin" || record.name === "Is global admin") { + if (record.viewRule === "Admin" || record.name === "Is admin") { options = [ {id: "Admin", name: "Admin"}, {id: "Immutable", name: "Immutable"}, diff --git a/web/src/table/SyncerTableColumnTable.js b/web/src/table/SyncerTableColumnTable.js index e602d831..e1aa6f99 100644 --- a/web/src/table/SyncerTableColumnTable.js +++ b/web/src/table/SyncerTableColumnTable.js @@ -100,7 +100,7 @@ class SyncerTableColumnTable extends React.Component { { ["Name", "CreatedTime", "UpdatedTime", "Id", "Type", "Password", "PasswordSalt", "DisplayName", "FirstName", "LastName", "Avatar", "PermanentAvatar", "Email", "EmailVerified", "Phone", "Location", "Address", "Affiliation", "Title", "IdCardType", "IdCard", "Homepage", "Bio", "Tag", "Region", - "Language", "Gender", "Birthday", "Education", "Score", "Ranking", "IsDefaultAvatar", "IsOnline", "IsAdmin", "IsGlobalAdmin", "IsForbidden", "IsDeleted", "CreatedIp"] + "Language", "Gender", "Birthday", "Education", "Score", "Ranking", "IsDefaultAvatar", "IsOnline", "IsAdmin", "IsForbidden", "IsDeleted", "CreatedIp"] .map((item, index) => ) } diff --git a/xlsx/user_test.xlsx b/xlsx/user_test.xlsx index cb9b7dbad05ec7b02b40297a3e4680cf19c5697a..95d42962eb9d4bb990411857e996aef1af37017b 100644 GIT binary patch delta 3281 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