Compare commits

..

12 Commits

Author SHA1 Message Date
5d9b17542f feat: end-user log out (#1356) 2023-01-17 22:57:05 +08:00
0021226a60 fix: check the duplicated Application ClientId (#1481)
* fix: Check the duplicate ClientId and ClientSecret of Application.

* Bug fix
2023-01-17 17:37:20 +08:00
79fc0516dd feat: check username if it's changed (#1482) 2023-01-17 17:08:37 +08:00
a73be11990 feat: update permission when role deleted (#1480) 2023-01-17 17:04:58 +08:00
eddd8acbf4 feat: update permission rule when role updated (#1477) 2023-01-17 10:27:02 +08:00
d0741e3705 feat: fix compatibility issue between Casbin request and model (#1478) 2023-01-15 12:06:10 +08:00
Liu
c66561dc9a feat: support sqlite database without cgo by using the modernc.org/sqlite driver (#1474)
* Refactor: update sqlite go driver to modernc.org/sqlite without cgo

* fix: update sqlite driver to modernc.org/sqlite

* fix: sqlite driver to modernc.org/sqlite

* Update adapter.go

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-01-14 20:11:23 +08:00
fcdf1e8dd2 feat: improve Select component performance (#1472) 2023-01-12 23:11:11 +08:00
6d4f94986e feat: fix the bug that "app - global" admin is not allowed to update user (#1468) 2023-01-12 12:29:11 +08:00
9ca686b240 feat: disable role and permission update and checks when updating user (#1466) 2023-01-12 11:40:32 +08:00
c93bc0dda2 fix: add e2e cypress screenshots and videos if failed (#1465)
* feat: location error

* feat: location error

* feat: test error

* feat add e2e

* feat: delete cypress dependency

* feat: Add e2e error feedback
2023-01-11 23:56:09 +08:00
7d25b9cdd8 feat: auto link accounts with the same email (#1464) 2023-01-11 23:19:16 +08:00
53 changed files with 1307 additions and 506 deletions

View File

@ -107,12 +107,23 @@ jobs:
run: nohup go run ./main.go &
working-directory: ./
- name: Sleep for starting
run: sleep 60s
run: sleep 90s
shell: bash
- name: e2e
run: npx cypress run --spec "**/e2e/**.cy.js"
working-directory: ./web
- uses: actions/upload-artifact@v3
if: failure()
with:
name: cypress-screenshots
path: ./web/cypress/screenshots
- uses: actions/upload-artifact@v3
if: always()
with:
name: cypress-videos
path: ./web/cypress/videos
release-and-push:
name: Release And Push
runs-on: ubuntu-latest

View File

@ -17,6 +17,7 @@ package controllers
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
@ -238,21 +239,67 @@ func (c *ApiController) Signup() {
// @Title Logout
// @Tag Login API
// @Description logout the current user
// @Param id_token_hint query string false "id_token_hint"
// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri"
// @Param state query string false "state"
// @Success 200 {object} controllers.Response The Response object
// @router /logout [get,post]
func (c *ApiController) Logout() {
user := c.GetSessionUsername()
object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID())
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication()
c.ClearUserSession()
// https://openid.net/specs/openid-connect-rpinitiated-1_0-final.html
accessToken := c.Input().Get("id_token_hint")
redirectUri := c.Input().Get("post_logout_redirect_uri")
state := c.Input().Get("state")
if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" {
c.ResponseOk(user)
if accessToken == "" && redirectUri == "" {
c.ClearUserSession()
object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID())
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication()
if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" {
c.ResponseOk(user)
return
}
c.ResponseOk(user, application.HomepageUrl)
return
} else {
if redirectUri == "" {
c.ResponseError(c.T("general:Missing parameter") + ": post_logout_redirect_uri")
return
}
if accessToken == "" {
c.ResponseError(c.T("general:Missing parameter") + ": id_token_hint")
return
}
affected, application, token := object.ExpireTokenByAccessToken(accessToken)
if !affected {
c.ResponseError(c.T("token:Token not found, invalid accessToken"))
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist")), token.Application)
return
}
if application.IsRedirectUriValid(redirectUri) {
if user == "" {
user = util.GetId(token.Organization, token.User)
}
c.ClearUserSession()
object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID())
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
c.Ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?state=%s", strings.TrimRight(redirectUri, "/"), state))
} else {
c.ResponseError(fmt.Sprintf(c.T("token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri))
return
}
}
c.ResponseOk(user, application.HomepageUrl)
}
// GetAccount

View File

@ -113,7 +113,7 @@ func (c *ApiController) GetOrganizationApplications() {
sortOrder := c.Input().Get("sortOrder")
if organization == "" {
c.ResponseError(c.T("application:Parameter organization is missing"))
c.ResponseError(c.T("general:Missing parameter") + ": organization")
return
}

View File

@ -405,54 +405,61 @@ func (c *ApiController) Login() {
return
}
// Handle username conflicts
tmpUser := object.GetUser(fmt.Sprintf("%s/%s", application.Organization, userInfo.Username))
if tmpUser != nil {
uid, err := uuid.NewRandom()
if application.EnableLinkWithEmail {
// find user that has the same email
user = object.GetUserByField(application.Organization, "email", userInfo.Email)
}
if user == nil || user.IsDeleted {
// Handle username conflicts
tmpUser := object.GetUser(fmt.Sprintf("%s/%s", application.Organization, userInfo.Username))
if tmpUser != nil {
uid, err := uuid.NewRandom()
if err != nil {
c.ResponseError(err.Error())
return
}
uidStr := strings.Split(uid.String(), "-")
userInfo.Username = fmt.Sprintf("%s_%s", userInfo.Username, uidStr[1])
}
properties := map[string]string{}
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
initScore, err := getInitScore(organization)
if err != nil {
c.ResponseError(err.Error())
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
return
}
uidStr := strings.Split(uid.String(), "-")
userInfo.Username = fmt.Sprintf("%s_%s", userInfo.Username, uidStr[1])
user = &object.User{
Owner: application.Organization,
Name: userInfo.Username,
CreatedTime: util.GetCurrentTime(),
Id: util.GenerateId(),
Type: "normal-user",
DisplayName: userInfo.DisplayName,
Avatar: userInfo.AvatarUrl,
Address: []string{},
Email: userInfo.Email,
Score: initScore,
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,
IsDeleted: false,
SignupApplication: application.Name,
Properties: properties,
}
affected := object.AddUser(user)
if !affected {
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to create user, user information is invalid: %s"), util.StructToJson(user)))
return
}
}
properties := map[string]string{}
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
initScore, err := getInitScore(organization)
if err != nil {
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
return
}
user = &object.User{
Owner: application.Organization,
Name: userInfo.Username,
CreatedTime: util.GetCurrentTime(),
Id: util.GenerateId(),
Type: "normal-user",
DisplayName: userInfo.DisplayName,
Avatar: userInfo.AvatarUrl,
Address: []string{},
Email: userInfo.Email,
Score: initScore,
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,
IsDeleted: false,
SignupApplication: application.Name,
Properties: properties,
}
// sync info from 3rd-party if possible
object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
affected := object.AddUser(user)
if !affected {
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to create user, user information is invalid: %s"), util.StructToJson(user)))
return
}
object.LinkUserAccount(user, provider.Type, userInfo.Id)
resp = c.HandleLoggedIn(application, user, &form)

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetLdapUser() {
ldapServer := LdapServer{}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer)
if err != nil || util.IsStrsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) {
c.ResponseError(c.T("ldap:Missing parameter"))
c.ResponseError(c.T("general:Missing parameter"))
return
}
@ -120,7 +120,7 @@ func (c *ApiController) GetLdap() {
id := c.Input().Get("id")
if util.IsStrsEmpty(id) {
c.ResponseError(c.T("ldap:Missing parameter"))
c.ResponseError(c.T("general:Missing parameter"))
return
}
@ -136,12 +136,12 @@ func (c *ApiController) AddLdap() {
var ldap object.Ldap
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
if err != nil {
c.ResponseError(c.T("ldap:Missing parameter"))
c.ResponseError(c.T("general:Missing parameter"))
return
}
if util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError(c.T("ldap:Missing parameter"))
c.ResponseError(c.T("general:Missing parameter"))
return
}
@ -171,7 +171,7 @@ func (c *ApiController) UpdateLdap() {
var ldap object.Ldap
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
if err != nil || util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError(c.T("ldap:Missing parameter"))
c.ResponseError(c.T("general:Missing parameter"))
return
}

View File

@ -16,7 +16,6 @@ package controllers
import (
"encoding/json"
"net/http"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
@ -247,28 +246,6 @@ func (c *ApiController) RefreshToken() {
c.ServeJSON()
}
// TokenLogout
// @Title TokenLogout
// @Tag Token API
// @Description delete token by AccessToken
// @Param id_token_hint query string true "id_token_hint"
// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri"
// @Param state query string true "state"
// @Success 200 {object} controllers.Response The Response object
// @router /login/oauth/logout [get]
func (c *ApiController) TokenLogout() {
token := c.Input().Get("id_token_hint")
flag, application := object.DeleteTokenByAccessToken(token)
redirectUri := c.Input().Get("post_logout_redirect_uri")
state := c.Input().Get("state")
if application != nil && application.IsRedirectUriValid(redirectUri) {
c.Ctx.Redirect(http.StatusFound, redirectUri+"?state="+state)
return
}
c.Data["json"] = wrapActionResponse(flag)
c.ServeJSON()
}
// IntrospectToken
// @Title IntrospectToken
// @Description The introspection endpoint is an OAuth 2.0 endpoint that takes a

View File

@ -95,20 +95,6 @@ func checkPermissionForUpdateUser(userId string, newUser object.User, c *ApiCont
itemsChanged = append(itemsChanged, item)
}
oldUserRolesJson, _ := json.Marshal(oldUser.Roles)
newUserRolesJson, _ := json.Marshal(newUser.Roles)
if string(oldUserRolesJson) != string(newUserRolesJson) {
item := object.GetAccountItemByName("Roles", organization)
itemsChanged = append(itemsChanged, item)
}
oldUserPermissionJson, _ := json.Marshal(oldUser.Permissions)
newUserPermissionJson, _ := json.Marshal(newUser.Permissions)
if string(oldUserPermissionJson) != string(newUserPermissionJson) {
item := object.GetAccountItemByName("Permissions", organization)
itemsChanged = append(itemsChanged, item)
}
oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties)
newUserPropertiesJson, _ := json.Marshal(newUser.Properties)
if string(oldUserPropertiesJson) != string(newUserPropertiesJson) {
@ -133,8 +119,15 @@ func checkPermissionForUpdateUser(userId string, newUser object.User, c *ApiCont
itemsChanged = append(itemsChanged, item)
}
currentUser := c.getCurrentUser()
if currentUser == nil && c.IsGlobalAdmin() {
currentUser = &object.User{
IsGlobalAdmin: true,
}
}
for i := range itemsChanged {
if pass, err := object.CheckAccountItemModifyRule(itemsChanged[i], c.getCurrentUser(), c.GetAcceptLanguage()); !pass {
if pass, err := object.CheckAccountItemModifyRule(itemsChanged[i], currentUser, c.GetAcceptLanguage()); !pass {
return pass, err
}
}

View File

@ -51,15 +51,15 @@ func (c *ApiController) SendVerificationCode() {
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
if destType == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": type.")
c.ResponseError(c.T("general:Missing parameter") + ": type.")
return
}
if dest == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": dest.")
c.ResponseError(c.T("general:Missing parameter") + ": dest.")
return
}
if applicationId == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": applicationId.")
c.ResponseError(c.T("general:Missing parameter") + ": applicationId.")
return
}
if !strings.Contains(applicationId, "/") {
@ -67,7 +67,7 @@ func (c *ApiController) SendVerificationCode() {
return
}
if checkType == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": checkType.")
c.ResponseError(c.T("general:Missing parameter") + ": checkType.")
return
}
@ -75,7 +75,7 @@ func (c *ApiController) SendVerificationCode() {
if captchaProvider != nil {
if checkKey == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": checkKey.")
c.ResponseError(c.T("general:Missing parameter") + ": checkKey.")
return
}
isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId)
@ -170,7 +170,7 @@ func (c *ApiController) ResetEmailOrPhone() {
dest := c.Ctx.Request.Form.Get("dest")
code := c.Ctx.Request.Form.Get("code")
if len(dest) == 0 || len(code) == 0 || len(destType) == 0 {
c.ResponseError(c.T("verification:Missing parameter"))
c.ResponseError(c.T("general:Missing parameter"))
return
}
@ -247,11 +247,11 @@ func (c *ApiController) VerifyCaptcha() {
captchaToken := c.Ctx.Request.Form.Get("captchaToken")
clientSecret := c.Ctx.Request.Form.Get("clientSecret")
if captchaToken == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": captchaToken.")
c.ResponseError(c.T("general:Missing parameter") + ": captchaToken.")
return
}
if clientSecret == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": clientSecret.")
c.ResponseError(c.T("general:Missing parameter") + ": clientSecret.")
return
}

5
go.mod
View File

@ -13,7 +13,7 @@ require (
github.com/casdoor/go-sms-sender v0.5.1
github.com/casdoor/oss v1.2.0
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc
github.com/denisenkom/go-mssqldb v0.9.0
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b
github.com/forestmgy/ldapserver v1.1.0
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
@ -51,7 +51,8 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v2 v2.3.0 // indirect
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84
xorm.io/builder v0.3.12 // indirect
xorm.io/core v0.7.2
xorm.io/xorm v1.0.5
xorm.io/xorm v1.1.2
)

58
go.sum
View File

@ -126,10 +126,13 @@ github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWW
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b h1:L63RATZFZuFMXy6ixnKmv3eNAXwYQF6HW1vd4IYsQqQ=
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b/go.mod h1:EYSpSkwoEcryMmQGfhol2IiB3IMN9IIIaNd/wcAQMGQ=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
@ -231,6 +234,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -279,14 +283,17 @@ github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9q
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -327,8 +334,11 @@ github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@ -384,6 +394,8 @@ github.com/qiangmzsx/string-adapter/v2 v2.1.0/go.mod h1:PElPB7b7HnGKTsuADAffFpOQ
github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk=
github.com/qiniu/go-sdk/v7 v7.12.1/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFsDY0BLE+w=
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -506,6 +518,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -539,6 +552,7 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -561,6 +575,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -585,6 +600,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -599,7 +615,10 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -669,10 +688,13 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@ -795,14 +817,42 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009 h1:u0oCo5b9wyLr++HF3AN9JicGhkUxJhMz51+8TIZH9N0=
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
modernc.org/ccgo/v3 v3.9.0 h1:JbcEIqjw4Agf+0g3Tc85YvfYqkkFOv6xBwS4zkfqSoA=
modernc.org/ccgo/v3 v3.9.0/go.mod h1:nQbgkn8mwzPdp4mm6BT6+p85ugQ7FrGgIcYaE7nSrpY=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.8.0 h1:Pp4uv9g0csgBMpGPABKtkieF6O5MGhfGo6ZiOdlYfR8=
modernc.org/libc v1.8.0/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84 h1:rgEUzE849tFlHSoeCrKyS9cZAljC+DY7MdMHKq6R6sY=
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84/go.mod h1:PGzq6qlhyYjL6uVbSgS6WoF7ZopTW/sI7+7p+mb4ZVU=
modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc=
modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/tcl v1.5.0 h1:euZSUNfE0Fd4W8VqXI1Ly1v7fqDJoBuAV88Ea+SnaSs=
modernc.org/tcl v1.5.0/go.mod h1:gb57hj4pO8fRrK54zveIfFXBaMHK3SKJNWcmRw1cRzc=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc=
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.8/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM=
xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
xorm.io/xorm v1.0.3/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
xorm.io/xorm v1.0.5 h1:LRr5PfOUb4ODPR63YwbowkNDwcolT2LnkwP/TUaMaB0=
xorm.io/xorm v1.0.5/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
xorm.io/xorm v1.1.2 h1:bje+1KZvK3m5AHtZNfUDlKEEyuw/IRHT+an0CLIG5TU=
xorm.io/xorm v1.1.2/go.mod h1:Cb0DKYTHbyECMaSfgRnIZp5aiUgQozxcJJ0vzcLGJSg=

View File

@ -8,9 +8,6 @@
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
@ -64,12 +61,12 @@
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
@ -112,7 +109,8 @@
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list"
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
@ -131,7 +129,6 @@
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Turing test failed.": "Turing test failed.",

View File

@ -8,9 +8,6 @@
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
@ -64,12 +61,12 @@
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
@ -112,7 +109,8 @@
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list"
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
@ -131,7 +129,6 @@
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Turing test failed.": "Turing test failed.",

View File

@ -8,9 +8,6 @@
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
@ -64,12 +61,12 @@
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
@ -112,7 +109,8 @@
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list"
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
@ -131,7 +129,6 @@
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Turing test failed.": "Turing test failed.",

View File

@ -8,9 +8,6 @@
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
@ -64,12 +61,12 @@
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
@ -112,7 +109,8 @@
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list"
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
@ -131,7 +129,6 @@
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Turing test failed.": "Turing test failed.",

View File

@ -8,9 +8,6 @@
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
@ -64,12 +61,12 @@
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
@ -112,7 +109,8 @@
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list"
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
@ -131,7 +129,6 @@
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Turing test failed.": "Turing test failed.",

View File

@ -8,9 +8,6 @@
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
@ -64,12 +61,12 @@
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
@ -112,7 +109,8 @@
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list"
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
@ -131,7 +129,6 @@
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Turing test failed.": "Turing test failed.",

View File

@ -8,9 +8,6 @@
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
@ -64,12 +61,12 @@
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
@ -112,7 +109,8 @@
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list"
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
@ -131,7 +129,6 @@
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Turing test failed.": "Turing test failed.",

View File

@ -8,9 +8,6 @@
"Please sign out first before signing up": "请在注册前先退出登录",
"The application does not allow to sign up new account": "该应用不允许注册新用户"
},
"application": {
"Parameter organization is missing": "缺少organization参数"
},
"auth": {
"%s No phone prefix": "%s 无此手机号前缀",
"Challenge method should be S256": "Challenge 方法应该为 S256",
@ -64,12 +61,12 @@
"unsupported password type: %s": "不支持的密码类型: %s"
},
"general": {
"Missing parameter": "缺少参数",
"Please login first": "请先登录",
"The user: %s doesn't exist": "用户: %s 不存在"
},
"ldap": {
"Ldap server exist": "LDAP服务器已存在",
"Missing parameter": "LDAP缺少参数"
"Ldap server exist": "LDAP服务器已存在"
},
"link": {
"Please link first": "请先绑定",
@ -112,7 +109,8 @@
"Grant_type: %s is not supported in this application": "该应用不支持Grant_type: %s",
"Invalid application or wrong clientSecret": "无效应用或错误的clientSecret",
"Invalid client_id": "无效的ClientId",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "重定向 URI%s 在许可跳转列表中未找到"
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "重定向 URI%s 在许可跳转列表中未找到",
"Token not found, invalid accessToken": "未查询到对应token, accessToken无效"
},
"user": {
"Display name cannot be empty": "显示名称不可为空",
@ -131,7 +129,6 @@
"Code has not been sent yet!": "验证码还未发送",
"Email is invalid": "非法的邮箱",
"Invalid captcha provider.": "非法的验证码提供商",
"Missing parameter": "缺少参数",
"Organization does not exist": "组织不存在",
"Phone number is invalid": "非法的手机号码",
"Turing test failed.": "验证码还未发送",

View File

@ -25,10 +25,10 @@ import (
_ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/go-sql-driver/mysql" // db = mysql
_ "github.com/lib/pq" // db = postgres
"xorm.io/xorm/migrate"
//_ "github.com/mattn/go-sqlite3" // db = sqlite3
_ "modernc.org/sqlite" // db = sqlite
"xorm.io/core"
"xorm.io/xorm"
"xorm.io/xorm/migrate"
)
var adapter *Adapter

View File

@ -50,6 +50,7 @@ type Application struct {
EnableCodeSignin bool `json:"enableCodeSignin"`
EnableSamlCompress bool `json:"enableSamlCompress"`
EnableWebAuthn bool `json:"enableWebAuthn"`
EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
@ -286,7 +287,8 @@ func GetMaskedApplications(applications []*Application, userId string) []*Applic
func UpdateApplication(id string, application *Application) bool {
owner, name := util.GetOwnerAndNameFromId(id)
if getApplication(owner, name) == nil {
oldApplication := getApplication(owner, name)
if oldApplication == nil {
return false
}
@ -301,6 +303,10 @@ func UpdateApplication(id string, application *Application) bool {
}
}
if oldApplication.ClientId != application.ClientId && GetApplicationByClientId(application.ClientId) != nil {
return false
}
for _, providerItem := range application.Providers {
providerItem.Provider = nil
}
@ -324,6 +330,9 @@ func AddApplication(application *Application) bool {
if application.ClientSecret == "" {
application.ClientSecret = util.GenerateClientSecret()
}
if GetApplicationByClientId(application.ClientId) != nil {
return false
}
for _, providerItem := range application.Providers {
providerItem.Provider = nil
}

View File

@ -347,11 +347,10 @@ func CheckUpdateUser(oldUser *User, user *User, lang string) string {
return i18n.Translate(lang, "user:Display name cannot be empty")
}
if msg := CheckUsername(user.Name, lang); msg != "" {
return msg
}
if oldUser.Name != user.Name {
if msg := CheckUsername(user.Name, lang); msg != "" {
return msg
}
if HasUserByField(user.Owner, "name", user.Name) {
return i18n.Translate(lang, "check:Username already exists")
}

View File

@ -68,14 +68,14 @@ func getModel(owner string, name string) *Model {
return nil
}
model := Model{Owner: owner, Name: name}
existed, err := adapter.Engine.Get(&model)
m := Model{Owner: owner, Name: name}
existed, err := adapter.Engine.Get(&m)
if err != nil {
panic(err)
}
if existed {
return &model
return &m
} else {
return nil
}
@ -152,3 +152,7 @@ func modelChangeTrigger(oldName string, newName string) error {
return session.Commit()
}
func HasRoleDefinition(m model.Model) bool {
return m["g"] != nil
}

View File

@ -40,6 +40,7 @@ type OidcDiscovery struct {
ClaimsSupported []string `json:"claims_supported"`
RequestParameterSupported bool `json:"request_parameter_supported"`
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"`
EndSessionEndpoint string `json:"end_session_endpoint"`
}
func getOriginFromHost(host string) (string, string) {
@ -84,6 +85,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
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"},
RequestParameterSupported: true,
RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"},
EndSessionEndpoint: fmt.Sprintf("%s/api/logout", originBackend),
}
return oidcDiscovery

View File

@ -57,6 +57,40 @@ type PermissionRule struct {
Id string `xorm:"varchar(100) index not null default ''" json:"id"`
}
const (
builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
builtInAdapter = "permission_rule"
)
func (p *Permission) GetId() string {
return util.GetId(p.Owner, p.Name)
}
func (p *PermissionRule) GetRequest(adapterName string, permissionId string) ([]interface{}, error) {
request := []interface{}{p.V0, p.V1, p.V2}
if p.V3 != "" {
request = append(request, p.V3)
}
if p.V4 != "" {
request = append(request, p.V4)
}
if adapterName == builtInAdapter {
if p.V5 != "" {
return nil, fmt.Errorf("too many parameters. The maximum parameter number cannot exceed %d", builtInAvailableField)
}
request = append(request, permissionId)
return request, nil
} else {
if p.V5 != "" {
request = append(request, p.V5)
}
return request, nil
}
}
func GetPermissionCount(owner, field, value string) int {
session := GetSession(owner, -1, -1, field, value, "", "")
count, err := session.Count(&Permission{})
@ -115,19 +149,25 @@ func GetPermission(id string) *Permission {
func checkPermissionValid(permission *Permission) {
enforcer := getEnforcer(permission)
enforcer.EnableAutoSave(false)
policies, groupingPolicies := getPolicies(permission)
policies := getPolicies(permission)
_, err := enforcer.AddPolicies(policies)
if err != nil {
panic(err)
}
if !HasRoleDefinition(enforcer.GetModel()) {
permission.Roles = []string{}
return
}
groupingPolicies := getGroupingPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
_, err := enforcer.AddPolicies(policies)
if err != nil {
panic(err)
}
}
func UpdatePermission(id string, permission *Permission) bool {
@ -144,6 +184,7 @@ func UpdatePermission(id string, permission *Permission) bool {
}
if affected != 0 {
removeGroupingPolicies(oldPermission)
removePolicies(oldPermission)
if oldPermission.Adapter != "" && oldPermission.Adapter != permission.Adapter {
isEmpty, _ := adapter.Engine.IsTableEmpty(oldPermission.Adapter)
@ -154,6 +195,7 @@ func UpdatePermission(id string, permission *Permission) bool {
}
}
}
addGroupingPolicies(permission)
addPolicies(permission)
}
@ -167,6 +209,7 @@ func AddPermission(permission *Permission) bool {
}
if affected != 0 {
addGroupingPolicies(permission)
addPolicies(permission)
}
@ -180,6 +223,7 @@ func DeletePermission(permission *Permission) bool {
}
if affected != 0 {
removeGroupingPolicies(permission)
removePolicies(permission)
if permission.Adapter != "" && permission.Adapter != "permission_rule" {
isEmpty, _ := adapter.Engine.IsTableEmpty(permission.Adapter)
@ -195,10 +239,6 @@ func DeletePermission(permission *Permission) bool {
return affected != 0
}
func (permission *Permission) GetId() string {
return fmt.Sprintf("%s/%s", permission.Owner, permission.Name)
}
func GetPermissionsByUser(userId string) []*Permission {
permissions := []*Permission{}
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions)

View File

@ -15,9 +15,11 @@
package object
import (
"fmt"
"strings"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/config"
"github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/conf"
@ -36,47 +38,48 @@ func getEnforcer(permission *Permission) *casbin.Enforcer {
panic(err)
}
modelText := `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act, "", "", permissionId
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
permissionModel := getModel(permission.Owner, permission.Model)
m := model.Model{}
if permissionModel != nil {
modelText = permissionModel.ModelText
m, err = GetBuiltInModel(permissionModel.ModelText)
} else {
m, err = GetBuiltInModel("")
}
m, err := model.NewModelFromString(modelText)
if err != nil {
panic(err)
}
policyFilter := xormadapter.Filter{}
if !HasRoleDefinition(m) {
policyFilter.Ptype = []string{"p"}
err = adapter.LoadFilteredPolicy(m, policyFilter)
if err != nil {
panic(err)
}
}
enforcer, err := casbin.NewEnforcer(m, adapter)
if err != nil {
panic(err)
}
// load Policy with a specific Permission
enforcer.LoadFilteredPolicy(xormadapter.Filter{
V5: []string{permission.Owner + "/" + permission.Name},
})
policyFilter.V5 = []string{permission.GetId()}
err = enforcer.LoadFilteredPolicy(policyFilter)
if err != nil {
panic(err)
}
return enforcer
}
func getPolicies(permission *Permission) ([][]string, [][]string) {
func getPolicies(permission *Permission) [][]string {
var policies [][]string
var groupingPolicies [][]string
permissionId := permission.Owner + "/" + permission.Name
permissionId := permission.GetId()
domainExist := len(permission.Domains) > 0
for _, user := range permission.Users {
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
@ -90,26 +93,8 @@ func getPolicies(permission *Permission) ([][]string, [][]string) {
}
}
}
for _, role := range permission.Roles {
roleObj := GetRole(role)
for _, subUser := range roleObj.Users {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subUser, domain, role, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subUser, role, "", "", "", permissionId})
}
}
for _, subRole := range roleObj.Roles {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subRole, domain, role, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subRole, role, "", "", "", permissionId})
}
}
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
if domainExist {
@ -122,19 +107,49 @@ func getPolicies(permission *Permission) ([][]string, [][]string) {
}
}
}
return policies, groupingPolicies
return policies
}
func getGroupingPolicies(permission *Permission) [][]string {
var groupingPolicies [][]string
domainExist := len(permission.Domains) > 0
permissionId := permission.GetId()
for _, role := range permission.Roles {
roleObj := GetRole(role)
if roleObj == nil {
continue
}
for _, subUser := range roleObj.Users {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subUser, domain, role, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subUser, role, "", "", "", permissionId})
}
}
for _, subRole := range roleObj.Roles {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subRole, domain, role, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subRole, role, "", "", "", permissionId})
}
}
}
return groupingPolicies
}
func addPolicies(permission *Permission) {
enforcer := getEnforcer(permission)
policies, groupingPolicies := getPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
policies := getPolicies(permission)
_, err := enforcer.AddPolicies(policies)
if err != nil {
@ -142,9 +157,21 @@ func addPolicies(permission *Permission) {
}
}
func removePolicies(permission *Permission) {
func addGroupingPolicies(permission *Permission) {
enforcer := getEnforcer(permission)
policies, groupingPolicies := getPolicies(permission)
groupingPolicies := getGroupingPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
}
func removeGroupingPolicies(permission *Permission) {
enforcer := getEnforcer(permission)
groupingPolicies := getGroupingPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.RemoveGroupingPolicies(groupingPolicies)
@ -152,6 +179,11 @@ func removePolicies(permission *Permission) {
panic(err)
}
}
}
func removePolicies(permission *Permission) {
enforcer := getEnforcer(permission)
policies := getPolicies(permission)
_, err := enforcer.RemovePolicies(policies)
if err != nil {
@ -163,10 +195,8 @@ func Enforce(permissionRule *PermissionRule) bool {
permission := GetPermission(permissionRule.Id)
enforcer := getEnforcer(permission)
request := []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2}
if permissionRule.V3 != "" {
request = append(request, permissionRule.V3)
}
request, _ := permissionRule.GetRequest(builtInAdapter, permissionRule.Id)
allow, err := enforcer.Enforce(request...)
if err != nil {
panic(err)
@ -177,11 +207,8 @@ func Enforce(permissionRule *PermissionRule) bool {
func BatchEnforce(permissionRules []PermissionRule) []bool {
var requests [][]interface{}
for _, permissionRule := range permissionRules {
if permissionRule.V3 != "" {
requests = append(requests, []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2, permissionRule.V3})
} else {
requests = append(requests, []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2})
}
request, _ := permissionRule.GetRequest(builtInAdapter, permissionRule.Id)
requests = append(requests, request)
}
permission := GetPermission(permissionRules[0].Id)
enforcer := getEnforcer(permission)
@ -226,3 +253,46 @@ func GetAllRoles(userId string) []string {
}
return res
}
func GetBuiltInModel(modelText string) (model.Model, error) {
if modelText == "" {
modelText = `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act, "", "", permissionId
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
return model.NewModelFromString(modelText)
} else {
cfg, err := config.NewConfigFromText(modelText)
if err != nil {
return nil, err
}
// load [policy_definition]
policyDefinition := strings.Split(cfg.String("policy_definition::p"), ",")
fieldsNum := len(policyDefinition)
if fieldsNum > builtInAvailableField {
panic(fmt.Errorf("the maximum policy_definition field number cannot exceed %d", builtInAvailableField))
}
// filled empty field with "" and V5 with "permissionId"
for i := builtInAvailableField - fieldsNum; i > 0; i-- {
policyDefinition = append(policyDefinition, "")
}
policyDefinition = append(policyDefinition, "permissionId")
m, _ := model.NewModelFromString(modelText)
m.AddDef("p", "p", strings.Join(policyDefinition, ","))
return m, err
}
}

View File

@ -95,6 +95,12 @@ func UpdateRole(id string, role *Role) bool {
return false
}
permissions := GetPermissionsByRole(id)
for _, permission := range permissions {
removeGroupingPolicies(permission)
removePolicies(permission)
}
if name != role.Name {
err := roleChangeTrigger(name, role.Name)
if err != nil {
@ -107,6 +113,13 @@ func UpdateRole(id string, role *Role) bool {
panic(err)
}
newRoleID := role.GetId()
permissions = GetPermissionsByRole(newRoleID)
for _, permission := range permissions {
addGroupingPolicies(permission)
addPolicies(permission)
}
return affected != 0
}
@ -120,6 +133,13 @@ func AddRole(role *Role) bool {
}
func DeleteRole(role *Role) bool {
roleId := role.GetId()
permissions := GetPermissionsByRole(roleId)
for _, permission := range permissions {
permission.Roles = util.DeleteVal(permission.Roles, roleId)
UpdatePermission(permission.GetId(), permission)
}
affected, err := adapter.Engine.ID(core.PK{role.Owner, role.Name}).Delete(&Role{})
if err != nil {
panic(err)

View File

@ -15,8 +15,6 @@
package object
import (
"time"
"github.com/beego/beego"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
@ -42,7 +40,7 @@ func SetSession(id string, sessionId string) {
if get {
_, err = adapter.Engine.ID(core.PK{owner, name}).Update(session)
} else {
session.CreatedTime = time.Now().Format(time.RFC3339)
session.CreatedTime = util.GetCurrentTime()
_, err = adapter.Engine.Insert(session)
}
if err != nil {
@ -66,7 +64,7 @@ func DeleteSession(id string) bool {
}
func DeleteSessionId(id string, sessionId string) bool {
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
owner, name := util.GetOwnerAndNameFromId(id)
session := &Session{Owner: owner, Name: name}
_, err := adapter.Engine.ID(core.PK{owner, name}).Get(session)

View File

@ -27,7 +27,7 @@ import (
)
const (
hourSeconds = 3600
hourMinutes = 60
InvalidRequest = "invalid_request"
InvalidClient = "invalid_client"
InvalidGrant = "invalid_grant"
@ -204,7 +204,7 @@ func DeleteToken(token *Token) bool {
return affected != 0
}
func DeleteTokenByAccessToken(accessToken string) (bool, *Application) {
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token) {
token := Token{AccessToken: accessToken}
existed, err := adapter.Engine.Get(&token)
if err != nil {
@ -212,15 +212,17 @@ func DeleteTokenByAccessToken(accessToken string) (bool, *Application) {
}
if !existed {
return false, nil
return false, nil, nil
}
application := getApplication(token.Owner, token.Application)
affected, err := adapter.Engine.Where("access_token=?", accessToken).Delete(&Token{})
token.ExpiresIn = 0
affected, err := adapter.Engine.ID(core.PK{token.Owner, token.Name}).Cols("expires_in").Update(&token)
if err != nil {
panic(err)
}
return affected != 0, application
application := getApplication(token.Owner, token.Application)
return affected != 0, application, &token
}
func GetTokenByAccessToken(accessToken string) *Token {
@ -304,7 +306,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
Code: util.GenerateClientId(),
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds,
ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope,
TokenType: "Bearer",
CodeChallenge: challenge,
@ -438,7 +440,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
Code: util.GenerateClientId(),
AccessToken: newAccessToken,
RefreshToken: newRefreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds,
ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope,
TokenType: "Bearer",
}
@ -588,7 +590,7 @@ func GetPasswordToken(application *Application, username string, password string
Code: util.GenerateClientId(),
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds,
ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope,
TokenType: "Bearer",
CodeIsUsed: true,
@ -628,7 +630,7 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
User: nullUser.Name,
Code: util.GenerateClientId(),
AccessToken: accessToken,
ExpiresIn: application.ExpireInHours * hourSeconds,
ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope,
TokenType: "Bearer",
CodeIsUsed: true,
@ -655,7 +657,7 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
Code: util.GenerateClientId(),
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds,
ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope,
TokenType: "Bearer",
CodeIsUsed: true,

View File

@ -110,8 +110,8 @@ type User struct {
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
Properties map[string]string `json:"properties"`
Roles []*Role `json:"roles"`
Permissions []*Permission `json:"permissions"`
Roles []*Role `xorm:"-" json:"roles"`
Permissions []*Permission `xorm:"-" json:"permissions"`
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"`

View File

@ -158,8 +158,6 @@ func initAPI() {
beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken")
beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken")
beego.Router("/api/login/oauth/logout", &controllers.ApiController{}, "GET:TokenLogout")
beego.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords")
beego.Router("/api/get-records-filter", &controllers.ApiController{}, "POST:GetRecordsByFilter")
beego.Router("/api/add-record", &controllers.ApiController{}, "POST:AddRecord")

View File

@ -275,6 +275,34 @@
}
}
},
"/api/add-record": {
"post": {
"tags": [
"Record API"
],
"description": "add a record",
"operationId": "ApiController.AddRecord",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the record",
"required": true,
"schema": {
"$ref": "#/definitions/object.Record"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/add-resource": {
"post": {
"tags": [
@ -431,6 +459,14 @@
"operationId": "ApiController.GetCaptcha"
}
},
"/api/api/get-webhook-event": {
"get": {
"tags": [
"GetWebhookEventType API"
],
"operationId": "ApiController.GetWebhookEventType"
}
},
"/api/api/reset-email-or-phone": {
"post": {
"tags": [
@ -523,6 +559,14 @@
}
}
},
"/api/api/webhook": {
"post": {
"tags": [
"HandleOfficialAccountEvent API"
],
"operationId": "ApiController.HandleOfficialAccountEvent"
}
},
"/api/buy-product": {
"post": {
"tags": [
@ -840,6 +884,35 @@
}
}
},
"/api/delete-session": {
"post": {
"tags": [
"Session API"
],
"description": "Delete session by userId",
"operationId": "ApiController.DeleteSession",
"parameters": [
{
"in": "query",
"name": "ID",
"description": "The ID(owner/name) of user.",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"/api/delete-syncer": {
"post": {
"tags": [
@ -1133,6 +1206,32 @@
}
}
},
"/api/get-default-application": {
"get": {
"tags": [
"Organization API"
],
"description": "get default application",
"operationId": "ApiController.GetDefaultApplication",
"parameters": [
{
"in": "query",
"name": "id",
"description": "organization id",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/Response"
}
}
}
}
},
"/api/get-email-and-phone": {
"post": {
"tags": [
@ -1166,6 +1265,26 @@
}
}
},
"/api/get-global-providers": {
"get": {
"tags": [
"Provider API"
],
"description": "get Global providers",
"operationId": "ApiController.GetGlobalProviders",
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Provider"
}
}
}
}
}
},
"/api/get-global-users": {
"get": {
"tags": [
@ -1459,6 +1578,55 @@
}
}
},
"/api/get-permissions-by-role": {
"get": {
"tags": [
"Permission API"
],
"description": "get permissions by role",
"operationId": "ApiController.GetPermissionsByRole",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id of the role",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Permission"
}
}
}
}
}
},
"/api/get-permissions-by-submitter": {
"get": {
"tags": [
"Permission API"
],
"description": "get permissions by submitter",
"operationId": "ApiController.GetPermissionsBySubmitter",
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Permission"
}
}
}
}
}
},
"/api/get-product": {
"get": {
"tags": [
@ -1631,6 +1799,20 @@
}
}
},
"/api/get-release": {
"get": {
"tags": [
"System API"
],
"description": "get local github repo's latest release version info",
"operationId": "ApiController.GitRepoVersion",
"responses": {
"200": {
"description": "{string} local latest version hash of casdoor"
}
}
}
},
"/api/get-resource": {
"get": {
"tags": [
@ -1702,6 +1884,35 @@
}
}
},
"/api/get-sessions": {
"get": {
"tags": [
"Session API"
],
"description": "Get organization user sessions",
"operationId": "ApiController.GetSessions",
"parameters": [
{
"in": "query",
"name": "owner",
"description": "The organization name",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"/api/get-sorted-users": {
"get": {
"tags": [
@ -1799,6 +2010,32 @@
}
}
},
"/api/get-system-info": {
"get": {
"tags": [
"System API"
],
"description": "get user's system info",
"operationId": "ApiController.GetSystemInfo",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id of the user",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/object.SystemInfo"
}
}
}
}
},
"/api/get-token": {
"get": {
"tags": [
@ -2360,45 +2597,6 @@
}
}
},
"/api/login/oauth/logout": {
"get": {
"tags": [
"Token API"
],
"description": "delete token by AccessToken",
"operationId": "ApiController.TokenLogout",
"parameters": [
{
"in": "query",
"name": "id_token_hint",
"description": "id_token_hint",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "post_logout_redirect_uri",
"description": "post_logout_redirect_uri",
"type": "string"
},
{
"in": "query",
"name": "state",
"description": "state",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/login/oauth/refresh_token": {
"post": {
"tags": [
@ -2471,6 +2669,26 @@
],
"description": "logout the current user",
"operationId": "ApiController.Logout",
"parameters": [
{
"in": "query",
"name": "id_token_hint",
"description": "id_token_hint",
"type": "string"
},
{
"in": "query",
"name": "post_logout_redirect_uri",
"description": "post_logout_redirect_uri",
"type": "string"
},
{
"in": "query",
"name": "state",
"description": "state",
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
@ -2486,6 +2704,26 @@
],
"description": "logout the current user",
"operationId": "ApiController.Logout",
"parameters": [
{
"in": "query",
"name": "id_token_hint",
"description": "id_token_hint",
"type": "string"
},
{
"in": "query",
"name": "post_logout_redirect_uri",
"description": "post_logout_redirect_uri",
"type": "string"
},
{
"in": "query",
"name": "state",
"description": "state",
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
@ -3267,11 +3505,11 @@
}
},
"definitions": {
"2200.0xc0003f8480.false": {
"2268.0xc0000f9650.false": {
"title": "false",
"type": "object"
},
"2235.0xc0003f84b0.false": {
"2302.0xc0000f9680.false": {
"title": "false",
"type": "object"
},
@ -3316,6 +3554,15 @@
"autoSignin": {
"type": "boolean"
},
"captchaToken": {
"type": "string"
},
"captchaType": {
"type": "string"
},
"clientSecret": {
"type": "string"
},
"code": {
"type": "string"
},
@ -3389,10 +3636,10 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/2200.0xc0003f8480.false"
"$ref": "#/definitions/2268.0xc0000f9650.false"
},
"data2": {
"$ref": "#/definitions/2235.0xc0003f84b0.false"
"$ref": "#/definitions/2302.0xc0000f9680.false"
},
"msg": {
"type": "string"
@ -3491,9 +3738,15 @@
"displayName": {
"type": "string"
},
"enableAutoSignin": {
"type": "boolean"
},
"enableCodeSignin": {
"type": "boolean"
},
"enableLinkWithEmail": {
"type": "boolean"
},
"enablePassword": {
"type": "boolean"
},
@ -3516,6 +3769,19 @@
"forgetUrl": {
"type": "string"
},
"formBackgroundUrl": {
"type": "string"
},
"formCss": {
"type": "string"
},
"formOffset": {
"type": "integer",
"format": "int64"
},
"formSideHtml": {
"type": "string"
},
"grantTypes": {
"type": "array",
"items": {
@ -3556,6 +3822,9 @@
"type": "integer",
"format": "int64"
},
"samlReplyUrl": {
"type": "string"
},
"signinHtml": {
"type": "string"
},
@ -3689,6 +3958,24 @@
}
}
},
"object.ManagedAccount": {
"title": "ManagedAccount",
"type": "object",
"properties": {
"application": {
"type": "string"
},
"password": {
"type": "string"
},
"signinUrl": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"object.Model": {
"title": "Model",
"type": "object",
@ -3726,6 +4013,9 @@
"type": "string"
}
},
"end_session_endpoint": {
"type": "string"
},
"grant_types_supported": {
"type": "array",
"items": {
@ -3801,6 +4091,9 @@
"createdTime": {
"type": "string"
},
"defaultApplication": {
"type": "string"
},
"defaultAvatar": {
"type": "string"
},
@ -3813,9 +4106,19 @@
"favicon": {
"type": "string"
},
"initScore": {
"type": "integer",
"format": "int64"
},
"isProfilePublic": {
"type": "boolean"
},
"languages": {
"type": "array",
"items": {
"type": "string"
}
},
"masterPassword": {
"type": "string"
},
@ -3943,12 +4246,27 @@
"type": "string"
}
},
"adapter": {
"type": "string"
},
"approveTime": {
"type": "string"
},
"approver": {
"type": "string"
},
"createdTime": {
"type": "string"
},
"displayName": {
"type": "string"
},
"domains": {
"type": "array",
"items": {
"type": "string"
}
},
"effect": {
"type": "string"
},
@ -3979,6 +4297,12 @@
"type": "string"
}
},
"state": {
"type": "string"
},
"submitter": {
"type": "string"
},
"users": {
"type": "array",
"items": {
@ -3997,6 +4321,9 @@
"currency": {
"type": "string"
},
"description": {
"type": "string"
},
"detail": {
"type": "string"
},
@ -4016,6 +4343,12 @@
"type": "number",
"format": "double"
},
"providerObjs": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Provider"
}
},
"providers": {
"type": "array",
"items": {
@ -4090,6 +4423,9 @@
"customUserInfoUrl": {
"type": "string"
},
"disableSsl": {
"type": "boolean"
},
"displayName": {
"type": "string"
},
@ -4126,6 +4462,9 @@
"owner": {
"type": "string"
},
"pathPrefix": {
"type": "string"
},
"port": {
"type": "integer",
"format": "int64"
@ -4133,6 +4472,9 @@
"providerUrl": {
"type": "string"
},
"receiver": {
"type": "string"
},
"regionId": {
"type": "string"
},
@ -4172,11 +4514,17 @@
"name": {
"type": "string"
},
"owner": {
"type": "string"
},
"prompted": {
"type": "boolean"
},
"provider": {
"$ref": "#/definitions/object.Provider"
},
"rule": {
"type": "string"
}
}
},
@ -4233,6 +4581,12 @@
"displayName": {
"type": "string"
},
"domains": {
"type": "array",
"items": {
"type": "string"
}
},
"isEnabled": {
"type": "boolean"
},
@ -4345,6 +4699,10 @@
}
}
},
"object.SystemInfo": {
"title": "SystemInfo",
"type": "object"
},
"object.TableColumn": {
"title": "TableColumn",
"type": "object",
@ -4605,15 +4963,27 @@
"lastSigninTime": {
"type": "string"
},
"lastSigninWrongTime": {
"type": "string"
},
"ldap": {
"type": "string"
},
"line": {
"type": "string"
},
"linkedin": {
"type": "string"
},
"location": {
"type": "string"
},
"managedAccounts": {
"type": "array",
"items": {
"$ref": "#/definitions/object.ManagedAccount"
}
},
"name": {
"type": "string"
},
@ -4669,6 +5039,10 @@
"type": "integer",
"format": "int64"
},
"signinWrongTimes": {
"type": "integer",
"format": "int64"
},
"signupApplication": {
"type": "string"
},
@ -4687,9 +5061,6 @@
"type": {
"type": "string"
},
"unionId": {
"type": "string"
},
"updatedTime": {
"type": "string"
},

View File

@ -177,6 +177,24 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-record:
post:
tags:
- Record API
description: add a record
operationId: ApiController.AddRecord
parameters:
- in: body
name: body
description: The details of the record
required: true
schema:
$ref: '#/definitions/object.Record'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-resource:
post:
tags:
@ -277,6 +295,11 @@ paths:
tags:
- Login API
operationId: ApiController.GetCaptcha
/api/api/get-webhook-event:
get:
tags:
- GetWebhookEventType API
operationId: ApiController.GetWebhookEventType
/api/api/reset-email-or-phone:
post:
tags:
@ -338,6 +361,11 @@ paths:
description: object
schema:
$ref: '#/definitions/Response'
/api/api/webhook:
post:
tags:
- HandleOfficialAccountEvent API
operationId: ApiController.HandleOfficialAccountEvent
/api/buy-product:
post:
tags:
@ -542,6 +570,25 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/delete-session:
post:
tags:
- Session API
description: Delete session by userId
operationId: ApiController.DeleteSession
parameters:
- in: query
name: ID
description: The ID(owner/name) of user.
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
type: string
/api/delete-syncer:
post:
tags:
@ -734,6 +781,23 @@ paths:
type: array
items:
$ref: '#/definitions/object.Cert'
/api/get-default-application:
get:
tags:
- Organization API
description: get default application
operationId: ApiController.GetDefaultApplication
parameters:
- in: query
name: id
description: organization id
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/Response'
/api/get-email-and-phone:
post:
tags:
@ -756,6 +820,19 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/get-global-providers:
get:
tags:
- Provider API
description: get Global providers
operationId: ApiController.GetGlobalProviders
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Provider'
/api/get-global-users:
get:
tags:
@ -947,6 +1024,38 @@ paths:
type: array
items:
$ref: '#/definitions/object.Permission'
/api/get-permissions-by-role:
get:
tags:
- Permission API
description: get permissions by role
operationId: ApiController.GetPermissionsByRole
parameters:
- in: query
name: id
description: The id of the role
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Permission'
/api/get-permissions-by-submitter:
get:
tags:
- Permission API
description: get permissions by submitter
operationId: ApiController.GetPermissionsBySubmitter
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Permission'
/api/get-product:
get:
tags:
@ -1060,6 +1169,15 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/object.Record'
/api/get-release:
get:
tags:
- System API
description: get local github repo's latest release version info
operationId: ApiController.GitRepoVersion
responses:
"200":
description: '{string} local latest version hash of casdoor'
/api/get-resource:
get:
tags:
@ -1106,6 +1224,25 @@ paths:
type: array
items:
$ref: '#/definitions/object.Role'
/api/get-sessions:
get:
tags:
- Session API
description: Get organization user sessions
operationId: ApiController.GetSessions
parameters:
- in: query
name: owner
description: The organization name
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
type: string
/api/get-sorted-users:
get:
tags:
@ -1170,6 +1307,23 @@ paths:
type: array
items:
$ref: '#/definitions/object.Syncer'
/api/get-system-info:
get:
tags:
- System API
description: get user's system info
operationId: ApiController.GetSystemInfo
parameters:
- in: query
name: id
description: The id of the user
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/object.SystemInfo'
/api/get-token:
get:
tags:
@ -1544,32 +1698,6 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/object.TokenError'
/api/login/oauth/logout:
get:
tags:
- Token API
description: delete token by AccessToken
operationId: ApiController.TokenLogout
parameters:
- in: query
name: id_token_hint
description: id_token_hint
required: true
type: string
- in: query
name: post_logout_redirect_uri
description: post_logout_redirect_uri
type: string
- in: query
name: state
description: state
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/login/oauth/refresh_token:
post:
tags:
@ -1620,6 +1748,19 @@ paths:
- Login API
description: logout the current user
operationId: ApiController.Logout
parameters:
- in: query
name: id_token_hint
description: id_token_hint
type: string
- in: query
name: post_logout_redirect_uri
description: post_logout_redirect_uri
type: string
- in: query
name: state
description: state
type: string
responses:
"200":
description: The Response object
@ -1630,6 +1771,19 @@ paths:
- Login API
description: logout the current user
operationId: ApiController.Logout
parameters:
- in: query
name: id_token_hint
description: id_token_hint
type: string
- in: query
name: post_logout_redirect_uri
description: post_logout_redirect_uri
type: string
- in: query
name: state
description: state
type: string
responses:
"200":
description: The Response object
@ -2139,10 +2293,10 @@ paths:
schema:
$ref: '#/definitions/Response'
definitions:
2200.0xc0003f8480.false:
2268.0xc0000f9650.false:
title: "false"
type: object
2235.0xc0003f84b0.false:
2302.0xc0000f9680.false:
title: "false"
type: object
Response:
@ -2174,6 +2328,12 @@ definitions:
type: string
autoSignin:
type: boolean
captchaToken:
type: string
captchaType:
type: string
clientSecret:
type: string
code:
type: string
email:
@ -2223,9 +2383,9 @@ definitions:
type: object
properties:
data:
$ref: '#/definitions/2200.0xc0003f8480.false'
$ref: '#/definitions/2268.0xc0000f9650.false'
data2:
$ref: '#/definitions/2235.0xc0003f84b0.false'
$ref: '#/definitions/2302.0xc0000f9680.false'
msg:
type: string
name:
@ -2291,8 +2451,12 @@ definitions:
type: string
displayName:
type: string
enableAutoSignin:
type: boolean
enableCodeSignin:
type: boolean
enableLinkWithEmail:
type: boolean
enablePassword:
type: boolean
enableSamlCompress:
@ -2308,6 +2472,15 @@ definitions:
format: int64
forgetUrl:
type: string
formBackgroundUrl:
type: string
formCss:
type: string
formOffset:
type: integer
format: int64
formSideHtml:
type: string
grantTypes:
type: array
items:
@ -2335,6 +2508,8 @@ definitions:
refreshExpireInHours:
type: integer
format: int64
samlReplyUrl:
type: string
signinHtml:
type: string
signinUrl:
@ -2424,6 +2599,18 @@ definitions:
type: string
username:
type: string
object.ManagedAccount:
title: ManagedAccount
type: object
properties:
application:
type: string
password:
type: string
signinUrl:
type: string
username:
type: string
object.Model:
title: Model
type: object
@ -2450,6 +2637,8 @@ definitions:
type: array
items:
type: string
end_session_endpoint:
type: string
grant_types_supported:
type: array
items:
@ -2500,6 +2689,8 @@ definitions:
$ref: '#/definitions/object.AccountItem'
createdTime:
type: string
defaultApplication:
type: string
defaultAvatar:
type: string
displayName:
@ -2508,8 +2699,15 @@ definitions:
type: boolean
favicon:
type: string
initScore:
type: integer
format: int64
isProfilePublic:
type: boolean
languages:
type: array
items:
type: string
masterPassword:
type: string
name:
@ -2595,10 +2793,20 @@ definitions:
type: array
items:
type: string
adapter:
type: string
approveTime:
type: string
approver:
type: string
createdTime:
type: string
displayName:
type: string
domains:
type: array
items:
type: string
effect:
type: string
isEnabled:
@ -2619,6 +2827,10 @@ definitions:
type: array
items:
type: string
state:
type: string
submitter:
type: string
users:
type: array
items:
@ -2631,6 +2843,8 @@ definitions:
type: string
currency:
type: string
description:
type: string
detail:
type: string
displayName:
@ -2644,6 +2858,10 @@ definitions:
price:
type: number
format: double
providerObjs:
type: array
items:
$ref: '#/definitions/object.Provider'
providers:
type: array
items:
@ -2694,6 +2912,8 @@ definitions:
type: string
customUserInfoUrl:
type: string
disableSsl:
type: boolean
displayName:
type: string
domain:
@ -2718,11 +2938,15 @@ definitions:
type: string
owner:
type: string
pathPrefix:
type: string
port:
type: integer
format: int64
providerUrl:
type: string
receiver:
type: string
regionId:
type: string
signName:
@ -2749,10 +2973,14 @@ definitions:
type: boolean
name:
type: string
owner:
type: string
prompted:
type: boolean
provider:
$ref: '#/definitions/object.Provider'
rule:
type: string
object.Record:
title: Record
type: object
@ -2790,6 +3018,10 @@ definitions:
type: string
displayName:
type: string
domains:
type: array
items:
type: string
isEnabled:
type: boolean
name:
@ -2864,6 +3096,9 @@ definitions:
type: string
user:
type: string
object.SystemInfo:
title: SystemInfo
type: object
object.TableColumn:
title: TableColumn
type: object
@ -3040,12 +3275,20 @@ definitions:
type: string
lastSigninTime:
type: string
lastSigninWrongTime:
type: string
ldap:
type: string
line:
type: string
linkedin:
type: string
location:
type: string
managedAccounts:
type: array
items:
$ref: '#/definitions/object.ManagedAccount'
name:
type: string
okta:
@ -3083,6 +3326,9 @@ definitions:
score:
type: integer
format: int64
signinWrongTimes:
type: integer
format: int64
signupApplication:
type: string
slack:
@ -3095,8 +3341,6 @@ definitions:
type: string
type:
type: string
unionId:
type: string
updatedTime:
type: string
webauthnCredentials:

View File

@ -53,7 +53,7 @@ func Test_IsTokenExpired(t *testing.T) {
for _, scenario := range []testCases{
{
description: "Token emited now is valid for 60 minutes",
description: "Token emitted now is valid for 60 minutes",
input: input{
createdTime: time.Now().Format(time.RFC3339),
expiresIn: 60,
@ -61,7 +61,7 @@ func Test_IsTokenExpired(t *testing.T) {
expected: false,
},
{
description: "Token emited 60 minutes before now is valid for 60 minutes",
description: "Token emitted 60 minutes before now is valid for 60 minutes",
input: input{
createdTime: time.Now().Add(-time.Minute * 60).Format(time.RFC3339),
expiresIn: 61,
@ -69,7 +69,7 @@ func Test_IsTokenExpired(t *testing.T) {
expected: false,
},
{
description: "Token emited 2 hours before now is Expired after 60 minutes",
description: "Token emitted 2 hours before now is Expired after 60 minutes",
input: input{
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
expiresIn: 60,
@ -77,7 +77,7 @@ func Test_IsTokenExpired(t *testing.T) {
expected: true,
},
{
description: "Token emited 61 minutes before now is Expired after 60 minutes",
description: "Token emitted 61 minutes before now is Expired after 60 minutes",
input: input{
createdTime: time.Now().Add(-time.Minute * 61).Format(time.RFC3339),
expiresIn: 60,
@ -85,7 +85,7 @@ func Test_IsTokenExpired(t *testing.T) {
expected: true,
},
{
description: "Token emited 2 hours before now is velid for 120 minutes",
description: "Token emitted 2 hours before now is valid for 120 minutes",
input: input{
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
expiresIn: 121,
@ -93,7 +93,7 @@ func Test_IsTokenExpired(t *testing.T) {
expected: false,
},
{
description: "Token emited 159 minutes before now is Expired after 60 minutes",
description: "Token emitted 159 minutes before now is Expired after 60 minutes",
input: input{
createdTime: time.Now().Add(-time.Minute * 159).Format(time.RFC3339),
expiresIn: 120,

View File

@ -343,12 +343,9 @@ class ApplicationEditPage extends React.Component {
{Setting.getLabel(i18next.t("application:Token format"), i18next.t("application:Token format - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenFormat} onChange={(value => {this.updateApplicationField("tokenFormat", value);})}>
{
["JWT", "JWT-Empty"]
.map((item, index) => <Option key={index} value={item}>{item}</Option>)
}
</Select>
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenFormat} onChange={(value => {this.updateApplicationField("tokenFormat", value);})}
options={["JWT", "JWT-Empty"].map((item) => Setting.getOption(item, item))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -606,6 +603,16 @@ class ApplicationEditPage extends React.Component {
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col span={(Setting.isMobile()) ? 19 : 6}>
{Setting.getLabel(i18next.t("application:Enable link accounts that with the same email"), i18next.t("application:Enable link accounts that with the same email - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.application.enableLinkWithEmail} onChange={checked => {
this.updateApplicationField("enableLinkWithEmail", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Preview"), i18next.t("general:Preview - Tooltip"))} :

View File

@ -153,7 +153,7 @@ export const CropperDiv = (props) => {
<Row style={{width: "100%", marginBottom: "20px"}}>
<input style={{display: "none"}} ref={input => uploadButton = input} type="file" accept="image/*" onChange={onChange} />
<Button block onClick={selectFile}>{i18next.t("user:Select a photo...")}</Button>
<Select
<Select virtual={false}
style={{width: "100%"}}
loading={loading}
placeholder={i18next.t("user:Please select avatar from resources")}

View File

@ -165,12 +165,9 @@ class OrganizationEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Password type"), i18next.t("general:Password type - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.passwordType} onChange={(value => {this.updateOrganizationField("passwordType", value);})}>
{
["plain", "salt", "md5-salt", "bcrypt", "pbkdf2-salt", "argon2id"]
.map((item, index) => <Option key={index} value={item}>{item}</Option>)
}
</Select>
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.passwordType} onChange={(value => {this.updateOrganizationField("passwordType", value);})}
options={["plain", "salt", "md5-salt", "bcrypt", "pbkdf2-salt", "argon2id"].map(item => Setting.getOption(item, item))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -225,11 +222,9 @@ class OrganizationEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Default application"), i18next.t("general:Default application - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.defaultApplication} onChange={(value => {this.updateOrganizationField("defaultApplication", value);})}>
{
this.state.applications?.map((item, index) => <Option key={index} value={item.name}>{item.name}</Option>)
}
</Select>
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.defaultApplication} onChange={(value => {this.updateOrganizationField("defaultApplication", value);})}
options={this.state.applications?.map((item) => Setting.getOption(item.name, item.name))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >

View File

@ -302,7 +302,7 @@ class PaymentEditPage extends React.Component {
{Setting.getLabel(i18next.t("payment:Invoice type"), i18next.t("payment:Invoice type - Tooltip"))} :
</Col>
<Col span={22} >
<Select disabled={this.state.payment.invoiceUrl !== ""} virtual={false} style={{width: "100%"}} value={this.state.payment.invoiceType} onChange={(value => {
<Select virtual={false} disabled={this.state.payment.invoiceUrl !== ""} style={{width: "100%"}} value={this.state.payment.invoiceType} onChange={(value => {
this.updatePaymentField("invoiceType", value);
if (value === "Individual") {
this.updatePaymentField("invoiceTitle", this.state.payment.personName);

View File

@ -24,8 +24,6 @@ import * as ModelBackend from "./backend/ModelBackend";
import * as ApplicationBackend from "./backend/ApplicationBackend";
import moment from "moment/moment";
const {Option} = Select;
class PermissionEditPage extends React.Component {
constructor(props) {
super(props);
@ -163,16 +161,13 @@ class PermissionEditPage extends React.Component {
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.permission.owner} onChange={(owner => {
this.updatePermissionField("owner", owner);
this.getUsers(owner);
this.getRoles(owner);
this.getModels(owner);
this.getResources(owner);
})}>
{
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
}
</Select>
})}
options={this.state.organizations.map((organization) => Setting.getOption(organization.name, organization.name))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -202,11 +197,9 @@ class PermissionEditPage extends React.Component {
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.permission.model} onChange={(model => {
this.updatePermissionField("model", model);
})}>
{
this.state.models.map((model, index) => <Option key={index} value={model.name}>{model.name}</Option>)
}
</Select>
})}
options={this.state.models.map((model) => Setting.getOption(model.name, model.name))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -224,11 +217,10 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.users} onChange={(value => {this.updatePermissionField("users", value);})}>
{
this.state.users.map((user, index) => <Option key={index} value={`${user.owner}/${user.name}`}>{`${user.owner}/${user.name}`}</Option>)
}
</Select>
<Select mode="tags" style={{width: "100%"}} value={this.state.permission.users}
onChange={(value => {this.updatePermissionField("users", value);})}
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -236,11 +228,10 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} disabled={!this.hasRoleDefinition(this.state.model)} mode="tags" style={{width: "100%"}} value={this.state.permission.roles} onChange={(value => {this.updatePermissionField("roles", value);})}>
{
this.state.roles.filter(roles => (roles.owner !== this.state.roles.owner || roles.name !== this.state.roles.name)).map((permission, index) => <Option key={index} value={`${permission.owner}/${permission.name}`}>{`${permission.owner}/${permission.name}`}</Option>)
}
</Select>
<Select virtual={false} disabled={!this.hasRoleDefinition(this.state.model)} mode="tags" style={{width: "100%"}} value={this.state.permission.roles}
onChange={(value => {this.updatePermissionField("roles", value);})}
options={this.state.roles.filter(roles => (roles.owner !== this.state.roles.owner || roles.name !== this.state.roles.name)).map((permission) => Setting.getOption(`${permission.owner}/${permission.name}`, `${permission.owner}/${permission.name}`))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -248,13 +239,12 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub domains"), i18next.t("role:Sub domains - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.domains} onChange={(value => {
this.updatePermissionField("domains", value);
})}>
{
this.state.permission.domains.map((domain, index) => <Option key={index} value={domain}>{domain}</Option>)
}
</Select>
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.domains}
onChange={(value => {
this.updatePermissionField("domains", value);
})}
options={this.state.permission.domains.map((domain) => Setting.getOption(domain, domain))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -264,14 +254,12 @@ class PermissionEditPage extends React.Component {
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.permission.resourceType} onChange={(value => {
this.updatePermissionField("resourceType", value);
})}>
{
[
{id: "Application", name: i18next.t("general:Application")},
{id: "TreeNode", name: i18next.t("permission:TreeNode")},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
})}
options={[
{value: "Application", name: i18next.t("general:Application")},
{value: "TreeNode", name: i18next.t("permission:TreeNode")},
].map((item) => Setting.getOption(item.name, item.value))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -279,11 +267,10 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("permission:Resources"), i18next.t("permission:Resources - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.resources} onChange={(value => {this.updatePermissionField("resources", value);})}>
{
this.state.resources.map((resource, index) => <Option key={index} value={`${resource.name}`}>{`${resource.name}`}</Option>)
}
</Select>
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.resources}
onChange={(value => {this.updatePermissionField("resources", value);})}
options={this.state.resources.map((resource) => Setting.getOption(`${resource.name}`, `${resource.name}`))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -293,15 +280,13 @@ class PermissionEditPage extends React.Component {
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.actions} onChange={(value => {
this.updatePermissionField("actions", value);
})}>
{
[
{id: "Read", name: i18next.t("permission:Read")},
{id: "Write", name: i18next.t("permission:Write")},
{id: "Admin", name: i18next.t("permission:Admin")},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
})}
options={[
{value: "Read", name: i18next.t("permission:Read")},
{value: "Write", name: i18next.t("permission:Write")},
{value: "Admin", name: i18next.t("permission:Admin")},
].map((item) => Setting.getOption(item.name, item.value))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -311,14 +296,12 @@ class PermissionEditPage extends React.Component {
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.permission.effect} onChange={(value => {
this.updatePermissionField("effect", value);
})}>
{
[
{id: "Allow", name: i18next.t("permission:Allow")},
{id: "Deny", name: i18next.t("permission:Deny")},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
})}
options={[
{value: "Allow", name: i18next.t("permission:Allow")},
{value: "Deny", name: i18next.t("permission:Deny")},
].map((item) => Setting.getOption(item.name, item.value))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -366,7 +349,7 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("permission:State"), i18next.t("permission:State - Tooltip"))} :
</Col>
<Col span={22} >
<Select disabled={!Setting.isLocalAdminUser(this.props.account)} virtual={false} style={{width: "100%"}} value={this.state.permission.state} onChange={(value => {
<Select virtual={false} disabled={!Setting.isLocalAdminUser(this.props.account)} style={{width: "100%"}} value={this.state.permission.state} onChange={(value => {
if (this.state.permission.state !== value) {
if (value === "Approved") {
this.updatePermissionField("approver", this.props.account.name);
@ -378,14 +361,12 @@ class PermissionEditPage extends React.Component {
}
this.updatePermissionField("state", value);
})}>
{
[
{id: "Approved", name: i18next.t("permission:Approved")},
{id: "Pending", name: i18next.t("permission:Pending")},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
})}
options={[
{value: "Approved", name: i18next.t("permission:Approved")},
{value: "Pending", name: i18next.t("permission:Pending")},
].map((item) => Setting.getOption(item.name, item.value))}
/>
</Col>
</Row>
</Card>

View File

@ -179,7 +179,7 @@ class ProviderTable extends React.Component {
// width: '120px',
// render: (text, record, index) => {
// return (
// <Select virtual={false} style={{width: '100%'}} value={text} onChange={(value => {
// <Select virtual={false} style={{width: '100%'}} value={text} onChange={(value => {
// this.updateField(table, index, 'alertType', value);
// })}>
// {

View File

@ -16,11 +16,9 @@ import React from "react";
import {Button, Card, Col, Input, Row, Select, Switch} from "antd";
import * as RoleBackend from "./backend/RoleBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as UserBackend from "./backend/UserBackend";
import * as Setting from "./Setting";
import i18next from "i18next";
const {Option} = Select;
import * as UserBackend from "./backend/UserBackend";
class RoleEditPage extends React.Component {
constructor(props) {
@ -113,11 +111,9 @@ class RoleEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.role.owner} onChange={(value => {this.updateRoleField("owner", value);})}>
{
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
}
</Select>
<Select virtual={false} style={{width: "100%"}} value={this.state.role.owner} onChange={(value => {this.updateRoleField("owner", value);})}
options={this.state.organizations.map((organization) => Setting.getOption(organization.name, organization.name))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -145,11 +141,10 @@ class RoleEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.role.users} onChange={(value => {this.updateRoleField("users", value);})}>
{
this.state.users.map((user, index) => <Option key={index} value={`${user.owner}/${user.name}`}>{`${user.owner}/${user.name}`}</Option>)
}
</Select>
<Select mode="tags" style={{width: "100%"}} value={this.state.role.users}
onChange={(value => {this.updateRoleField("users", value);})}
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -157,11 +152,9 @@ class RoleEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.role.roles} onChange={(value => {this.updateRoleField("roles", value);})}>
{
this.state.roles.filter(role => (role.owner !== this.state.role.owner || role.name !== this.state.role.name)).map((role, index) => <Option key={index} value={`${role.owner}/${role.name}`}>{`${role.owner}/${role.name}`}</Option>)
}
</Select>
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.role.roles} onChange={(value => {this.updateRoleField("roles", value);})}
options={this.state.roles.filter(role => (role.owner !== this.state.role.owner || role.name !== this.state.role.name)).map((role) => Setting.getOption(`${role.owner}/${role.name}`, `${role.owner}/${role.name}`))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
@ -171,11 +164,9 @@ class RoleEditPage extends React.Component {
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.role.domains} onChange={(value => {
this.updateRoleField("domains", value);
})}>
{
this.state.role.domains?.map((domain, index) => <Option key={index} value={domain}>{domain}</Option>)
}
</Select>
})}
options={this.state.role.domains?.map((domain) => Setting.getOption(domain, domain))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >

View File

@ -924,6 +924,14 @@ export function getItem(label, key, icon, children, type) {
type,
};
}
export function getOption(label, value) {
return {
label,
value,
};
}
function repeat(str, len) {
while (str.length < len) {
str += str.substr(0, len - str.length);

View File

@ -195,11 +195,7 @@ class SignupTable extends React.Component {
return (
<Select virtual={false} style={{width: "100%"}} value={text} onChange={(value => {
this.updateField(table, index, "rule", value);
})}>
{
options.map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
})} options={options.map(item => Setting.getOption(item.name, item.id))} />
);
},
},

View File

@ -257,12 +257,9 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:User type"), i18next.t("general:User type - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.user.type} onChange={(value => {this.updateUserField("type", value);})}>
{
["normal-user"]
.map((item, index) => <Option key={index} value={item}>{item}</Option>)
}
</Select>
<Select virtual={false} style={{width: "100%"}} value={this.state.user.type} onChange={(value => {this.updateUserField("type", value);})}
options={["normal-user"].map(item => Setting.getOption(item, item))}
/>
</Col>
</Row>
);
@ -290,7 +287,7 @@ class UserEditPage extends React.Component {
onChange={e => {
this.updateUserField("email", e.target.value);
}} />) :
(<Select value={this.state.user.email}
(<Select virtual={false} value={this.state.user.email}
options={[Setting.getItem(this.state.user.email, this.state.user.email)]}
disabled={disabled}
onChange={e => {
@ -317,7 +314,7 @@ class UserEditPage extends React.Component {
onChange={e => {
this.updateUserField("phone", e.target.value);
}} /> :
(<Select value={`+${this.state.application?.organizationObj.phonePrefix} ${this.state.user.phone}`}
(<Select virtual={false} value={`+${this.state.application?.organizationObj.phonePrefix} ${this.state.user.phone}`}
options={[Setting.getItem(`+${this.state.application?.organizationObj.phonePrefix} ${this.state.user.phone}`, this.state.user.phone)]}
disabled={disabled}
onChange={e => {
@ -409,16 +406,14 @@ class UserEditPage extends React.Component {
<Col span={22} >
{
this.state.application?.organizationObj.tags?.length > 0 ? (
<Select virtual={false} style={{width: "100%"}} value={this.state.user.tag} onChange={(value => {this.updateUserField("tag", value);})}>
{
this.state.application.organizationObj.tags?.map((tag, index) => {
const tokens = tag.split("|");
const value = tokens[0];
const displayValue = Setting.getLanguage() !== "zh" ? tokens[0] : tokens[1];
return <Option key={index} value={value}>{displayValue}</Option>;
})
}
</Select>
<Select virtual={false} style={{width: "100%"}} value={this.state.user.tag}
onChange={(value => {this.updateUserField("tag", value);})}
options={this.state.application.organizationObj.tags?.map((tag) => {
const tokens = tag.split("|");
const value = tokens[0];
const displayValue = Setting.getLanguage() !== "zh" ? tokens[0] : tokens[1];
return Setting.getOption(displayValue, value);
})} />
) : (
<Input value={this.state.user.tag} onChange={e => {
this.updateUserField("tag", e.target.value);
@ -435,11 +430,10 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Signup application"), i18next.t("general:Signup application - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} disabled={disabled} value={this.state.user.signupApplication} onChange={(value => {this.updateUserField("signupApplication", value);})}>
{
this.state.applications.map((application, index) => <Option key={index} value={application.name}>{application.name}</Option>)
}
</Select>
<Select virtual={false} style={{width: "100%"}} disabled={disabled} value={this.state.user.signupApplication}
onChange={(value => {this.updateUserField("signupApplication", value);})}
options={this.state.applications.map((application) => Setting.getOption(application.name, application.name))
} />
</Col>
</Row>
);
@ -480,7 +474,7 @@ class UserEditPage extends React.Component {
<div style={{marginBottom: 20}}>
{
(this.state.application === null || this.state.user === null) ? null : (
this.state.application?.providers.filter(providerItem => Setting.isProviderVisible(providerItem)).map((providerItem, index) =>
this.state.application?.providers.filter(providerItem => Setting.isProviderVisible(providerItem)).map((providerItem) =>
(providerItem.provider.category === "OAuth") ? (
<OAuthWidget key={providerItem.name} labelSpan={(Setting.isMobile()) ? 10 : 3} user={this.state.user} application={this.state.application} providerItem={providerItem} account={this.props.account} onUnlinked={() => {return this.unlinked();}} />
) : (

View File

@ -320,9 +320,9 @@ class ForgetPage extends React.Component {
>
{
this.state.isFixed ? <Input disabled /> :
<Select
<Select virtual={false}
key={this.state.verifyType}
virtual={false} style={{textAlign: "left"}}
style={{textAlign: "left"}}
defaultValue={this.state.verifyType}
disabled={this.state.username === ""}
placeholder={i18next.t("forget:Choose email or phone")}

View File

@ -18,8 +18,6 @@ import i18next from "i18next";
import * as UserBackend from "../backend/UserBackend";
import * as Setting from "../Setting";
const {Option} = Select;
class AffiliationSelect extends React.Component {
constructor(props) {
super(props);
@ -99,20 +97,16 @@ class AffiliationSelect extends React.Component {
this.updateUserField("affiliation", e.target.value);
}} />
) : (
<Select virtual={false} style={{width: "100%"}} value={this.props.user.affiliation} onChange={(value => {
const name = value;
const affiliationOption = Setting.getArrayItem(this.state.affiliationOptions, "name", name);
const id = affiliationOption.id;
this.updateUserField("affiliation", name);
this.updateUserField("score", id);
})}>
{
<Option key={0} value={""}>(empty)</Option>
}
{
this.state.affiliationOptions.map((affiliationOption, index) => <Option key={affiliationOption.id} value={affiliationOption.name}>{affiliationOption.name}</Option>)
}
</Select>
<Select virtual={false} style={{width: "100%"}} value={this.props.user.affiliation}
onChange={(value => {
const name = value;
const affiliationOption = Setting.getArrayItem(this.state.affiliationOptions, "name", name);
const id = affiliationOption.id;
this.updateUserField("affiliation", name);
this.updateUserField("score", id);
})}
options={[Setting.getOption("(empty)", "")].concat(this.state.affiliationOptions.map((affiliationOption) => Setting.getOption(affiliationOption.name, affiliationOption.name))
)} />
)
}
</Col>

View File

@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Code-Anmeldung aktivieren",
"Enable code signin - Tooltip": "Aktiviere Codeanmeldung - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Aktiviere Anmeldesession - Tooltip",
"Enable signup": "Anmeldung aktivieren",

View File

@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Enable signup",

View File

@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Habilitar inicio de sesión de WebAuthn - Tooltip",
"Enable code signin": "Habilitar inicio de sesión por código",
"Enable code signin - Tooltip": "Habilitar inicio de sesión por código - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Habilitar nuevos registros",

View File

@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Activer la connexion au code",
"Enable code signin - Tooltip": "Activer la connexion au code - infobulle",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Activer la session de connexion - infobulle",
"Enable signup": "Activer l'inscription",

View File

@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "コードサインインを有効にする",
"Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "サインアップを有効にする",

View File

@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Enable signup",

View File

@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Включить вход с WebAuthn - Подсказка",
"Enable code signin": "Включить кодовый вход",
"Enable code signin - Tooltip": "Включить вход с кодом - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Включить сеанс входа - Подсказка",
"Enable signup": "Включить регистрацию",

View File

@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "是否支持用户在登录页面通过WebAuthn方式登录",
"Enable code signin": "启用验证码登录",
"Enable code signin - Tooltip": "是否允许用手机或邮箱验证码登录",
"Enable link accounts that with the same email": "自动关联邮箱相同的账号",
"Enable link accounts that with the same email - Tooltip": "使用第三方授权登录时,如果组织中存在与授权用户邮箱相同的用户,会自动关联该第三方登录方式到该用户",
"Enable side panel": "启用侧面板",
"Enable signin session - Tooltip": "从应用登录Casdoor后Casdoor是否保持会话",
"Enable signup": "启用注册",