Compare commits

...

3 Commits

Author SHA1 Message Date
Yaodong Yu
950a274b23 fix: region don't display in userEditPage (#1544) 2023-02-12 18:56:56 +08:00
Yang Luo
478bd05db4 Improve error handling in migrator 2023-02-12 10:39:20 +08:00
Zayn Xie
9256791420 feat: app session control and db migrate (#1539)
* feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process (#1533)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774)

* feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process

* feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process

* feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process

---------

Co-authored-by: Zayn Xie <84443886+xiaoniuren99@users.noreply.github.com>

* fix: migrate err

* fix: migrate err

* feat: app session control and db migrate

* feat: app session control and db migrate

* feat: app session control and db migrate

---------

Co-authored-by: Zayn Xie <84443886+xiaoniuren99@users.noreply.github.com>
2023-02-12 09:33:24 +08:00
54 changed files with 622 additions and 248 deletions

View File

@@ -20,9 +20,9 @@ import (
"github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
xormadapter "github.com/casdoor/xorm-adapter/v3"
stringadapter "github.com/qiangmzsx/string-adapter/v2" stringadapter "github.com/qiangmzsx/string-adapter/v2"
) )

View File

@@ -254,7 +254,8 @@ func (c *ApiController) Logout() {
if accessToken == "" && redirectUri == "" { if accessToken == "" && redirectUri == "" {
c.ClearUserSession() c.ClearUserSession()
object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID()) // TODO https://github.com/casdoor/casdoor/pull/1494#discussion_r1095675265
object.DeleteSessionId(util.GetSessionId(object.CasdoorOrganization, object.CasdoorApplication, user), c.Ctx.Input.CruSession.SessionID())
util.LogInfo(c.Ctx, "API: [%s] logged out", user) util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication() application := c.GetSessionApplication()
@@ -291,7 +292,8 @@ func (c *ApiController) Logout() {
} }
c.ClearUserSession() c.ClearUserSession()
object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID()) // TODO https://github.com/casdoor/casdoor/pull/1494#discussion_r1095675265
object.DeleteSessionId(util.GetSessionId(object.CasdoorOrganization, object.CasdoorApplication, user), c.Ctx.Input.CruSession.SessionID())
util.LogInfo(c.Ctx, "API: [%s] logged out", user) util.LogInfo(c.Ctx, "API: [%s] logged out", user)
c.Ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?state=%s", strings.TrimRight(redirectUri, "/"), state)) c.Ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?state=%s", strings.TrimRight(redirectUri, "/"), state))

View File

@@ -139,8 +139,13 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
}) })
} }
if resp.Status == "ok" { if resp.Status == "ok" && user.Owner == object.CasdoorOrganization && application.Name == object.CasdoorApplication {
object.SetSession(user.GetId(), c.Ctx.Input.CruSession.SessionID()) object.AddSession(&object.Session{
Owner: user.Owner,
Name: user.Name,
Application: application.Name,
SessionId: []string{c.Ctx.Input.CruSession.SessionID()},
})
} }
return resp return resp

View File

@@ -18,9 +18,9 @@ import (
"encoding/json" "encoding/json"
"github.com/beego/beego/utils/pagination" "github.com/beego/beego/utils/pagination"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
) )
func (c *ApiController) GetCasbinAdapters() { func (c *ApiController) GetCasbinAdapters() {

View File

@@ -22,29 +22,10 @@ import (
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
// DeleteSession
// @Title DeleteSession
// @Tag Session API
// @Description Delete session by userId
// @Param id query string true "The id ( owner/name )(owner/name) of user."
// @Success 200 {array} string The Response object
// @router /delete-session [post]
func (c *ApiController) DeleteSession() {
var session object.Session
err := json.Unmarshal(c.Ctx.Input.RequestBody, &session)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteSession(util.GetId(session.Owner, session.Name)))
c.ServeJSON()
}
// GetSessions // GetSessions
// @Title GetSessions // @Title GetSessions
// @Tag Session API // @Tag Session API
// @Description Get organization user sessions // @Description Get organization user sessions.
// @Param owner query string true "The organization name" // @Param owner query string true "The organization name"
// @Success 200 {array} string The Response object // @Success 200 {array} string The Response object
// @router /get-sessions [get] // @router /get-sessions [get]
@@ -66,3 +47,93 @@ func (c *ApiController) GetSessions() {
c.ResponseOk(sessions, paginator.Nums()) c.ResponseOk(sessions, paginator.Nums())
} }
} }
// GetSingleSession
// @Title GetSingleSession
// @Tag Session API
// @Description Get session for one user in one application.
// @Param id query string true "The id(organization/application/user) of session"
// @Success 200 {array} string The Response object
// @router /get-session [get]
func (c *ApiController) GetSingleSession() {
id := c.Input().Get("sessionPkId")
c.Data["json"] = object.GetSingleSession(id)
c.ServeJSON()
}
// UpdateSession
// @Title UpdateSession
// @Tag Session API
// @Description Update session for one user in one application.
// @Param id query string true "The id(organization/application/user) of session"
// @Success 200 {array} string The Response object
// @router /update-session [post]
func (c *ApiController) UpdateSession() {
var session object.Session
err := json.Unmarshal(c.Ctx.Input.RequestBody, &session)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateSession(util.GetSessionId(session.Owner, session.Name, session.Application), &session))
c.ServeJSON()
}
// AddSession
// @Title AddSession
// @Tag Session API
// @Description Add session for one user in one application. If there are other existing sessions, join the session into the list.
// @Param id query string true "The id(organization/application/user) of session"
// @Param sessionId query string true "sessionId to be added"
// @Success 200 {array} string The Response object
// @router /add-session [post]
func (c *ApiController) AddSession() {
var session object.Session
err := json.Unmarshal(c.Ctx.Input.RequestBody, &session)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddSession(&session))
c.ServeJSON()
}
// DeleteSession
// @Title DeleteSession
// @Tag Session API
// @Description Delete session for one user in one application.
// @Param id query string true "The id(organization/application/user) of session"
// @Success 200 {array} string The Response object
// @router /delete-session [post]
func (c *ApiController) DeleteSession() {
var session object.Session
err := json.Unmarshal(c.Ctx.Input.RequestBody, &session)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteSession(util.GetSessionId(session.Owner, session.Name, session.Application)))
c.ServeJSON()
}
// IsSessionDuplicated
// @Title IsSessionDuplicated
// @Tag Session API
// @Description Check if there are other different sessions for one user in one application.
// @Param id query string true "The id(organization/application/user) of session"
// @Param sessionId query string true "sessionId to be checked"
// @Success 200 {array} string The Response object
// @router /is-user-session-duplicated [get]
func (c *ApiController) IsSessionDuplicated() {
id := c.Input().Get("sessionPkId")
sessionId := c.Input().Get("sessionId")
isUserSessionDuplicated := object.IsSessionDuplicated(id, sessionId)
c.Data["json"] = &Response{Status: "ok", Msg: "", Data: isUserSessionDuplicated}
c.ServeJSON()
}

7
go.mod
View File

@@ -9,10 +9,10 @@ require (
github.com/beego/beego v1.12.11 github.com/beego/beego v1.12.11
github.com/beevik/etree v1.1.0 github.com/beevik/etree v1.1.0
github.com/casbin/casbin/v2 v2.30.1 github.com/casbin/casbin/v2 v2.30.1
github.com/casbin/xorm-adapter/v3 v3.0.1
github.com/casdoor/go-sms-sender v0.5.1 github.com/casdoor/go-sms-sender v0.5.1
github.com/casdoor/gomail/v2 v2.0.1 github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/oss v1.2.0 github.com/casdoor/oss v1.2.0
github.com/casdoor/xorm-adapter/v3 v3.0.4
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
github.com/denisenkom/go-mssqldb v0.9.0 github.com/denisenkom/go-mssqldb v0.9.0
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b
@@ -41,6 +41,8 @@ require (
github.com/tealeg/xlsx v1.0.5 github.com/tealeg/xlsx v1.0.5
github.com/thanhpk/randstr v1.0.4 github.com/thanhpk/randstr v1.0.4
github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/xorm-io/core v0.7.4
github.com/xorm-io/xorm v1.1.6
github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 golang.org/x/crypto v0.0.0-20220214200702-86341886e292
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
@@ -50,7 +52,4 @@ require (
gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84 modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84
xorm.io/builder v0.3.12 // indirect
xorm.io/core v0.7.3
xorm.io/xorm v1.1.2
) )

24
go.sum
View File

@@ -58,7 +58,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b h1:EgJ6N2S0h1WfFIjU5/VVHWbMSVYXAluop97Qxpr/lfQ= github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b h1:EgJ6N2S0h1WfFIjU5/VVHWbMSVYXAluop97Qxpr/lfQ=
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b/go.mod h1:3SAoF0F5EbcOuBD5WT9nYkbIJieBS84cUQXADbXeBsU= github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b/go.mod h1:3SAoF0F5EbcOuBD5WT9nYkbIJieBS84cUQXADbXeBsU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -73,7 +72,6 @@ github.com/aliyun/alibaba-cloud-sdk-go v1.61.1075 h1:Z0SzZttfYI/raZ5O9WF3cezZJTS
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1075/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1075/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible h1:9gWa46nstkJ9miBReJcN8Gq34cBFbzSpQZVVT9N09TM= github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible h1:9gWa46nstkJ9miBReJcN8Gq34cBFbzSpQZVVT9N09TM=
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.44.4 h1:ePN0CVJMdiz2vYUcJH96eyxRrtKGSDMgyhP6rah2OgE= github.com/aws/aws-sdk-go v1.44.4 h1:ePN0CVJMdiz2vYUcJH96eyxRrtKGSDMgyhP6rah2OgE=
github.com/aws/aws-sdk-go v1.44.4/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.44.4/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
@@ -96,14 +94,14 @@ github.com/casbin/casbin/v2 v2.1.0/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n
github.com/casbin/casbin/v2 v2.28.3/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/casbin/casbin/v2 v2.28.3/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/casbin/v2 v2.30.1 h1:P5HWadDL7olwUXNdcuKUBk+x75Y2eitFxYTcLNKeKF0= github.com/casbin/casbin/v2 v2.30.1 h1:P5HWadDL7olwUXNdcuKUBk+x75Y2eitFxYTcLNKeKF0=
github.com/casbin/casbin/v2 v2.30.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/casbin/casbin/v2 v2.30.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/xorm-adapter/v3 v3.0.1 h1:0l0zkYxo6cNuIdrBZgFxlje1TRvmheYa/zIp+sGPK58=
github.com/casbin/xorm-adapter/v3 v3.0.1/go.mod h1:1BL7rHEDXrxO+vQdSo/ZaWKRivXl7YTos67GdMYcd20=
github.com/casdoor/go-sms-sender v0.5.1 h1:1/Wp1OLkVAVY4lEGQhekSNetSAWhnPcxYPV7xpCZgC0= github.com/casdoor/go-sms-sender v0.5.1 h1:1/Wp1OLkVAVY4lEGQhekSNetSAWhnPcxYPV7xpCZgC0=
github.com/casdoor/go-sms-sender v0.5.1/go.mod h1:kBykbqwgRDXbXdMAIxmZKinVM1WjdqEbej5LAbUbcfI= github.com/casdoor/go-sms-sender v0.5.1/go.mod h1:kBykbqwgRDXbXdMAIxmZKinVM1WjdqEbej5LAbUbcfI=
github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w= github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w=
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q= github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
github.com/casdoor/oss v1.2.0 h1:ozLAE+nnNdFQBWbzH8U9spzaO8h8NrB57lBcdyMUUQ8= github.com/casdoor/oss v1.2.0 h1:ozLAE+nnNdFQBWbzH8U9spzaO8h8NrB57lBcdyMUUQ8=
github.com/casdoor/oss v1.2.0/go.mod h1:qii35VBuxnR/uEuYSKpS0aJ8htQFOcCVsZ4FHgHLuss= github.com/casdoor/oss v1.2.0/go.mod h1:qii35VBuxnR/uEuYSKpS0aJ8htQFOcCVsZ4FHgHLuss=
github.com/casdoor/xorm-adapter/v3 v3.0.4 h1:vB04Ao8n2jA7aFBI9F+gGXo9+Aa1IQP6mTdo50913DM=
github.com/casdoor/xorm-adapter/v3 v3.0.4/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -128,7 +126,6 @@ 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/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 h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= 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/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk= 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/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 h1:L63RATZFZuFMXy6ixnKmv3eNAXwYQF6HW1vd4IYsQqQ=
@@ -338,7 +335,6 @@ github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 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-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.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 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 h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -457,6 +453,12 @@ github.com/volcengine/volc-sdk-golang v1.0.19/go.mod h1:+GGi447k4p1I5PNdbpG2GLaF
github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xorm-io/builder v0.3.13 h1:J4oZxt4Gjgm/Si9iKazfzYwHB/ijEOD9EHInyjOSX+M=
github.com/xorm-io/builder v0.3.13/go.mod h1:24o5riRwzre2WvjmN+LM4YpUtJg7W8MdvJ8H57rvrJA=
github.com/xorm-io/core v0.7.4 h1:qIznlqqmYNEb03ewzRXCrNkbbxpkgc/44nVF8yoFV7Y=
github.com/xorm-io/core v0.7.4/go.mod h1:GueyhafDnkB0KK0fXX/dEhr/P1EAGW0GLmoNDUEE1Mo=
github.com/xorm-io/xorm v1.1.6 h1:s4fDpUXJx8Zr/PBovXNaadn+v1P3h/U3iV4OxAkWS8s=
github.com/xorm-io/xorm v1.1.6/go.mod h1:7nsSUdmgLIcqHSSaKOzbVQiZtzIzbpGf1GGSYp6DD70=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -521,7 +523,6 @@ 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.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 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -846,12 +847,3 @@ 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/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/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 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.3 h1:W8ws1PlrnkS1CZU1YWaYLMQcQilwAmQXU0BJDJon+H0=
xorm.io/core v0.7.3/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
xorm.io/xorm v1.0.3/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

@@ -35,7 +35,10 @@ func main() {
createDatabase := flag.Bool("createDatabase", false, "true if you need Casdoor to create database") createDatabase := flag.Bool("createDatabase", false, "true if you need Casdoor to create database")
flag.Parse() flag.Parse()
object.InitAdapter(*createDatabase) object.InitAdapter()
object.DoMigration()
object.CreateTables(*createDatabase)
object.InitDb() object.InitDb()
object.InitFromFile() object.InitFromFile()
object.InitDefaultStorageProvider() object.InitDefaultStorageProvider()

View File

@@ -19,15 +19,15 @@ import (
"runtime" "runtime"
"github.com/beego/beego" "github.com/beego/beego"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
_ "github.com/denisenkom/go-mssqldb" // db = mssql _ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/go-sql-driver/mysql" // db = mysql _ "github.com/go-sql-driver/mysql" // db = mysql
_ "github.com/lib/pq" // db = postgres _ "github.com/lib/pq" // db = postgres
_ "modernc.org/sqlite" // db = sqlite "github.com/xorm-io/core"
"xorm.io/core" "github.com/xorm-io/xorm"
"xorm.io/xorm" _ "modernc.org/sqlite" // db = sqlite
) )
var adapter *Adapter var adapter *Adapter
@@ -40,12 +40,16 @@ func InitConfig() {
beego.BConfig.WebConfig.Session.SessionOn = true beego.BConfig.WebConfig.Session.SessionOn = true
InitAdapter(true) InitAdapter()
MigrateDatabase() DoMigration()
CreateTables(true)
} }
func InitAdapter(createDatabase bool) { func InitAdapter() {
adapter = NewAdapter(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName")) adapter = NewAdapter(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))
}
func CreateTables(createDatabase bool) {
if createDatabase { if createDatabase {
adapter.CreateDatabase() adapter.CreateDatabase()
} }

View File

@@ -21,7 +21,7 @@ import (
"github.com/casdoor/casdoor/idp" "github.com/casdoor/casdoor/idp"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type SignupItem struct { type SignupItem struct {

View File

@@ -20,9 +20,9 @@ import (
"github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" xormadapter "github.com/casdoor/xorm-adapter/v3"
"github.com/xorm-io/core"
) )
type CasbinAdapter struct { type CasbinAdapter struct {

View File

@@ -18,7 +18,7 @@ import (
"fmt" "fmt"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type Cert struct { type Cert struct {

View File

@@ -34,8 +34,6 @@ func InitDb() {
initBuiltInApplication() initBuiltInApplication()
initBuiltInCert() initBuiltInCert()
initBuiltInLdap() initBuiltInLdap()
} else {
MigrateDatabase()
} }
initWebAuthn() initWebAuthn()

47
object/migrator.go Normal file
View File

@@ -0,0 +1,47 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import "github.com/xorm-io/xorm/migrate"
type Migrator interface {
IsMigrationNeeded() bool
DoMigration() *migrate.Migration
}
func DoMigration() {
migrators := []Migrator{
&Migrator_1_101_0_PR_1083{},
&Migrator_1_235_0_PR_1530{},
&Migrator_1_240_0_PR_1539{},
// more migrators add here in chronological order...
}
migrations := []*migrate.Migration{}
for _, migrator := range migrators {
if migrator.IsMigrationNeeded() {
migrations = append(migrations, migrator.DoMigration())
}
}
options := &migrate.Options{
TableName: "migration",
IDColumnName: "id",
}
m := migrate.New(adapter.Engine, options, migrations)
m.Migrate()
}

View File

@@ -10,51 +10,36 @@
// distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License.package object // limitations under the License.
package object package object
import ( import (
"strings" "strings"
xormadapter "github.com/casbin/xorm-adapter/v3" "github.com/xorm-io/xorm"
"xorm.io/xorm" "github.com/xorm-io/xorm/migrate"
"xorm.io/xorm/migrate"
) )
func MigrateDatabase() { type Migrator_1_101_0_PR_1083 struct{}
migrations := []*migrate.Migration{
MigrateCasbinRule(),
MigratePermissionRule(),
}
m := migrate.New(adapter.Engine, migrate.DefaultOptions, migrations) func (*Migrator_1_101_0_PR_1083) IsMigrationNeeded() bool {
m.Migrate() exist1, _ := adapter.Engine.IsTableExist("model")
exist2, _ := adapter.Engine.IsTableExist("permission")
exist3, _ := adapter.Engine.IsTableExist("permission_rule")
if exist1 && exist2 && exist3 {
return true
}
return false
} }
func MigrateCasbinRule() *migrate.Migration { func (*Migrator_1_101_0_PR_1083) DoMigration() *migrate.Migration {
migration := migrate.Migration{
ID: "20221015CasbinRule--fill ptype field with p",
Migrate: func(engine *xorm.Engine) error {
_, err := engine.Cols("ptype").Update(&xormadapter.CasbinRule{
Ptype: "p",
})
return err
},
Rollback: func(engine *xorm.Engine) error {
return engine.DropTables(&xormadapter.CasbinRule{})
},
}
return &migration
}
func MigratePermissionRule() *migrate.Migration {
migration := migrate.Migration{ migration := migrate.Migration{
ID: "20230209MigratePermissionRule--Use V5 instead of V1 to store permissionID", ID: "20230209MigratePermissionRule--Use V5 instead of V1 to store permissionID",
Migrate: func(engine *xorm.Engine) error { Migrate: func(engine *xorm.Engine) error {
models := []*Model{} models := []*Model{}
err := engine.Find(&models, &Model{}) err := engine.Table("model").Find(&models, &Model{})
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@@ -0,0 +1,49 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
xormadapter "github.com/casdoor/xorm-adapter/v3"
"github.com/xorm-io/xorm"
"github.com/xorm-io/xorm/migrate"
)
type Migrator_1_235_0_PR_1530 struct{}
func (*Migrator_1_235_0_PR_1530) IsMigrationNeeded() bool {
exist, _ := adapter.Engine.IsTableExist("casbin_rule")
if exist {
return true
}
return false
}
func (*Migrator_1_235_0_PR_1530) DoMigration() *migrate.Migration {
migration := migrate.Migration{
ID: "20221015CasbinRule--fill ptype field with p",
Migrate: func(engine *xorm.Engine) error {
_, err := engine.Cols("ptype").Update(&xormadapter.CasbinRule{
Ptype: "p",
})
return err
},
Rollback: func(engine *xorm.Engine) error {
return engine.DropTables(&xormadapter.CasbinRule{})
},
}
return &migration
}

View File

@@ -0,0 +1,141 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"errors"
"github.com/xorm-io/xorm"
"github.com/xorm-io/xorm/migrate"
)
type Migrator_1_240_0_PR_1539 struct{}
func (*Migrator_1_240_0_PR_1539) IsMigrationNeeded() bool {
exist, _ := adapter.Engine.IsTableExist("session")
err := adapter.Engine.Table("session").Find(&[]*Session{})
if exist && err != nil {
return true
}
return false
}
func (*Migrator_1_240_0_PR_1539) DoMigration() *migrate.Migration {
migration := migrate.Migration{
ID: "20230211MigrateSession--Create a new field 'application' for table `session`",
Migrate: func(engine *xorm.Engine) error {
if alreadyCreated, _ := engine.IsTableExist("session_tmp"); alreadyCreated {
return errors.New("there is already a table called 'session_tmp', please rename or delete it for casdoor version migration and restart")
}
type oldSession struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
SessionId []string `json:"sessionId"`
}
tx := engine.NewSession()
defer tx.Close()
err := tx.Begin()
if err != nil {
return err
}
err = tx.Table("session_tmp").CreateTable(&Session{})
if err != nil {
return err
}
oldSessions := []*oldSession{}
newSessions := []*Session{}
err = tx.Table("session").Find(&oldSessions)
if err != nil {
return err
}
for _, oldSession := range oldSessions {
newApplication := "null"
if oldSession.Owner == "built-in" {
newApplication = "app-built-in"
}
newSessions = append(newSessions, &Session{
Owner: oldSession.Owner,
Name: oldSession.Name,
Application: newApplication,
CreatedTime: oldSession.CreatedTime,
SessionId: oldSession.SessionId,
})
}
rollbackFlag := false
_, err = tx.Table("session_tmp").Insert(newSessions)
count1, _ := tx.Table("session_tmp").Count()
count2, _ := tx.Table("session").Count()
if err != nil || count1 != count2 {
rollbackFlag = true
}
delete := &Session{
Application: "null",
}
_, err = tx.Table("session_tmp").Delete(*delete)
if err != nil {
rollbackFlag = true
}
if rollbackFlag {
tx.DropTable("session_tmp")
return errors.New("there is something wrong with data migration for table `session`, if there is a table called `session_tmp` not created by you in casdoor, please drop it, then restart anyhow")
}
err = tx.DropTable("session")
if err != nil {
return errors.New("fail to drop table `session` for casdoor, please drop it and rename the table `session_tmp` to `session` manually and restart")
}
// Already drop table `session`
// Can't find an api from xorm for altering table name
err = tx.Table("session").CreateTable(&Session{})
if err != nil {
return errors.New("there is something wrong with data migration for table `session`, please restart")
}
sessions := []*Session{}
tx.Table("session_tmp").Find(&sessions)
_, err = tx.Table("session").Insert(sessions)
if err != nil {
return errors.New("there is something wrong with data migration for table `session`, please drop table `session` and rename table `session_tmp` to `session` and restart")
}
err = tx.DropTable("session_tmp")
if err != nil {
return errors.New("fail to drop table `session_tmp` for casdoor, please drop it manually and restart")
}
tx.Commit()
return nil
},
}
return &migration
}

View File

@@ -19,7 +19,7 @@ import (
"github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/model"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type Model struct { type Model struct {

View File

@@ -21,7 +21,7 @@ import (
"github.com/casdoor/casdoor/cred" "github.com/casdoor/casdoor/cred"
"github.com/casdoor/casdoor/i18n" "github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type AccountItem struct { type AccountItem struct {

View File

@@ -19,7 +19,7 @@ import (
"net/http" "net/http"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type Payment struct { type Payment struct {

View File

@@ -18,7 +18,7 @@ import (
"fmt" "fmt"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type Permission struct { type Permission struct {

View File

@@ -21,8 +21,8 @@ import (
"github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/config" "github.com/casbin/casbin/v2/config"
"github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
xormadapter "github.com/casdoor/xorm-adapter/v3"
) )
func getEnforcer(permission *Permission) *casbin.Enforcer { func getEnforcer(permission *Permission) *casbin.Enforcer {

View File

@@ -18,7 +18,7 @@ import (
"fmt" "fmt"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type Product struct { type Product struct {

View File

@@ -20,7 +20,7 @@ import (
"github.com/casdoor/casdoor/i18n" "github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/pp" "github.com/casdoor/casdoor/pp"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type Provider struct { type Provider struct {

View File

@@ -18,7 +18,7 @@ import (
"fmt" "fmt"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type Resource struct { type Resource struct {

View File

@@ -19,7 +19,7 @@ import (
"strings" "strings"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type Role struct { type Role struct {

View File

@@ -17,82 +17,23 @@ package object
import ( import (
"github.com/beego/beego" "github.com/beego/beego"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
)
var (
CasdoorApplication = "app-built-in"
CasdoorOrganization = "built-in"
) )
type Session struct { type Session struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"` Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"` Name string `xorm:"varchar(100) notnull pk" json:"name"`
Application string `xorm:"varchar(100) notnull pk" json:"application"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"` CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
SessionId []string `json:"sessionId"` SessionId []string `json:"sessionId"`
} }
func SetSession(id string, sessionId string) {
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
session := &Session{Owner: owner, Name: name}
get, err := adapter.Engine.Get(session)
if err != nil {
panic(err)
}
session.SessionId = append(session.SessionId, sessionId)
if get {
_, err = adapter.Engine.ID(core.PK{owner, name}).Update(session)
} else {
session.CreatedTime = util.GetCurrentTime()
_, err = adapter.Engine.Insert(session)
}
if err != nil {
panic(err)
}
}
func DeleteSession(id string) bool {
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
session := &Session{Owner: owner, Name: name}
_, err := adapter.Engine.ID(core.PK{owner, name}).Get(session)
if err != nil {
return false
}
DeleteBeegoSession(session.SessionId)
affected, err := adapter.Engine.ID(core.PK{owner, name}).Delete(session)
return affected != 0
}
func DeleteSessionId(id string, sessionId string) bool {
owner, name := util.GetOwnerAndNameFromId(id)
session := &Session{Owner: owner, Name: name}
_, err := adapter.Engine.ID(core.PK{owner, name}).Get(session)
if err != nil {
return false
}
DeleteBeegoSession([]string{sessionId})
session.SessionId = util.DeleteVal(session.SessionId, sessionId)
if len(session.SessionId) < 1 {
affected, _ := adapter.Engine.ID(core.PK{owner, name}).Delete(session)
return affected != 0
} else {
affected, _ := adapter.Engine.ID(core.PK{owner, name}).Update(session)
return affected != 0
}
}
func DeleteBeegoSession(sessionIds []string) {
for _, sessionId := range sessionIds {
err := beego.GlobalSessions.GetProvider().SessionDestroy(sessionId)
if err != nil {
return
}
}
}
func GetSessions(owner string) []*Session { func GetSessions(owner string) []*Session {
sessions := []*Session{} sessions := []*Session{}
var err error var err error
@@ -128,3 +69,128 @@ func GetSessionCount(owner, field, value string) int {
return int(count) return int(count)
} }
func GetSingleSession(id string) *Session {
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
session := &Session{Owner: owner, Name: name, Application: application}
_, err := adapter.Engine.ID(core.PK{owner, name, application}).Get(session)
if err != nil {
panic(err)
}
return session
}
func UpdateSession(id string, session *Session) bool {
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
_, err := adapter.Engine.ID(core.PK{owner, name, application}).Get(session)
if err != nil {
return false
}
affected, err := adapter.Engine.ID(core.PK{owner, name, application}).Update(session)
if err != nil {
panic(err)
}
return affected != 0
}
func AddSession(session *Session) bool {
owner, name, application := session.Owner, session.Name, session.Application
dbSession := &Session{Owner: owner, Name: name, Application: application}
get, err := adapter.Engine.ID(core.PK{owner, name, application}).Get(dbSession)
if err != nil {
return false
}
var affected int64
var dbErr error
if !get {
session.CreatedTime = util.GetCurrentTime()
affected, dbErr = adapter.Engine.Insert(session)
} else {
m := make(map[string]struct{})
for _, v := range dbSession.SessionId {
m[v] = struct{}{}
}
for _, v := range session.SessionId {
if _, exists := m[v]; !exists {
dbSession.SessionId = append(dbSession.SessionId, v)
}
}
affected, dbErr = adapter.Engine.ID(core.PK{owner, name, application}).Update(dbSession)
}
if dbErr != nil {
panic(dbErr)
}
return affected != 0
}
func DeleteSession(id string) bool {
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
session := &Session{Owner: owner, Name: name, Application: application}
_, err := adapter.Engine.ID(core.PK{owner, name, application}).Get(session)
if err != nil {
return false
}
if owner == CasdoorOrganization && application == CasdoorApplication {
DeleteBeegoSession(session.SessionId)
}
affected, err := adapter.Engine.ID(core.PK{owner, name, application}).Delete(session)
return affected != 0
}
func DeleteSessionId(id string, sessionId string) bool {
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
session := &Session{Owner: owner, Name: name, Application: application}
_, err := adapter.Engine.ID(core.PK{owner, name, application}).Get(session)
if err != nil {
return false
}
if owner == CasdoorOrganization && application == CasdoorApplication {
DeleteBeegoSession([]string{sessionId})
}
session.SessionId = util.DeleteVal(session.SessionId, sessionId)
if len(session.SessionId) < 1 {
affected, _ := adapter.Engine.ID(core.PK{owner, name, application}).Delete(session)
return affected != 0
} else {
affected, _ := adapter.Engine.ID(core.PK{owner, name, application}).Update(session)
return affected != 0
}
}
func DeleteBeegoSession(sessionIds []string) {
for _, sessionId := range sessionIds {
err := beego.GlobalSessions.GetProvider().SessionDestroy(sessionId)
if err != nil {
return
}
}
}
func IsSessionDuplicated(id string, sessionId string) bool {
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
session := &Session{Owner: owner, Name: name, Application: application}
get, _ := adapter.Engine.ID(core.PK{owner, name, application}).Get(session)
if !get {
return false
} else {
if len(session.SessionId) > 1 {
return true
} else if len(session.SessionId) < 1 {
return false
} else {
return session.SessionId[0] != sessionId
}
}
}

View File

@@ -18,7 +18,7 @@ import (
"fmt" "fmt"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type TableColumn struct { type TableColumn struct {

View File

@@ -20,7 +20,7 @@ import (
"time" "time"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type OriginalUser = User type OriginalUser = User

View File

@@ -23,7 +23,7 @@ import (
"github.com/casdoor/casdoor/i18n" "github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/idp" "github.com/casdoor/casdoor/idp"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
const ( const (

View File

@@ -21,7 +21,7 @@ import (
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/duo-labs/webauthn/webauthn" "github.com/duo-labs/webauthn/webauthn"
"xorm.io/core" "github.com/xorm-io/core"
) )
const ( const (
@@ -578,7 +578,7 @@ func AddUsersInBatch(users []*User) bool {
func DeleteUser(user *User) bool { func DeleteUser(user *User) bool {
// Forced offline the user first // Forced offline the user first
DeleteSession(user.GetId()) DeleteSession(util.GetSessionId(user.Owner, user.Name, CasdoorApplication))
affected, err := adapter.Engine.ID(core.PK{user.Owner, user.Name}).Delete(&User{}) affected, err := adapter.Engine.ID(core.PK{user.Owner, user.Name}).Delete(&User{})
if err != nil { if err != nil {

View File

@@ -21,7 +21,7 @@ import (
"testing" "testing"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
func updateUserColumn(column string, user *User) bool { func updateUserColumn(column string, user *User) bool {

View File

@@ -20,7 +20,7 @@ import (
"strings" "strings"
"github.com/casdoor/casdoor/idp" "github.com/casdoor/casdoor/idp"
"xorm.io/core" "github.com/xorm-io/core"
) )
func GetUserByField(organizationName string, field string, value string) *User { func GetUserByField(organizationName string, field string, value string) *User {

View File

@@ -23,7 +23,7 @@ import (
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n" "github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
const ( const (

View File

@@ -18,7 +18,7 @@ import (
"fmt" "fmt"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type Header struct { type Header struct {

View File

@@ -163,7 +163,11 @@ func initAPI() {
beego.Router("/api/add-record", &controllers.ApiController{}, "POST:AddRecord") beego.Router("/api/add-record", &controllers.ApiController{}, "POST:AddRecord")
beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions") beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions")
beego.Router("/api/get-session", &controllers.ApiController{}, "GET:GetSingleSession")
beego.Router("/api/update-session", &controllers.ApiController{}, "POST:UpdateSession")
beego.Router("/api/add-session", &controllers.ApiController{}, "POST:AddSession")
beego.Router("/api/delete-session", &controllers.ApiController{}, "POST:DeleteSession") beego.Router("/api/delete-session", &controllers.ApiController{}, "POST:DeleteSession")
beego.Router("/api/is-session-duplicated", &controllers.ApiController{}, "GET:IsSessionDuplicated")
beego.Router("/api/get-webhooks", &controllers.ApiController{}, "GET:GetWebhooks") beego.Router("/api/get-webhooks", &controllers.ApiController{}, "GET:GetWebhooks")
beego.Router("/api/get-webhook", &controllers.ApiController{}, "GET:GetWebhook") beego.Router("/api/get-webhook", &controllers.ApiController{}, "GET:GetWebhook")

View File

@@ -100,6 +100,15 @@ func GetOwnerAndNameFromIdNoCheck(id string) (string, string) {
return tokens[0], tokens[1] return tokens[0], tokens[1]
} }
func GetOwnerAndNameAndOtherFromId(id string) (string, string, string) {
tokens := strings.Split(id, "/")
if len(tokens) != 3 {
panic(errors.New("GetOwnerAndNameAndOtherFromId() error, wrong token count for ID: " + id))
}
return tokens[0], tokens[1], tokens[2]
}
func GenerateId() string { func GenerateId() string {
return uuid.NewString() return uuid.NewString()
} }
@@ -127,6 +136,10 @@ func GetId(owner, name string) string {
return fmt.Sprintf("%s/%s", owner, name) return fmt.Sprintf("%s/%s", owner, name)
} }
func GetSessionId(owner, name, application string) string {
return fmt.Sprintf("%s/%s/%s", owner, name, application)
}
func GetMd5Hash(text string) string { func GetMd5Hash(text string) string {
hash := md5.Sum([]byte(text)) hash := md5.Sum([]byte(text))
return hex.EncodeToString(hash[:]) return hex.EncodeToString(hash[:])

View File

@@ -14,7 +14,7 @@
package util package util
import xormadapter "github.com/casbin/xorm-adapter/v3" import xormadapter "github.com/casdoor/xorm-adapter/v3"
func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string { func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string {
s := []string{ s := []string{

View File

@@ -84,8 +84,8 @@ class App extends Component {
uri: null, uri: null,
menuVisible: false, menuVisible: false,
themeAlgorithm: ["default"], themeAlgorithm: ["default"],
themeData: Setting.ThemeDefault, themeData: Conf.ThemeDefault,
logo: this.getLogo(Setting.getAlgorithmNames(Setting.ThemeDefault)), logo: this.getLogo(Setting.getAlgorithmNames(Conf.ThemeDefault)),
}; };
Setting.initServerUrl(); Setting.initServerUrl();

View File

@@ -18,6 +18,7 @@ import {CopyOutlined, LinkOutlined, UploadOutlined} from "@ant-design/icons";
import * as ApplicationBackend from "./backend/ApplicationBackend"; import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as CertBackend from "./backend/CertBackend"; import * as CertBackend from "./backend/CertBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as Conf from "./Conf";
import * as ProviderBackend from "./backend/ProviderBackend"; import * as ProviderBackend from "./backend/ProviderBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as ResourceBackend from "./backend/ResourceBackend"; import * as ResourceBackend from "./backend/ResourceBackend";
@@ -717,7 +718,7 @@ class ApplicationEditPage extends React.Component {
<Col span={22} style={{marginTop: "5px"}}> <Col span={22} style={{marginTop: "5px"}}>
<Row> <Row>
<Radio.Group value={this.state.application.themeData?.isEnabled ?? false} onChange={e => { <Radio.Group value={this.state.application.themeData?.isEnabled ?? false} onChange={e => {
const {_, ...theme} = this.state.application.themeData ?? {...Setting.ThemeDefault, isEnabled: false}; const {_, ...theme} = this.state.application.themeData ?? {...Conf.ThemeDefault, isEnabled: false};
this.updateApplicationField("themeData", {...theme, isEnabled: e.target.value}); this.updateApplicationField("themeData", {...theme, isEnabled: e.target.value});
}} > }} >
<Radio.Button value={false}>{i18next.t("application:Follow organization theme")}</Radio.Button> <Radio.Button value={false}>{i18next.t("application:Follow organization theme")}</Radio.Button>
@@ -728,7 +729,7 @@ class ApplicationEditPage extends React.Component {
this.state.application.themeData?.isEnabled ? this.state.application.themeData?.isEnabled ?
<Row style={{marginTop: "20px"}}> <Row style={{marginTop: "20px"}}>
<ThemeEditor themeData={this.state.application.themeData} onThemeChange={(_, nextThemeData) => { <ThemeEditor themeData={this.state.application.themeData} onThemeChange={(_, nextThemeData) => {
const {isEnabled} = this.state.application.themeData ?? {...Setting.ThemeDefault, isEnabled: false}; const {isEnabled} = this.state.application.themeData ?? {...Conf.ThemeDefault, isEnabled: false};
this.updateApplicationField("themeData", {...nextThemeData, isEnabled}); this.updateApplicationField("themeData", {...nextThemeData, isEnabled});
}} /> }} />
</Row> : null </Row> : null
@@ -764,7 +765,7 @@ class ApplicationEditPage extends React.Component {
} }
renderSignupSigninPreview() { renderSignupSigninPreview() {
const themeData = this.state.application.themeData ?? Setting.ThemeDefault; const themeData = this.state.application.themeData ?? Conf.ThemeDefault;
let signUpUrl = `/signup/${this.state.application.name}`; let signUpUrl = `/signup/${this.state.application.name}`;
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${this.state.application.redirectUris[0]}&scope=read&state=casdoor`; const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${this.state.application.redirectUris[0]}&scope=read&state=casdoor`;
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"}; const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
@@ -835,7 +836,7 @@ class ApplicationEditPage extends React.Component {
} }
renderPromptPreview() { renderPromptPreview() {
const themeData = this.state.application.themeData ?? Setting.ThemeDefault; const themeData = this.state.application.themeData ?? Conf.ThemeDefault;
const promptUrl = `/prompt/${this.state.application.name}`; const promptUrl = `/prompt/${this.state.application.name}`;
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"}; const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
return ( return (

View File

@@ -17,6 +17,13 @@ export const GithubRepo = "https://github.com/casdoor/casdoor";
export const ForceLanguage = ""; export const ForceLanguage = "";
export const DefaultLanguage = "en"; export const DefaultLanguage = "en";
export const InitThemeAlgorithm = true;
export const EnableExtraPages = true; export const EnableExtraPages = true;
export const InitThemeAlgorithm = true;
export const ThemeDefault = {
themeType: "default",
colorPrimary: "#5734d3",
borderRadius: 6,
isCompact: false,
};

View File

@@ -17,6 +17,7 @@ import {Redirect, Route, Switch} from "react-router-dom";
import {Spin} from "antd"; import {Spin} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as Conf from "./Conf";
import SignupPage from "./auth/SignupPage"; import SignupPage from "./auth/SignupPage";
import SelfLoginPage from "./auth/SelfLoginPage"; import SelfLoginPage from "./auth/SelfLoginPage";
import LoginPage from "./auth/LoginPage"; import LoginPage from "./auth/LoginPage";
@@ -62,7 +63,7 @@ class EntryPage extends React.Component {
application: application, application: application,
}); });
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Setting.ThemeDefault; const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
this.props.updataThemeData(themeData); this.props.updataThemeData(themeData);
}; };

View File

@@ -18,6 +18,7 @@ import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as ApplicationBackend from "./backend/ApplicationBackend"; import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as LdapBackend from "./backend/LdapBackend"; import * as LdapBackend from "./backend/LdapBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as Conf from "./Conf";
import i18next from "i18next"; import i18next from "i18next";
import {LinkOutlined} from "@ant-design/icons"; import {LinkOutlined} from "@ant-design/icons";
import LdapTable from "./LdapTable"; import LdapTable from "./LdapTable";
@@ -324,7 +325,7 @@ class OrganizationEditPage extends React.Component {
<Col span={22} style={{marginTop: "5px"}}> <Col span={22} style={{marginTop: "5px"}}>
<Row> <Row>
<Radio.Group value={this.state.organization.themeData?.isEnabled ?? false} onChange={e => { <Radio.Group value={this.state.organization.themeData?.isEnabled ?? false} onChange={e => {
const {_, ...theme} = this.state.organization.themeData ?? {...Setting.ThemeDefault, isEnabled: false}; const {_, ...theme} = this.state.organization.themeData ?? {...Conf.ThemeDefault, isEnabled: false};
this.updateOrganizationField("themeData", {...theme, isEnabled: e.target.value}); this.updateOrganizationField("themeData", {...theme, isEnabled: e.target.value});
}} > }} >
<Radio.Button value={false}>{i18next.t("organization:Follow global theme")}</Radio.Button> <Radio.Button value={false}>{i18next.t("organization:Follow global theme")}</Radio.Button>
@@ -335,7 +336,7 @@ class OrganizationEditPage extends React.Component {
this.state.organization.themeData?.isEnabled ? this.state.organization.themeData?.isEnabled ?
<Row style={{marginTop: "20px"}}> <Row style={{marginTop: "20px"}}>
<ThemeEditor themeData={this.state.organization.themeData} onThemeChange={(_, nextThemeData) => { <ThemeEditor themeData={this.state.organization.themeData} onThemeChange={(_, nextThemeData) => {
const {isEnabled} = this.state.organization.themeData ?? {...Setting.ThemeDefault, isEnabled: false}; const {isEnabled} = this.state.organization.themeData ?? {...Conf.ThemeDefault, isEnabled: false};
this.updateOrganizationField("themeData", {...nextThemeData, isEnabled}); this.updateOrganizationField("themeData", {...nextThemeData, isEnabled});
}} /> }} />
</Row> : null </Row> : null

View File

@@ -17,7 +17,7 @@ import i18next from "i18next";
import React from "react"; import React from "react";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as UserBackend from "./backend/UserBackend"; import * as UserBackend from "./backend/UserBackend";
import {CountDownInput} from "./common/CountDownInput"; import {SendCodeInput} from "./common/SendCodeInput";
import {MailOutlined, PhoneOutlined} from "@ant-design/icons"; import {MailOutlined, PhoneOutlined} from "@ant-design/icons";
export const ResetModal = (props) => { export const ResetModal = (props) => {
@@ -93,7 +93,7 @@ export const ResetModal = (props) => {
/> />
</Row> </Row>
<Row style={{width: "100%", marginBottom: "20px"}}> <Row style={{width: "100%", marginBottom: "20px"}}>
<CountDownInput <SendCodeInput
textBefore={i18next.t("code:Code You Received")} textBefore={i18next.t("code:Code You Received")}
onChange={setCode} onChange={setCode}
method={"reset"} method={"reset"}

View File

@@ -18,6 +18,16 @@ import {Dropdown} from "antd";
import "./App.less"; import "./App.less";
import {GlobalOutlined} from "@ant-design/icons"; import {GlobalOutlined} from "@ant-design/icons";
export const Countries = [{label: "English", key: "en", country: "US", alt: "English"},
{label: "简体中文", key: "zh", country: "CN", alt: "简体中文"},
{label: "Español", key: "es", country: "ES", alt: "Español"},
{label: "Français", key: "fr", country: "FR", alt: "Français"},
{label: "Deutsch", key: "de", country: "DE", alt: "Deutsch"},
{label: "日本語", key: "ja", country: "JP", alt: "日本語"},
{label: "한국어", key: "ko", country: "KR", alt: "한국어"},
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
];
function flagIcon(country, alt) { function flagIcon(country, alt) {
return ( return (
<img width={24} alt={alt} src={`${Setting.StaticBaseUrl}/flag-icons/${country}.svg`} /> <img width={24} alt={alt} src={`${Setting.StaticBaseUrl}/flag-icons/${country}.svg`} />
@@ -32,12 +42,12 @@ class SelectLanguageBox extends React.Component {
languages: props.languages ?? ["en", "zh", "es", "fr", "de", "ja", "ko", "ru"], languages: props.languages ?? ["en", "zh", "es", "fr", "de", "ja", "ko", "ru"],
}; };
Setting.Countries.forEach((country) => { Countries.forEach((country) => {
new Image().src = `${Setting.StaticBaseUrl}/flag-icons/${country.country}.svg`; new Image().src = `${Setting.StaticBaseUrl}/flag-icons/${country.country}.svg`;
}); });
} }
items = Setting.Countries.map((country) => Setting.getItem(country.label, country.key, flagIcon(country.country, country.alt))); items = Countries.map((country) => Setting.getItem(country.label, country.key, flagIcon(country.country, country.alt)));
getOrganizationLanguages(languages) { getOrganizationLanguages(languages) {
const select = []; const select = [];

View File

@@ -49,8 +49,8 @@ class SelectRegionBox extends React.Component {
} }
> >
{ {
Setting.CountryRegionData.map((item, index) => ( Setting.getCountryNames().map((item) => (
<Option key={index} value={item.code} label={`${item.name} (${item.code})`} > <Option key={item.code} value={item.code} label={`${item.name} (${item.code})`} >
<img src={`${Setting.StaticBaseUrl}/flag-icons/${item.code}.svg`} alt={item.name} height={20} style={{marginRight: 10}} /> <img src={`${Setting.StaticBaseUrl}/flag-icons/${item.code}.svg`} alt={item.name} height={20} style={{marginRight: 10}} />
{`${item.name} (${item.code})`} {`${item.name} (${item.code})`}
</Option> </Option>

View File

@@ -30,33 +30,13 @@ export const ServerUrl = "";
// export const StaticBaseUrl = "https://cdn.jsdelivr.net/gh/casbin/static"; // export const StaticBaseUrl = "https://cdn.jsdelivr.net/gh/casbin/static";
export const StaticBaseUrl = "https://cdn.casbin.org"; export const StaticBaseUrl = "https://cdn.casbin.org";
// https://catamphetamine.gitlab.io/country-flag-icons/3x2/index.html
export const CountryRegionData = getCountryRegionData();
export const Countries = [{label: "English", key: "en", country: "US", alt: "English"},
{label: "简体中文", key: "zh", country: "CN", alt: "简体中文"},
{label: "Español", key: "es", country: "ES", alt: "Español"},
{label: "Français", key: "fr", country: "FR", alt: "Français"},
{label: "Deutsch", key: "de", country: "DE", alt: "Deutsch"},
{label: "日本語", key: "ja", country: "JP", alt: "日本語"},
{label: "한국어", key: "ko", country: "KR", alt: "한국어"},
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
];
export const ThemeDefault = {
themeType: "default",
colorPrimary: "#5734d3",
borderRadius: 6,
isCompact: false,
};
export function getThemeData(organization, application) { export function getThemeData(organization, application) {
if (application?.themeData?.isEnabled) { if (application?.themeData?.isEnabled) {
return application.themeData; return application.themeData;
} else if (organization?.themeData?.isEnabled) { } else if (organization?.themeData?.isEnabled) {
return organization.themeData; return organization.themeData;
} else { } else {
return ThemeDefault; return Conf.ThemeDefault;
} }
} }
@@ -208,18 +188,18 @@ export const OtherProviderInfo = {
}, },
}; };
export function getCountryRegionData() { export function getCountriesData() {
let language = i18next.language;
if (language === null || language === "null") {
language = Conf.DefaultLanguage;
}
const countries = require("i18n-iso-countries"); const countries = require("i18n-iso-countries");
countries.registerLocale(require("i18n-iso-countries/langs/" + language + ".json")); countries.registerLocale(require("i18n-iso-countries/langs/" + getLanguage() + ".json"));
const data = countries.getNames(language, {select: "official"}); return countries;
const result = []; }
for (const i in data) {result.push({code: i, name: data[i]});}
return result; export function getCountryNames() {
const data = getCountriesData().getNames(getLanguage(), {select: "official"});
return Object.entries(data).map(items => {
return {code: items[0], name: items[1]};
});
} }
export function initServerUrl() { export function initServerUrl() {
@@ -702,7 +682,7 @@ export function getLanguageText(text) {
} }
export function getLanguage() { export function getLanguage() {
return i18next.language; return i18next.language ?? Conf.DefaultLanguage;
} }
export function setLanguage(language) { export function setLanguage(language) {

View File

@@ -135,13 +135,6 @@ class UserListPage extends BaseListPage {
} }
renderTable(users) { renderTable(users) {
// transfer country code to name based on selected language
const countries = require("i18n-iso-countries");
countries.registerLocale(require("i18n-iso-countries/langs/" + i18next.language + ".json"));
for (const index in users) {
users[index].region = countries.getName(users[index].region, i18next.language, {select: "official"});
}
const columns = [ const columns = [
{ {
title: i18next.t("general:Organization"), title: i18next.t("general:Organization"),
@@ -267,6 +260,9 @@ class UserListPage extends BaseListPage {
width: "140px", width: "140px",
sorter: true, sorter: true,
...this.getColumnSearchProps("region"), ...this.getColumnSearchProps("region"),
render: (text, record, index) => {
return Setting.getCountriesData().getName(record.region, Setting.getLanguage(), {select: "official"});
},
}, },
{ {
title: i18next.t("user:Tag"), title: i18next.t("user:Tag"),

View File

@@ -19,7 +19,7 @@ import * as ApplicationBackend from "../backend/ApplicationBackend";
import * as Util from "./Util"; import * as Util from "./Util";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import i18next from "i18next"; import i18next from "i18next";
import {CountDownInput} from "../common/CountDownInput"; import {SendCodeInput} from "../common/SendCodeInput";
import * as UserBackend from "../backend/UserBackend"; import * as UserBackend from "../backend/UserBackend";
import {CheckCircleOutlined, KeyOutlined, LockOutlined, SolutionOutlined, UserOutlined} from "@ant-design/icons"; import {CheckCircleOutlined, KeyOutlined, LockOutlined, SolutionOutlined, UserOutlined} from "@ant-design/icons";
import CustomGithubCorner from "../CustomGithubCorner"; import CustomGithubCorner from "../CustomGithubCorner";
@@ -350,14 +350,14 @@ class ForgetPage extends React.Component {
]} ]}
> >
{this.state.verifyType === "email" ? ( {this.state.verifyType === "email" ? (
<CountDownInput <SendCodeInput
disabled={this.state.username === "" || this.state.verifyType === ""} disabled={this.state.username === "" || this.state.verifyType === ""}
method={"forget"} method={"forget"}
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(this.getApplicationObj()), this.state.name]} onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(this.getApplicationObj()), this.state.name]}
application={application} application={application}
/> />
) : ( ) : (
<CountDownInput <SendCodeInput
disabled={this.state.username === "" || this.state.verifyType === ""} disabled={this.state.username === "" || this.state.verifyType === ""}
method={"forget"} method={"forget"}
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(this.getApplicationObj()), this.state.name]} onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(this.getApplicationObj()), this.state.name]}

View File

@@ -26,7 +26,7 @@ import * as Setting from "../Setting";
import SelfLoginButton from "./SelfLoginButton"; import SelfLoginButton from "./SelfLoginButton";
import i18next from "i18next"; import i18next from "i18next";
import CustomGithubCorner from "../CustomGithubCorner"; import CustomGithubCorner from "../CustomGithubCorner";
import {CountDownInput} from "../common/CountDownInput"; import {SendCodeInput} from "../common/SendCodeInput";
import SelectLanguageBox from "../SelectLanguageBox"; import SelectLanguageBox from "../SelectLanguageBox";
import {CaptchaModal} from "../common/CaptchaModal"; import {CaptchaModal} from "../common/CaptchaModal";
import RedirectForm from "../common/RedirectForm"; import RedirectForm from "../common/RedirectForm";
@@ -419,7 +419,7 @@ class LoginPage extends React.Component {
rules={[ rules={[
{ {
required: true, required: true,
message: i18next.t("login:Please input your username, Email or phone!"), message: i18next.t("login:Please input your Email or Phone!"),
}, },
{ {
validator: (_, value) => { validator: (_, value) => {
@@ -755,7 +755,7 @@ class LoginPage extends React.Component {
name="code" name="code"
rules={[{required: true, message: i18next.t("login:Please input your code!")}]} rules={[{required: true, message: i18next.t("login:Please input your code!")}]}
> >
<CountDownInput <SendCodeInput
disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone} disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone}
method={"login"} method={"login"}
onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]} onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]}

View File

@@ -21,7 +21,7 @@ import i18next from "i18next";
import * as Util from "./Util"; import * as Util from "./Util";
import {authConfig} from "./Auth"; import {authConfig} from "./Auth";
import * as ApplicationBackend from "../backend/ApplicationBackend"; import * as ApplicationBackend from "../backend/ApplicationBackend";
import {CountDownInput} from "../common/CountDownInput"; import {SendCodeInput} from "../common/SendCodeInput";
import SelectRegionBox from "../SelectRegionBox"; import SelectRegionBox from "../SelectRegionBox";
import CustomGithubCorner from "../CustomGithubCorner"; import CustomGithubCorner from "../CustomGithubCorner";
import SelectLanguageBox from "../SelectLanguageBox"; import SelectLanguageBox from "../SelectLanguageBox";
@@ -365,7 +365,7 @@ class SignupPage extends React.Component {
message: i18next.t("code:Please input your verification code!"), message: i18next.t("code:Please input your verification code!"),
}]} }]}
> >
<CountDownInput <SendCodeInput
disabled={!this.state.validEmail} disabled={!this.state.validEmail}
method={"signup"} method={"signup"}
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(application)]} onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(application)]}
@@ -419,7 +419,7 @@ class SignupPage extends React.Component {
}, },
]} ]}
> >
<CountDownInput <SendCodeInput
disabled={!this.state.validPhone} disabled={!this.state.validPhone}
method={"signup"} method={"signup"}
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]} onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]}

View File

@@ -109,7 +109,7 @@ export function setPassword(userOwner, userName, oldPassword, newPassword) {
}).then(res => res.json()); }).then(res => res.json());
} }
export function sendCode(checkType, checkId, checkKey, method, dest, type, applicationId, checkUser) { export function sendCode(checkType, checkId, checkKey, method, dest, type, applicationId, checkUser = "") {
const formData = new FormData(); const formData = new FormData();
formData.append("checkType", checkType); formData.append("checkType", checkType);
formData.append("checkId", checkId); formData.append("checkId", checkId);

View File

@@ -21,7 +21,7 @@ import {CaptchaWidget} from "./CaptchaWidget";
const {Search} = Input; const {Search} = Input;
export const CountDownInput = (props) => { export const SendCodeInput = (props) => {
const {disabled, textBefore, onChange, onButtonClickArgs, application, method} = props; const {disabled, textBefore, onChange, onButtonClickArgs, application, method} = props;
const [visible, setVisible] = React.useState(false); const [visible, setVisible] = React.useState(false);
const [key, setKey] = React.useState(""); const [key, setKey] = React.useState("");

View File

@@ -14,14 +14,13 @@
import {Card, ConfigProvider, Form, Layout, Switch, theme} from "antd"; import {Card, ConfigProvider, Form, Layout, Switch, theme} from "antd";
import ThemePicker from "./ThemePicker"; import ThemePicker from "./ThemePicker";
import ColorPicker from "./ColorPicker"; import ColorPicker, {GREEN_COLOR, PINK_COLOR} from "./ColorPicker";
import RadiusPicker from "./RadiusPicker"; import RadiusPicker from "./RadiusPicker";
import * as React from "react"; import * as React from "react";
import {GREEN_COLOR, PINK_COLOR} from "./ColorPicker"; import {useEffect} from "react";
import {Content} from "antd/es/layout/layout"; import {Content} from "antd/es/layout/layout";
import i18next from "i18next"; import i18next from "i18next";
import {useEffect} from "react"; import * as Conf from "../../Conf";
import * as Setting from "../../Setting";
const ThemesInfo = { const ThemesInfo = {
default: {}, default: {},
@@ -41,7 +40,7 @@ const ThemesInfo = {
const onChange = () => {}; const onChange = () => {};
export default function ThemeEditor(props) { export default function ThemeEditor(props) {
const themeData = props.themeData ?? Setting.ThemeDefault; const themeData = props.themeData ?? Conf.ThemeDefault;
const onThemeChange = props.onThemeChange ?? onChange; const onThemeChange = props.onThemeChange ?? onChange;
const {isCompact, themeType, ...themeToken} = themeData; const {isCompact, themeType, ...themeToken} = themeData;
@@ -59,7 +58,7 @@ export default function ThemeEditor(props) {
}, [isLight, isCompact]); }, [isLight, isCompact]);
useEffect(() => { useEffect(() => {
const mergedData = Object.assign(Object.assign(Object.assign({}, Setting.ThemeDefault), {themeType}), ThemesInfo[themeType]); const mergedData = Object.assign(Object.assign(Object.assign({}, Conf.ThemeDefault), {themeType}), ThemesInfo[themeType]);
onThemeChange(null, mergedData); onThemeChange(null, mergedData);
form.setFieldsValue(mergedData); form.setFieldsValue(mergedData);
}, [themeType]); }, [themeType]);