Compare commits

...

45 Commits

Author SHA1 Message Date
412a8b5da7 fix: init name is inconsistent with frontend (#1583) 2023-02-24 14:28:34 +08:00
8ebd16a14e feat: fix resetting email and phone bug (#1579) 2023-02-23 18:06:13 +08:00
44ec854465 Refactor getClientIdLabel() and getClientSecretLabel() 2023-02-23 17:57:46 +08:00
26e87b0d98 feat: fix compatibility with lower version browsers like Chrome 87 (#1578) 2023-02-22 20:57:57 +08:00
7e0ea0b8d9 Fix missing accountItem crash bug 2023-02-22 12:16:05 +08:00
ace8e9da06 Refactor getAppIdRow() 2023-02-22 12:10:55 +08:00
aac8714d72 feat: handle aliyun SMS response error (#1577) 2023-02-21 20:08:23 +08:00
e71e41b343 feat: fix captcha none type bug (#1572) 2023-02-19 16:56:51 +08:00
6131286cbd Add getBuiltInAccountItems() 2023-02-19 09:45:06 +08:00
3bda8fb9dc Remove Migrator_1_245_0_PR_1557 2023-02-19 09:38:57 +08:00
11f55a474c refactor: New Crowdin translations (#1440)
* refactor: New Crowdin translations by Github Action

* refactor: New Crowdin Backend translations by Github Action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-02-18 23:46:44 +08:00
4806e76cf6 Fix demo mode default login 2023-02-18 23:43:22 +08:00
edbd3d4018 Add i18n banner 2023-02-18 18:09:43 +08:00
3f0a741e6c Improve i18n languages 2023-02-18 17:35:36 +08:00
d273fdd670 Use username as saml:NameID 2023-02-18 16:42:45 +08:00
3ae81716b9 Fix getLdaps() GET 2023-02-18 16:27:47 +08:00
3a70f4e788 Add Setting.isResponseDenied() 2023-02-18 16:21:12 +08:00
842d4865b2 Fix router error message's i18n 2023-02-18 16:11:23 +08:00
19fb7273bb fix: detect access denied by response message for demo mode (#1565)
fix: detect access denied by response message for demo mode
2023-02-18 12:12:54 +08:00
943bd82731 feat: fix migrator_1_245_0_PR_1557.go (#1564) 2023-02-18 10:58:14 +08:00
f2f962b893 fix: refactor functions and code (#1559) 2023-02-18 09:31:58 +08:00
eb72c9f273 feat: support multiple country codes for sending SMS (#1557)
* feat: support multiple country code

* feat: improve UI

* feat: migrate accountItem

* fix: Aliyun compatible

* fix: phone validate

* fix: typo
2023-02-16 22:53:28 +08:00
4605938f8e Refactor FetchFilter.js code 2023-02-16 22:50:08 +08:00
14fa914e6f feat: add IsDemoMode for frontend (#1555)
* feat: add `IsDemoMode` for frontend

* fix: add i18n

* fix: support autologin and go same page

* fix: use i18n for button text
2023-02-16 20:36:30 +08:00
e877045671 feat: fix CAS login crash bug (#1549) 2023-02-14 16:52:15 +08:00
29f1ec08a2 fix: fix CI error by auto waiting for localhost:7001 to start up (#1548) 2023-02-14 14:50:58 +08:00
389744a27d feat: change claims to claimsWithoutThirdIdp when gen token (#1552) 2023-02-14 09:33:46 +08:00
dc7b66822d feat: change token ExpiresIn to second (#1550) 2023-02-14 09:18:30 +08:00
efacf8226c fix: session Id error (#1554) 2023-02-13 22:58:26 +08:00
6beb68dcce fix: some bugs in session module when testing single-log-in (#1547)
Co-authored-by: Zayn Xie <84443886+xiaoniuren99@users.noreply.github.com>
2023-02-13 18:16:31 +08:00
c9b990a319 Add removeExtraSessionIds() 2023-02-12 21:11:16 +08:00
eedcde3aa5 Refactor session.go 2023-02-12 21:06:08 +08:00
950a274b23 fix: region don't display in userEditPage (#1544) 2023-02-12 18:56:56 +08:00
478bd05db4 Improve error handling in migrator 2023-02-12 10:39:20 +08:00
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
6f2ef32d02 Update xorm.io/core to v0.7.3 2023-02-11 17:46:34 +08:00
8b8c866fd2 feat: replace gomail and disable the Email username check for Mailtrap (#1538) 2023-02-11 15:59:23 +08:00
6f7230e949 feat: support refresh token on GetOAuthToken (#1536) 2023-02-10 23:52:13 +08:00
wht
9558bb4167 feat: fix file name length problem (#1534) 2023-02-10 20:27:20 +08:00
04567babf8 feat: fix click MenuItem blank part invalid bug (#1535) 2023-02-10 18:11:15 +08:00
543b316942 feat: update swagger parameter id description (#1532) 2023-02-10 10:42:16 +08:00
e2b6e8ee6e chore: unify migrate database way (#1530) 2023-02-09 19:28:15 +08:00
wht
e7e0518517 feat: fix the upload file name contains space problem (#1527) 2023-02-07 23:26:17 +08:00
943aa61869 feat: add provider icons and menus (#1522)
* fix: add provider icons(email and captcha) and menus

* fix: add provider icons and menus
2023-02-06 20:28:40 +08:00
wht
fcc75dd3be feat: fix the Unicode filename encoding bug in storage provider (#1518) 2023-02-04 18:09:18 +08:00
143 changed files with 2923 additions and 841 deletions

View File

@ -97,21 +97,20 @@ jobs:
- uses: actions/setup-node@v2 - uses: actions/setup-node@v2
with: with:
node-version: 16 node-version: 16
- name: back start
run: nohup go run ./main.go &
working-directory: ./
- name: front install - name: front install
run: yarn install run: yarn install
working-directory: ./web working-directory: ./web
- name: front start - name: front start
run: nohup yarn start & run: nohup yarn start &
working-directory: ./web working-directory: ./web
- name: back start - uses: cypress-io/github-action@v4
run: nohup go run ./main.go & with:
working-directory: ./ working-directory: ./web
- name: Sleep for starting wait-on: 'http://localhost:7001'
run: sleep 90s wait-on-timeout: 180
shell: bash
- name: e2e
run: npx cypress run --spec "**/e2e/**.cy.js"
working-directory: ./web
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: failure() if: failure()

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

@ -21,19 +21,21 @@ type CaptchaProvider interface {
} }
func GetCaptchaProvider(captchaType string) CaptchaProvider { func GetCaptchaProvider(captchaType string) CaptchaProvider {
if captchaType == "Default" { switch captchaType {
case "Default":
return NewDefaultCaptchaProvider() return NewDefaultCaptchaProvider()
} else if captchaType == "reCAPTCHA" { case "reCAPTCHA":
return NewReCaptchaProvider() return NewReCaptchaProvider()
} else if captchaType == "hCaptcha" { case "Aliyun Captcha":
return NewHCaptchaProvider()
} else if captchaType == "Aliyun Captcha" {
return NewAliyunCaptchaProvider() return NewAliyunCaptchaProvider()
} else if captchaType == "GEETEST" { case "hCaptcha":
return NewHCaptchaProvider()
case "GEETEST":
return NewGEETESTCaptchaProvider() return NewGEETESTCaptchaProvider()
} else if captchaType == "Cloudflare Turnstile" { case "Cloudflare Turnstile":
return NewCloudflareTurnstileProvider() return NewCloudflareTurnstileProvider()
} }
return nil return nil
} }

View File

@ -20,5 +20,5 @@ staticBaseUrl = "https://cdn.casbin.org"
isDemoMode = false isDemoMode = false
batchSize = 100 batchSize = 100
ldapServerPort = 389 ldapServerPort = 389
languages = en,zh,es,fr,de,ja,ko,ru languages = en,zh,es,fr,de,ja,ko,ru,vi
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1} quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}

View File

@ -105,6 +105,20 @@ func GetConfigDataSourceName() string {
return dataSourceName return dataSourceName
} }
func GetLanguage(language string) string {
if language == "" {
return "en"
}
language = language[0:2]
if strings.Contains(GetConfigString("languages"), language) {
return language
} else {
return "en"
}
}
func IsDemoMode() bool { func IsDemoMode() bool {
return strings.ToLower(GetConfigString("isDemoMode")) == "true" return strings.ToLower(GetConfigString("isDemoMode")) == "true"
} }

View File

@ -58,7 +58,7 @@ type RequestForm struct {
EmailCode string `json:"emailCode"` EmailCode string `json:"emailCode"`
PhoneCode string `json:"phoneCode"` PhoneCode string `json:"phoneCode"`
PhonePrefix string `json:"phonePrefix"` CountryCode string `json:"countryCode"`
AutoSignin bool `json:"autoSignin"` AutoSignin bool `json:"autoSignin"`
@ -121,7 +121,7 @@ func (c *ApiController) Signup() {
} }
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", form.Organization)) organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", form.Organization))
msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.FirstName, form.LastName, form.Email, form.Phone, form.Affiliation, c.GetAcceptLanguage()) msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.FirstName, form.LastName, form.Email, form.Phone, form.CountryCode, form.Affiliation, c.GetAcceptLanguage())
if msg != "" { if msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
return return
@ -137,7 +137,7 @@ func (c *ApiController) Signup() {
var checkPhone string var checkPhone string
if application.IsSignupItemVisible("Phone") && form.Phone != "" { if application.IsSignupItemVisible("Phone") && form.Phone != "" {
checkPhone = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Phone) checkPhone, _ = util.GetE164Number(form.Phone, form.CountryCode)
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode, c.GetAcceptLanguage()) checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode, c.GetAcceptLanguage())
if len(checkResult) != 0 { if len(checkResult) != 0 {
c.ResponseError(c.T("account:Phone: %s"), checkResult) c.ResponseError(c.T("account:Phone: %s"), checkResult)
@ -179,6 +179,7 @@ func (c *ApiController) Signup() {
Avatar: organization.DefaultAvatar, Avatar: organization.DefaultAvatar,
Email: form.Email, Email: form.Email,
Phone: form.Phone, Phone: form.Phone,
CountryCode: form.CountryCode,
Address: []string{}, Address: []string{},
Affiliation: form.Affiliation, Affiliation: form.Affiliation,
IdCard: form.IdCard, IdCard: form.IdCard,
@ -254,7 +255,10 @@ 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
owner, username := util.GetOwnerAndNameFromId(user)
object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), 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 +295,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

@ -63,7 +63,7 @@ func (c *ApiController) GetApplications() {
// @Title GetApplication // @Title GetApplication
// @Tag Application API // @Tag Application API
// @Description get the detail of an application // @Description get the detail of an application
// @Param id query string true "The id of the application." // @Param id query string true "The id ( owner/name ) of the application."
// @Success 200 {object} object.Application The Response object // @Success 200 {object} object.Application The Response object
// @router /get-application [get] // @router /get-application [get]
func (c *ApiController) GetApplication() { func (c *ApiController) GetApplication() {
@ -78,7 +78,7 @@ func (c *ApiController) GetApplication() {
// @Title GetUserApplication // @Title GetUserApplication
// @Tag Application API // @Tag Application API
// @Description get the detail of the user's application // @Description get the detail of the user's application
// @Param id query string true "The id of the user" // @Param id query string true "The id ( owner/name ) of the user"
// @Success 200 {object} object.Application The Response object // @Success 200 {object} object.Application The Response object
// @router /get-user-application [get] // @router /get-user-application [get]
func (c *ApiController) GetUserApplication() { func (c *ApiController) GetUserApplication() {
@ -134,7 +134,7 @@ func (c *ApiController) GetOrganizationApplications() {
// @Title UpdateApplication // @Title UpdateApplication
// @Tag Application API // @Tag Application API
// @Description update an application // @Description update an application
// @Param id query string true "The id of the application" // @Param id query string true "The id ( owner/name ) of the application"
// @Param body body object.Application true "The details of the application" // @Param body body object.Application true "The details of the application"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-application [post] // @router /update-application [post]

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
@ -258,34 +263,32 @@ func (c *ApiController) Login() {
checkDest = form.Username checkDest = form.Username
} else { } else {
verificationCodeType = "phone" verificationCodeType = "phone"
if len(form.PhonePrefix) == 0 {
responseText := fmt.Sprintf(c.T("auth:%s No phone prefix"), verificationCodeType)
c.ResponseError(responseText)
return
}
if user != nil && util.GetMaskedPhone(user.Phone) == form.Username { if user != nil && util.GetMaskedPhone(user.Phone) == form.Username {
form.Username = user.Phone form.Username = user.Phone
} }
checkDest = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
} }
user = object.GetUserByFields(form.Organization, form.Username)
if user == nil { if user = object.GetUserByFields(form.Organization, form.Username); user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(form.Organization, form.Username))) c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(form.Organization, form.Username)))
return return
} }
if verificationCodeType == "phone" {
form.CountryCode = user.GetCountryCode(form.CountryCode)
var ok bool
if checkDest, ok = util.GetE164Number(form.Username, form.CountryCode); !ok {
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), form.CountryCode))
return
}
}
checkResult = object.CheckSigninCode(user, checkDest, form.Code, c.GetAcceptLanguage()) checkResult = object.CheckSigninCode(user, checkDest, form.Code, c.GetAcceptLanguage())
if len(checkResult) != 0 { if len(checkResult) != 0 {
responseText := fmt.Sprintf("%s - %s", verificationCodeType, checkResult) c.ResponseError(fmt.Sprintf("%s - %s", verificationCodeType, checkResult))
c.ResponseError(responseText)
return return
} }
// disable the verification code // disable the verification code
if strings.Contains(form.Username, "@") { object.DisableVerificationCode(checkDest)
object.DisableVerificationCode(form.Username)
} else {
object.DisableVerificationCode(fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username))
}
} else { } else {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application)) application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil { if application == nil {

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

@ -52,7 +52,7 @@ func (c *ApiController) GetCerts() {
// @Title GetCert // @Title GetCert
// @Tag Cert API // @Tag Cert API
// @Description get cert // @Description get cert
// @Param id query string true "The id of the cert" // @Param id query string true "The id ( owner/name ) of the cert"
// @Success 200 {object} object.Cert The Response object // @Success 200 {object} object.Cert The Response object
// @router /get-cert [get] // @router /get-cert [get]
func (c *ApiController) GetCert() { func (c *ApiController) GetCert() {
@ -66,7 +66,7 @@ func (c *ApiController) GetCert() {
// @Title UpdateCert // @Title UpdateCert
// @Tag Cert API // @Tag Cert API
// @Description update cert // @Description update cert
// @Param id query string true "The id of the cert" // @Param id query string true "The id ( owner/name ) of the cert"
// @Param body body object.Cert true "The details of the cert" // @Param body body object.Cert true "The details of the cert"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-cert [post] // @router /update-cert [post]

View File

@ -51,7 +51,7 @@ type LdapSyncResp struct {
func (c *ApiController) GetLdapUser() { func (c *ApiController) GetLdapUser() {
ldapServer := LdapServer{} ldapServer := LdapServer{}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer) err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer)
if err != nil || util.IsStrsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) { if err != nil || util.IsStringsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) {
c.ResponseError(c.T("general:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }
@ -104,7 +104,7 @@ func (c *ApiController) GetLdapUser() {
// GetLdaps // GetLdaps
// @Tag Account API // @Tag Account API
// @Title GetLdaps // @Title GetLdaps
// @router /get-ldaps [post] // @router /get-ldaps [get]
func (c *ApiController) GetLdaps() { func (c *ApiController) GetLdaps() {
owner := c.Input().Get("owner") owner := c.Input().Get("owner")
@ -115,11 +115,11 @@ func (c *ApiController) GetLdaps() {
// GetLdap // GetLdap
// @Tag Account API // @Tag Account API
// @Title GetLdap // @Title GetLdap
// @router /get-ldap [post] // @router /get-ldap [get]
func (c *ApiController) GetLdap() { func (c *ApiController) GetLdap() {
id := c.Input().Get("id") id := c.Input().Get("id")
if util.IsStrsEmpty(id) { if util.IsStringsEmpty(id) {
c.ResponseError(c.T("general:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }
@ -140,7 +140,7 @@ func (c *ApiController) AddLdap() {
return return
} }
if util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) { if util.IsStringsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError(c.T("general:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }
@ -170,7 +170,7 @@ func (c *ApiController) AddLdap() {
func (c *ApiController) UpdateLdap() { func (c *ApiController) UpdateLdap() {
var ldap object.Ldap var ldap object.Ldap
err := json.Unmarshal(c.Ctx.Input.RequestBody, &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) { if err != nil || util.IsStringsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError(c.T("general:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetModels() {
// @Title GetModel // @Title GetModel
// @Tag Model API // @Tag Model API
// @Description get model // @Description get model
// @Param id query string true "The id of the model" // @Param id query string true "The id ( owner/name ) of the model"
// @Success 200 {object} object.Model The Response object // @Success 200 {object} object.Model The Response object
// @router /get-model [get] // @router /get-model [get]
func (c *ApiController) GetModel() { func (c *ApiController) GetModel() {
@ -66,7 +66,7 @@ func (c *ApiController) GetModel() {
// @Title UpdateModel // @Title UpdateModel
// @Tag Model API // @Tag Model API
// @Description update model // @Description update model
// @Param id query string true "The id of the model" // @Param id query string true "The id ( owner/name ) of the model"
// @Param body body object.Model true "The details of the model" // @Param body body object.Model true "The details of the model"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-model [post] // @router /update-model [post]

View File

@ -66,7 +66,7 @@ func (c *ApiController) GetOrganization() {
// @Title UpdateOrganization // @Title UpdateOrganization
// @Tag Organization API // @Tag Organization API
// @Description update organization // @Description update organization
// @Param id query string true "The id of the organization" // @Param id query string true "The id ( owner/name ) of the organization"
// @Param body body object.Organization true "The details of the organization" // @Param body body object.Organization true "The details of the organization"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-organization [post] // @router /update-organization [post]

View File

@ -71,7 +71,7 @@ func (c *ApiController) GetUserPayments() {
// @Title GetPayment // @Title GetPayment
// @Tag Payment API // @Tag Payment API
// @Description get payment // @Description get payment
// @Param id query string true "The id of the payment" // @Param id query string true "The id ( owner/name ) of the payment"
// @Success 200 {object} object.Payment The Response object // @Success 200 {object} object.Payment The Response object
// @router /get-payment [get] // @router /get-payment [get]
func (c *ApiController) GetPayment() { func (c *ApiController) GetPayment() {
@ -85,7 +85,7 @@ func (c *ApiController) GetPayment() {
// @Title UpdatePayment // @Title UpdatePayment
// @Tag Payment API // @Tag Payment API
// @Description update payment // @Description update payment
// @Param id query string true "The id of the payment" // @Param id query string true "The id ( owner/name ) of the payment"
// @Param body body object.Payment true "The details of the payment" // @Param body body object.Payment true "The details of the payment"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-payment [post] // @router /update-payment [post]
@ -172,7 +172,7 @@ func (c *ApiController) NotifyPayment() {
// @Title InvoicePayment // @Title InvoicePayment
// @Tag Payment API // @Tag Payment API
// @Description invoice payment // @Description invoice payment
// @Param id query string true "The id of the payment" // @Param id query string true "The id ( owner/name ) of the payment"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /invoice-payment [post] // @router /invoice-payment [post]
func (c *ApiController) InvoicePayment() { func (c *ApiController) InvoicePayment() {

View File

@ -69,7 +69,7 @@ func (c *ApiController) GetPermissionsBySubmitter() {
// @Title GetPermissionsByRole // @Title GetPermissionsByRole
// @Tag Permission API // @Tag Permission API
// @Description get permissions by role // @Description get permissions by role
// @Param id query string true "The id of the role" // @Param id query string true "The id ( owner/name ) of the role"
// @Success 200 {array} object.Permission The Response object // @Success 200 {array} object.Permission The Response object
// @router /get-permissions-by-role [get] // @router /get-permissions-by-role [get]
func (c *ApiController) GetPermissionsByRole() { func (c *ApiController) GetPermissionsByRole() {
@ -83,7 +83,7 @@ func (c *ApiController) GetPermissionsByRole() {
// @Title GetPermission // @Title GetPermission
// @Tag Permission API // @Tag Permission API
// @Description get permission // @Description get permission
// @Param id query string true "The id of the permission" // @Param id query string true "The id ( owner/name ) of the permission"
// @Success 200 {object} object.Permission The Response object // @Success 200 {object} object.Permission The Response object
// @router /get-permission [get] // @router /get-permission [get]
func (c *ApiController) GetPermission() { func (c *ApiController) GetPermission() {
@ -97,7 +97,7 @@ func (c *ApiController) GetPermission() {
// @Title UpdatePermission // @Title UpdatePermission
// @Tag Permission API // @Tag Permission API
// @Description update permission // @Description update permission
// @Param id query string true "The id of the permission" // @Param id query string true "The id ( owner/name ) of the permission"
// @Param body body object.Permission true "The details of the permission" // @Param body body object.Permission true "The details of the permission"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-permission [post] // @router /update-permission [post]

View File

@ -53,7 +53,7 @@ func (c *ApiController) GetProducts() {
// @Title GetProduct // @Title GetProduct
// @Tag Product API // @Tag Product API
// @Description get product // @Description get product
// @Param id query string true "The id of the product" // @Param id query string true "The id ( owner/name ) of the product"
// @Success 200 {object} object.Product The Response object // @Success 200 {object} object.Product The Response object
// @router /get-product [get] // @router /get-product [get]
func (c *ApiController) GetProduct() { func (c *ApiController) GetProduct() {
@ -70,7 +70,7 @@ func (c *ApiController) GetProduct() {
// @Title UpdateProduct // @Title UpdateProduct
// @Tag Product API // @Tag Product API
// @Description update product // @Description update product
// @Param id query string true "The id of the product" // @Param id query string true "The id ( owner/name ) of the product"
// @Param body body object.Product true "The details of the product" // @Param body body object.Product true "The details of the product"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-product [post] // @router /update-product [post]
@ -130,7 +130,7 @@ func (c *ApiController) DeleteProduct() {
// @Title BuyProduct // @Title BuyProduct
// @Tag Product API // @Tag Product API
// @Description buy product // @Description buy product
// @Param id query string true "The id of the product" // @Param id query string true "The id ( owner/name ) of the product"
// @Param providerName query string true "The name of the provider" // @Param providerName query string true "The name of the provider"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /buy-product [post] // @router /buy-product [post]

View File

@ -76,7 +76,7 @@ func (c *ApiController) GetGlobalProviders() {
// @Title GetProvider // @Title GetProvider
// @Tag Provider API // @Tag Provider API
// @Description get provider // @Description get provider
// @Param id query string true "The id of the provider" // @Param id query string true "The id ( owner/name ) of the provider"
// @Success 200 {object} object.Provider The Response object // @Success 200 {object} object.Provider The Response object
// @router /get-provider [get] // @router /get-provider [get]
func (c *ApiController) GetProvider() { func (c *ApiController) GetProvider() {
@ -89,7 +89,7 @@ func (c *ApiController) GetProvider() {
// @Title UpdateProvider // @Title UpdateProvider
// @Tag Provider API // @Tag Provider API
// @Description update provider // @Description update provider
// @Param id query string true "The id of the provider" // @Param id query string true "The id ( owner/name ) of the provider"
// @Param body body object.Provider true "The details of the provider" // @Param body body object.Provider true "The details of the provider"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-provider [post] // @router /update-provider [post]

View File

@ -180,6 +180,7 @@ func (c *ApiController) UploadResource() {
fileType, _ = util.GetOwnerAndNameFromId(mimeType) fileType, _ = util.GetOwnerAndNameFromId(mimeType)
} }
fullFilePath = object.GetTruncatedPath(provider, fullFilePath, 175)
if tag != "avatar" && tag != "termsOfUse" { if tag != "avatar" && tag != "termsOfUse" {
ext := filepath.Ext(filepath.Base(fullFilePath)) ext := filepath.Ext(filepath.Base(fullFilePath))
index := len(fullFilePath) - len(ext) index := len(fullFilePath) - len(ext)

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetRoles() {
// @Title GetRole // @Title GetRole
// @Tag Role API // @Tag Role API
// @Description get role // @Description get role
// @Param id query string true "The id of the role" // @Param id query string true "The id ( owner/name ) of the role"
// @Success 200 {object} object.Role The Response object // @Success 200 {object} object.Role The Response object
// @router /get-role [get] // @router /get-role [get]
func (c *ApiController) GetRole() { func (c *ApiController) GetRole() {
@ -66,7 +66,7 @@ func (c *ApiController) GetRole() {
// @Title UpdateRole // @Title UpdateRole
// @Tag Role API // @Tag Role API
// @Description update role // @Description update role
// @Param id query string true "The id of the role" // @Param id query string true "The id ( owner/name ) of the role"
// @Param body body object.Role true "The details of the role" // @Param body body object.Role true "The details of the role"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-role [post] // @router /update-role [post]

View File

@ -80,7 +80,7 @@ func (c *ApiController) SendEmail() {
c.ResponseOk() c.ResponseOk()
} }
if util.IsStrsEmpty(emailForm.Title, emailForm.Content, emailForm.Sender) { if util.IsStringsEmpty(emailForm.Title, emailForm.Content, emailForm.Sender) {
c.ResponseError(fmt.Sprintf(c.T("service:Empty parameters for emailForm: %v"), emailForm)) c.ResponseError(fmt.Sprintf(c.T("service:Empty parameters for emailForm: %v"), emailForm))
return return
} }
@ -130,13 +130,13 @@ func (c *ApiController) SendSms() {
return return
} }
org := object.GetOrganization(smsForm.OrgId)
var invalidReceivers []string var invalidReceivers []string
for idx, receiver := range smsForm.Receivers { for idx, receiver := range smsForm.Receivers {
if !util.IsPhoneCnValid(receiver) { // The receiver phone format: E164 like +8613854673829 +441932567890
if !util.IsPhoneValid(receiver, "") {
invalidReceivers = append(invalidReceivers, receiver) invalidReceivers = append(invalidReceivers, receiver)
} else { } else {
smsForm.Receivers[idx] = fmt.Sprintf("+%s%s", org.PhonePrefix, receiver) smsForm.Receivers[idx] = receiver
} }
} }

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) 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-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()
}

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetSyncers() {
// @Title GetSyncer // @Title GetSyncer
// @Tag Syncer API // @Tag Syncer API
// @Description get syncer // @Description get syncer
// @Param id query string true "The id of the syncer" // @Param id query string true "The id ( owner/name ) of the syncer"
// @Success 200 {object} object.Syncer The Response object // @Success 200 {object} object.Syncer The Response object
// @router /get-syncer [get] // @router /get-syncer [get]
func (c *ApiController) GetSyncer() { func (c *ApiController) GetSyncer() {
@ -66,7 +66,7 @@ func (c *ApiController) GetSyncer() {
// @Title UpdateSyncer // @Title UpdateSyncer
// @Tag Syncer API // @Tag Syncer API
// @Description update syncer // @Description update syncer
// @Param id query string true "The id of the syncer" // @Param id query string true "The id ( owner/name ) of the syncer"
// @Param body body object.Syncer true "The details of the syncer" // @Param body body object.Syncer true "The details of the syncer"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-syncer [post] // @router /update-syncer [post]

View File

@ -29,7 +29,7 @@ type SystemInfo struct {
// @Title GetSystemInfo // @Title GetSystemInfo
// @Tag System API // @Tag System API
// @Description get user's system info // @Description get user's system info
// @Param id query string true "The id of the user" // @Param id query string true "The id ( owner/name ) of the user"
// @Success 200 {object} object.SystemInfo The Response object // @Success 200 {object} object.SystemInfo The Response object
// @router /get-system-info [get] // @router /get-system-info [get]
func (c *ApiController) GetSystemInfo() { func (c *ApiController) GetSystemInfo() {

View File

@ -54,7 +54,7 @@ func (c *ApiController) GetTokens() {
// @Title GetToken // @Title GetToken
// @Tag Token API // @Tag Token API
// @Description get token // @Description get token
// @Param id query string true "The id of token" // @Param id query string true "The id ( owner/name ) of token"
// @Success 200 {object} object.Token The Response object // @Success 200 {object} object.Token The Response object
// @router /get-token [get] // @router /get-token [get]
func (c *ApiController) GetToken() { func (c *ApiController) GetToken() {
@ -68,7 +68,7 @@ func (c *ApiController) GetToken() {
// @Title UpdateToken // @Title UpdateToken
// @Tag Token API // @Tag Token API
// @Description update token // @Description update token
// @Param id query string true "The id of token" // @Param id query string true "The id ( owner/name ) of token"
// @Param body body object.Token true "Details of the token" // @Param body body object.Token true "Details of the token"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-token [post] // @router /update-token [post]
@ -128,7 +128,7 @@ func (c *ApiController) DeleteToken() {
// @Title GetOAuthCode // @Title GetOAuthCode
// @Tag Token API // @Tag Token API
// @Description get OAuth code // @Description get OAuth code
// @Param user_id query string true "The id of user" // @Param id query string true "The id ( owner/name ) of user"
// @Param client_id query string true "OAuth client id" // @Param client_id query string true "OAuth client id"
// @Param response_type query string true "OAuth response type" // @Param response_type query string true "OAuth response type"
// @Param redirect_uri query string true "OAuth redirect URI" // @Param redirect_uri query string true "OAuth redirect URI"
@ -172,6 +172,7 @@ func (c *ApiController) GetOAuthCode() {
// @router /login/oauth/access_token [post] // @router /login/oauth/access_token [post]
func (c *ApiController) GetOAuthToken() { func (c *ApiController) GetOAuthToken() {
grantType := c.Input().Get("grant_type") grantType := c.Input().Get("grant_type")
refreshToken := c.Input().Get("refresh_token")
clientId := c.Input().Get("client_id") clientId := c.Input().Get("client_id")
clientSecret := c.Input().Get("client_secret") clientSecret := c.Input().Get("client_secret")
code := c.Input().Get("code") code := c.Input().Get("code")
@ -192,6 +193,7 @@ func (c *ApiController) GetOAuthToken() {
clientId = tokenRequest.ClientId clientId = tokenRequest.ClientId
clientSecret = tokenRequest.ClientSecret clientSecret = tokenRequest.ClientSecret
grantType = tokenRequest.GrantType grantType = tokenRequest.GrantType
refreshToken = tokenRequest.RefreshToken
code = tokenRequest.Code code = tokenRequest.Code
verifier = tokenRequest.Verifier verifier = tokenRequest.Verifier
scope = tokenRequest.Scope scope = tokenRequest.Scope
@ -203,7 +205,7 @@ func (c *ApiController) GetOAuthToken() {
} }
host := c.Ctx.Request.Host host := c.Ctx.Request.Host
c.Data["json"] = object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, tag, avatar, c.GetAcceptLanguage()) c.Data["json"] = object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
c.SetTokenErrorHttpStatus() c.SetTokenErrorHttpStatus()
c.ServeJSON() c.ServeJSON()
} }

View File

@ -80,7 +80,7 @@ func (c *ApiController) GetUsers() {
// @Title GetUser // @Title GetUser
// @Tag User API // @Tag User API
// @Description get user // @Description get user
// @Param id query string true "The id of the user" // @Param id query string true "The id ( owner/name ) of the user"
// @Param owner query string false "The owner of the user" // @Param owner query string false "The owner of the user"
// @Param email query string false "The email of the user" // @Param email query string false "The email of the user"
// @Param phone query string false "The phone of the user" // @Param phone query string false "The phone of the user"
@ -129,7 +129,7 @@ func (c *ApiController) GetUser() {
// @Title UpdateUser // @Title UpdateUser
// @Tag User API // @Tag User API
// @Description update user // @Description update user
// @Param id query string true "The id of the user" // @Param id query string true "The id ( owner/name ) of the user"
// @Param body body object.User true "The details of the user" // @Param body body object.User true "The details of the user"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-user [post] // @router /update-user [post]

View File

@ -62,6 +62,10 @@ func checkPermissionForUpdateUser(userId string, newUser object.User, c *ApiCont
item := object.GetAccountItemByName("Phone", organization) item := object.GetAccountItemByName("Phone", organization)
itemsChanged = append(itemsChanged, item) itemsChanged = append(itemsChanged, item)
} }
if oldUser.CountryCode != newUser.CountryCode {
item := object.GetAccountItemByName("Country code", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Region != newUser.Region { if oldUser.Region != newUser.Region {
item := object.GetAccountItemByName("Country/Region", organization) item := object.GetAccountItemByName("Country/Region", organization)
itemsChanged = append(itemsChanged, item) itemsChanged = append(itemsChanged, item)

View File

@ -17,7 +17,6 @@ package controllers
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n" "github.com/casdoor/casdoor/i18n"
@ -56,11 +55,8 @@ func (c *ApiController) T(error string) string {
// GetAcceptLanguage ... // GetAcceptLanguage ...
func (c *ApiController) GetAcceptLanguage() string { func (c *ApiController) GetAcceptLanguage() string {
lang := c.Ctx.Request.Header.Get("Accept-Language") language := c.Ctx.Request.Header.Get("Accept-Language")
if lang == "" || !strings.Contains(conf.GetConfigString("languages"), lang[0:2]) { return conf.GetLanguage(language)
lang = "en"
}
return lang[0:2]
} }
// SetTokenErrorHttpStatus ... // SetTokenErrorHttpStatus ...

View File

@ -24,6 +24,13 @@ import (
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
const (
SignupVerification = "signup"
ResetVerification = "reset"
LoginVerification = "login"
ForgetVerification = "forget"
)
func (c *ApiController) getCurrentUser() *object.User { func (c *ApiController) getCurrentUser() *object.User {
var user *object.User var user *object.User
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
@ -42,18 +49,15 @@ func (c *ApiController) getCurrentUser() *object.User {
func (c *ApiController) SendVerificationCode() { func (c *ApiController) SendVerificationCode() {
destType := c.Ctx.Request.Form.Get("type") destType := c.Ctx.Request.Form.Get("type")
dest := c.Ctx.Request.Form.Get("dest") dest := c.Ctx.Request.Form.Get("dest")
countryCode := c.Ctx.Request.Form.Get("countryCode")
checkType := c.Ctx.Request.Form.Get("checkType") checkType := c.Ctx.Request.Form.Get("checkType")
checkId := c.Ctx.Request.Form.Get("checkId") checkId := c.Ctx.Request.Form.Get("checkId")
checkKey := c.Ctx.Request.Form.Get("checkKey") checkKey := c.Ctx.Request.Form.Get("checkKey")
checkUser := c.Ctx.Request.Form.Get("checkUser")
applicationId := c.Ctx.Request.Form.Get("applicationId") applicationId := c.Ctx.Request.Form.Get("applicationId")
method := c.Ctx.Request.Form.Get("method") method := c.Ctx.Request.Form.Get("method")
checkUser := c.Ctx.Request.Form.Get("checkUser")
remoteAddr := util.GetIPFromRequest(c.Ctx.Request) remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
if destType == "" {
c.ResponseError(c.T("general:Missing parameter") + ": type.")
return
}
if dest == "" { if dest == "" {
c.ResponseError(c.T("general:Missing parameter") + ": dest.") c.ResponseError(c.T("general:Missing parameter") + ": dest.")
return return
@ -62,98 +66,104 @@ func (c *ApiController) SendVerificationCode() {
c.ResponseError(c.T("general:Missing parameter") + ": applicationId.") c.ResponseError(c.T("general:Missing parameter") + ": applicationId.")
return return
} }
if !strings.Contains(applicationId, "/") {
c.ResponseError(c.T("verification:Wrong parameter") + ": applicationId.")
return
}
if checkType == "" { if checkType == "" {
c.ResponseError(c.T("general:Missing parameter") + ": checkType.") c.ResponseError(c.T("general:Missing parameter") + ": checkType.")
return return
} }
if !strings.Contains(applicationId, "/") {
c.ResponseError(c.T("verification:Wrong parameter") + ": applicationId.")
return
}
captchaProvider := captcha.GetCaptchaProvider(checkType) if checkType != "none" {
if captchaProvider != nil {
if checkKey == "" { if checkKey == "" {
c.ResponseError(c.T("general:Missing parameter") + ": checkKey.") c.ResponseError(c.T("general:Missing parameter") + ": checkKey.")
return return
} }
isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId)
if err != nil { if captchaProvider := captcha.GetCaptchaProvider(checkType); captchaProvider == nil {
c.ResponseError(c.T("general:don't support captchaProvider: ") + checkType)
return
} else if isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId); err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} } else if !isHuman {
if !isHuman {
c.ResponseError(c.T("verification:Turing test failed.")) c.ResponseError(c.T("verification:Turing test failed."))
return return
} }
} }
user := c.getCurrentUser()
application := object.GetApplication(applicationId) application := object.GetApplication(applicationId)
organization := object.GetOrganization(fmt.Sprintf("%s/%s", application.Owner, application.Organization)) organization := object.GetOrganization(util.GetId(application.Owner, application.Organization))
if organization == nil { if organization == nil {
c.ResponseError(c.T("verification:Organization does not exist")) c.ResponseError(c.T("verification:Organization does not exist"))
return return
} }
if checkUser == "true" && user == nil && object.GetUserByFields(organization.Name, dest) == nil { var user *object.User
c.ResponseError(c.T("general:Please login first")) // checkUser != "", means method is ForgetVerification
return if checkUser != "" {
owner := application.Organization
user = object.GetUser(util.GetId(owner, checkUser))
} }
sendResp := errors.New("invalid dest type") sendResp := errors.New("invalid dest type")
if user == nil && checkUser != "" && checkUser != "true" {
name := application.Organization
user = object.GetUser(fmt.Sprintf("%s/%s", name, checkUser))
}
switch destType { switch destType {
case "email": case "email":
if user != nil && util.GetMaskedEmail(user.Email) == dest {
dest = user.Email
}
if !util.IsEmailValid(dest) { if !util.IsEmailValid(dest) {
c.ResponseError(c.T("verification:Email is invalid")) c.ResponseError(c.T("verification:Email is invalid"))
return return
} }
userByEmail := object.GetUserByEmail(organization.Name, dest) if method == LoginVerification || method == ForgetVerification {
if userByEmail == nil && method != "signup" && method != "reset" { if user != nil && util.GetMaskedEmail(user.Email) == dest {
c.ResponseError(c.T("verification:the user does not exist, please sign up first")) dest = user.Email
return }
user = object.GetUserByEmail(organization.Name, dest)
if user == nil {
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
return
}
} else if method == ResetVerification {
user = c.getCurrentUser()
} }
provider := application.GetEmailProvider() provider := application.GetEmailProvider()
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, dest) sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, dest)
case "phone": case "phone":
if user != nil && util.GetMaskedPhone(user.Phone) == dest { if method == LoginVerification || method == ForgetVerification {
dest = user.Phone if user != nil && util.GetMaskedPhone(user.Phone) == dest {
} dest = user.Phone
if !util.IsPhoneCnValid(dest) { }
c.ResponseError(c.T("verification:Phone number is invalid"))
return if user = object.GetUserByPhone(organization.Name, dest); user == nil {
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
return
}
countryCode = user.GetCountryCode(countryCode)
} else if method == ResetVerification {
if user = c.getCurrentUser(); user != nil {
countryCode = user.GetCountryCode(countryCode)
}
} }
userByPhone := object.GetUserByPhone(organization.Name, dest)
if userByPhone == nil && method != "signup" && method != "reset" {
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
return
}
dest = fmt.Sprintf("+%s%s", organization.PhonePrefix, dest)
provider := application.GetSmsProvider() provider := application.GetSmsProvider()
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, remoteAddr, dest) if phone, ok := util.GetE164Number(dest, countryCode); !ok {
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), countryCode))
return
} else {
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, remoteAddr, phone)
}
} }
if sendResp != nil { if sendResp != nil {
c.Data["json"] = Response{Status: "error", Msg: sendResp.Error()} c.ResponseError(sendResp.Error())
} else { } else {
c.Data["json"] = Response{Status: "ok"} c.ResponseOk()
} }
c.ServeJSON()
} }
// ResetEmailOrPhone ... // ResetEmailOrPhone ...
@ -169,7 +179,8 @@ func (c *ApiController) ResetEmailOrPhone() {
destType := c.Ctx.Request.Form.Get("type") destType := c.Ctx.Request.Form.Get("type")
dest := c.Ctx.Request.Form.Get("dest") dest := c.Ctx.Request.Form.Get("dest")
code := c.Ctx.Request.Form.Get("code") code := c.Ctx.Request.Form.Get("code")
if len(dest) == 0 || len(code) == 0 || len(destType) == 0 {
if util.IsStringsEmpty(destType, dest, code) {
c.ResponseError(c.T("general:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }
@ -177,7 +188,7 @@ func (c *ApiController) ResetEmailOrPhone() {
checkDest := dest checkDest := dest
organization := object.GetOrganizationByUser(user) organization := object.GetOrganizationByUser(user)
if destType == "phone" { if destType == "phone" {
if object.HasUserByField(user.Owner, "phone", user.Phone) { if object.HasUserByField(user.Owner, "phone", dest) {
c.ResponseError(c.T("check:Phone already exists")) c.ResponseError(c.T("check:Phone already exists"))
return return
} }
@ -192,14 +203,12 @@ func (c *ApiController) ResetEmailOrPhone() {
c.ResponseError(errMsg) c.ResponseError(errMsg)
return return
} }
if checkDest, ok = util.GetE164Number(dest, user.GetCountryCode("")); !ok {
phonePrefix := "86" c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), user.CountryCode))
if organization != nil && organization.PhonePrefix != "" { return
phonePrefix = organization.PhonePrefix
} }
checkDest = fmt.Sprintf("+%s%s", phonePrefix, dest)
} else if destType == "email" { } else if destType == "email" {
if object.HasUserByField(user.Owner, "email", user.Email) { if object.HasUserByField(user.Owner, "email", dest) {
c.ResponseError(c.T("check:Email already exists")) c.ResponseError(c.T("check:Email already exists"))
return return
} }
@ -215,8 +224,8 @@ func (c *ApiController) ResetEmailOrPhone() {
return return
} }
} }
if ret := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); len(ret) != 0 { if msg := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); len(msg) != 0 {
c.ResponseError(ret) c.ResponseError(msg)
return return
} }
@ -233,8 +242,7 @@ func (c *ApiController) ResetEmailOrPhone() {
} }
object.DisableVerificationCode(checkDest) object.DisableVerificationCode(checkDest)
c.Data["json"] = Response{Status: "ok"} c.ResponseOk()
c.ServeJSON()
} }
// VerifyCaptcha ... // VerifyCaptcha ...

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetWebhooks() {
// @Title GetWebhook // @Title GetWebhook
// @Tag Webhook API // @Tag Webhook API
// @Description get webhook // @Description get webhook
// @Param id query string true "The id of the webhook" // @Param id query string true "The id ( owner/name ) of the webhook"
// @Success 200 {object} object.Webhook The Response object // @Success 200 {object} object.Webhook The Response object
// @router /get-webhook [get] // @router /get-webhook [get]
func (c *ApiController) GetWebhook() { func (c *ApiController) GetWebhook() {
@ -66,7 +66,7 @@ func (c *ApiController) GetWebhook() {
// @Title UpdateWebhook // @Title UpdateWebhook
// @Tag Webhook API // @Tag Webhook API
// @Description update webhook // @Description update webhook
// @Param id query string true "The id of the webhook" // @Param id query string true "The id ( owner/name ) of the webhook"
// @Param body body object.Webhook true "The details of the webhook" // @Param body body object.Webhook true "The details of the webhook"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-webhook [post] // @router /update-webhook [post]

14
go.mod
View File

@ -9,14 +9,14 @@ 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.2
github.com/casdoor/go-sms-sender v0.5.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
github.com/forestmgy/ldapserver v1.1.0 github.com/forestmgy/ldapserver v1.1.0
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
github.com/go-ldap/ldap/v3 v3.3.0 github.com/go-ldap/ldap/v3 v3.3.0
github.com/go-pay/gopay v1.5.72 github.com/go-pay/gopay v1.5.72
github.com/go-sql-driver/mysql v1.5.0 github.com/go-sql-driver/mysql v1.5.0
@ -30,6 +30,7 @@ require (
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
github.com/markbates/goth v1.75.2 github.com/markbates/goth v1.75.2
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/nyaruka/phonenumbers v1.1.5
github.com/qiangmzsx/string-adapter/v2 v2.1.0 github.com/qiangmzsx/string-adapter/v2 v2.1.0
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/russellhaering/gosaml2 v0.6.0 github.com/russellhaering/gosaml2 v0.6.0
@ -41,18 +42,15 @@ 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
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
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.2
xorm.io/xorm v1.1.2
) )

37
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,12 +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/casdoor/go-sms-sender v0.5.2 h1:LRjqoXBAOsH4yxXeJ7kINMesyoyEx5pEoiJz9d7lGJA=
github.com/casbin/xorm-adapter/v3 v3.0.1/go.mod h1:1BL7rHEDXrxO+vQdSo/ZaWKRivXl7YTos67GdMYcd20= github.com/casdoor/go-sms-sender v0.5.2/go.mod h1:kBykbqwgRDXbXdMAIxmZKinVM1WjdqEbej5LAbUbcfI=
github.com/casdoor/go-sms-sender v0.5.1 h1:1/Wp1OLkVAVY4lEGQhekSNetSAWhnPcxYPV7xpCZgC0= github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w=
github.com/casdoor/go-sms-sender v0.5.1/go.mod h1:kBykbqwgRDXbXdMAIxmZKinVM1WjdqEbej5LAbUbcfI= 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=
@ -126,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=
@ -154,8 +153,6 @@ github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df h1:Bao6dhmbTA1KFVxmJ6nBoMuOJit2yjEgLJpIMYpop0E=
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ= github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
@ -270,6 +267,7 @@ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko= github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko=
github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc= github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
@ -337,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=
@ -357,6 +354,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/nyaruka/phonenumbers v1.1.5 h1:vYy2DI+z5hdaemqVzXYJ4CVyK92IG484CirEY+40GTo=
github.com/nyaruka/phonenumbers v1.1.5/go.mod h1:yShPJHDSH3aTKzCbXyVxNpbl2kA+F+Ne5Pun/MvFRos=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
@ -456,6 +455,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=
@ -520,7 +525,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=
@ -789,8 +793,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@ -847,12 +849,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.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.1.2 h1:bje+1KZvK3m5AHtZNfUDlKEEyuw/IRHT+an0CLIG5TU=
xorm.io/xorm v1.1.2/go.mod h1:Cb0DKYTHbyECMaSfgRnIZp5aiUgQozxcJJ0vzcLGJSg=

View File

@ -30,24 +30,26 @@ func TestGenerateI18nFrontend(t *testing.T) {
enData := parseEnData("frontend") enData := parseEnData("frontend")
writeI18nFile("frontend", "en", enData) writeI18nFile("frontend", "en", enData)
applyToOtherLanguage("frontend", "de", enData) applyToOtherLanguage("frontend", "zh", enData)
applyToOtherLanguage("frontend", "es", enData) applyToOtherLanguage("frontend", "es", enData)
applyToOtherLanguage("frontend", "fr", enData) applyToOtherLanguage("frontend", "fr", enData)
applyToOtherLanguage("frontend", "de", enData)
applyToOtherLanguage("frontend", "ja", enData) applyToOtherLanguage("frontend", "ja", enData)
applyToOtherLanguage("frontend", "ko", enData) applyToOtherLanguage("frontend", "ko", enData)
applyToOtherLanguage("frontend", "ru", enData) applyToOtherLanguage("frontend", "ru", enData)
applyToOtherLanguage("frontend", "zh", enData) applyToOtherLanguage("frontend", "vi", enData)
} }
func TestGenerateI18nBackend(t *testing.T) { func TestGenerateI18nBackend(t *testing.T) {
enData := parseEnData("backend") enData := parseEnData("backend")
writeI18nFile("backend", "en", enData) writeI18nFile("backend", "en", enData)
applyToOtherLanguage("backend", "de", enData) applyToOtherLanguage("backend", "zh", enData)
applyToOtherLanguage("backend", "es", enData) applyToOtherLanguage("backend", "es", enData)
applyToOtherLanguage("backend", "fr", enData) applyToOtherLanguage("backend", "fr", enData)
applyToOtherLanguage("backend", "de", enData)
applyToOtherLanguage("backend", "ja", enData) applyToOtherLanguage("backend", "ja", enData)
applyToOtherLanguage("backend", "ko", enData) applyToOtherLanguage("backend", "ko", enData)
applyToOtherLanguage("backend", "ru", enData) applyToOtherLanguage("backend", "ru", enData)
applyToOtherLanguage("backend", "zh", enData) applyToOtherLanguage("backend", "vi", enData)
} }

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account" "The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Failed to login in: %s",
@ -55,15 +54,17 @@
"Username cannot contain white spaces": "Username cannot contain white spaces", "Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit", "Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "Username must have at least 2 characters": "Benutzername muss mindestens 2 Zeichen lang sein",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "unsupported password type: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist" "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account" "The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "unsupported password type: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist" "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account" "The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "unsupported password type: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist" "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account" "The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "unsupported password type: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist" "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account" "The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "unsupported password type: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist" "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account" "The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "unsupported password type: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist" "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account" "The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s", "Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s", "Failed to login in: %s": "Failed to login in: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).", "Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters", "Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s" "unsupported password type: %s": "unsupported password type: %s"
}, },
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist" "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: "
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Ldap server exist"
@ -130,7 +131,7 @@
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",

147
i18n/locales/vi/data.json Normal file
View File

@ -0,0 +1,147 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"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"
},
"auth": {
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LastName cannot be blank": "LastName cannot be blank",
"Ldap user name or password incorrect": "Ldap user name or password incorrect",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"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",
"don't support captchaProvider: ": "don't support captchaProvider: "
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"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",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong parameter": "Wrong parameter",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

View File

@ -9,7 +9,6 @@
"The application does not allow to sign up new account": "该应用不允许注册新用户" "The application does not allow to sign up new account": "该应用不允许注册新用户"
}, },
"auth": { "auth": {
"%s No phone prefix": "%s 无此手机号前缀",
"Challenge method should be S256": "Challenge 方法应该为 S256", "Challenge method should be S256": "Challenge 方法应该为 S256",
"Failed to create user, user information is invalid: %s": "创建用户失败,用户信息无效: %s", "Failed to create user, user information is invalid: %s": "创建用户失败,用户信息无效: %s",
"Failed to login in: %s": "登录失败: %s", "Failed to login in: %s": "登录失败: %s",
@ -57,13 +56,15 @@
"Username is too long (maximum is 39 characters).": "用户名过长最大允许长度为39个字符", "Username is too long (maximum is 39 characters).": "用户名过长最大允许长度为39个字符",
"Username must have at least 2 characters": "用户名至少要有2个字符", "Username must have at least 2 characters": "用户名至少要有2个字符",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "密码错误次数已达上限,请在 %d 分后重试", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "密码错误次数已达上限,请在 %d 分后重试",
"Your region is not allow to signup by phone": "所在地区不支持手机号注册",
"password or code is incorrect, you have %d remaining chances": "密码错误,您还有 %d 次尝试的机会", "password or code is incorrect, you have %d remaining chances": "密码错误,您还有 %d 次尝试的机会",
"unsupported password type: %s": "不支持的密码类型: %s" "unsupported password type: %s": "不支持的密码类型: %s"
}, },
"general": { "general": {
"Missing parameter": "缺少参数", "Missing parameter": "缺少参数",
"Please login first": "请先登录", "Please login first": "请先登录",
"The user: %s doesn't exist": "用户: %s 不存在" "The user: %s doesn't exist": "用户: %s 不存在",
"don't support captchaProvider: ": "不支持验证码提供商: "
}, },
"ldap": { "ldap": {
"Ldap server exist": "LDAP服务器已存在" "Ldap server exist": "LDAP服务器已存在"
@ -130,7 +131,7 @@
"Email is invalid": "非法的邮箱", "Email is invalid": "非法的邮箱",
"Invalid captcha provider.": "非法的验证码提供商", "Invalid captcha provider.": "非法的验证码提供商",
"Organization does not exist": "组织不存在", "Organization does not exist": "组织不存在",
"Phone number is invalid": "非法的手机号码", "Phone number is invalid in your region %s": "您所在地区的电话号码无效 %s",
"Turing test failed.": "验证码还未发送", "Turing test failed.": "验证码还未发送",
"Unable to get the email modify rule.": "无法获取邮箱修改规则", "Unable to get the email modify rule.": "无法获取邮箱修改规则",
"Unable to get the phone modify rule.": "无法获取手机号修改规则", "Unable to get the phone modify rule.": "无法获取手机号修改规则",

View File

@ -7,7 +7,7 @@
"websiteUrl": "", "websiteUrl": "",
"favicon": "", "favicon": "",
"passwordType": "", "passwordType": "",
"phonePrefix": "", "countryCodes": [""],
"defaultAvatar": "", "defaultAvatar": "",
"tags": [""] "tags": [""]
} }
@ -107,6 +107,7 @@
"avatar": "", "avatar": "",
"email": "", "email": "",
"phone": "", "phone": "",
"countryCode": "",
"address": [], "address": [],
"affiliation": "", "affiliation": "",
"tag": "", "tag": "",

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,16 +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
"xorm.io/xorm/migrate"
) )
var adapter *Adapter var adapter *Adapter
@ -41,12 +40,16 @@ func InitConfig() {
beego.BConfig.WebConfig.Session.SessionOn = true beego.BConfig.WebConfig.Session.SessionOn = true
InitAdapter(true) InitAdapter()
initMigrations() 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()
} }
@ -252,22 +255,3 @@ func GetSession(owner string, offset, limit int, field, value, sortField, sortOr
} }
return session return session
} }
func initMigrations() {
migrations := []*migrate.Migration{
{
ID: "20221015CasbinRule--fill ptype field with p",
Migrate: func(tx *xorm.Engine) error {
_, err := tx.Cols("ptype").Update(&xormadapter.CasbinRule{
Ptype: "p",
})
return err
},
Rollback: func(tx *xorm.Engine) error {
return tx.DropTables(&xormadapter.CasbinRule{})
},
},
}
m := migrate.New(adapter.Engine, migrate.DefaultOptions, migrations)
m.Migrate()
}

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

@ -42,7 +42,7 @@ func init() {
reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`) reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
} }
func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, firstName string, lastName string, email string, phone string, affiliation string, lang string) string { func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, firstName string, lastName string, email string, phone string, countryCode string, affiliation string, lang string) string {
if organization == nil { if organization == nil {
return i18n.Translate(lang, "check:Organization does not exist") return i18n.Translate(lang, "check:Organization does not exist")
} }
@ -107,7 +107,9 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if HasUserByField(organization.Name, "phone", phone) { if HasUserByField(organization.Name, "phone", phone) {
return i18n.Translate(lang, "check:Phone already exists") return i18n.Translate(lang, "check:Phone already exists")
} else if organization.PhonePrefix == "86" && !util.IsPhoneCnValid(phone) { } else if !util.IsPhoneAllowInRegin(countryCode, organization.CountryCodes) {
return i18n.Translate(lang, "check:Your region is not allow to signup by phone")
} else if !util.IsPhoneValid(phone, countryCode) {
return i18n.Translate(lang, "check:Phone number is invalid") return i18n.Translate(lang, "check:Phone number is invalid")
} }
} }

View File

@ -19,7 +19,7 @@ package object
import ( import (
"crypto/tls" "crypto/tls"
"github.com/go-gomail/gomail" "github.com/casdoor/gomail/v2"
) )
func getDialer(provider *Provider) *gomail.Dialer { func getDialer(provider *Provider) *gomail.Dialer {
@ -45,6 +45,10 @@ func SendEmail(provider *Provider, title string, content string, dest string, se
message.SetHeader("Subject", title) message.SetHeader("Subject", title)
message.SetBody("text/html", content) message.SetBody("text/html", content)
if provider.Type == "Mailtrap" {
message.SkipUsernameCheck = true
}
return dialer.DialAndSend(message) return dialer.DialAndSend(message)
} }

View File

@ -25,8 +25,6 @@ import (
) )
func InitDb() { func InitDb() {
MigratePermissionRule()
existed := initBuiltInOrganization() existed := initBuiltInOrganization()
if !existed { if !existed {
initBuiltInModel() initBuiltInModel()
@ -41,6 +39,39 @@ func InitDb() {
initWebAuthn() initWebAuthn()
} }
func getBuiltInAccountItems() []*AccountItem {
return []*AccountItem{
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "ID", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Name", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Display name", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Avatar", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "User type", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Password", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Email", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Phone", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Country code", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Country/Region", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Location", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Affiliation", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Title", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Homepage", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is global admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
}
}
func initBuiltInOrganization() bool { func initBuiltInOrganization() bool {
organization := getOrganization("admin", "built-in") organization := getOrganization("admin", "built-in")
if organization != nil { if organization != nil {
@ -55,40 +86,12 @@ func initBuiltInOrganization() bool {
WebsiteUrl: "https://example.com", WebsiteUrl: "https://example.com",
Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", conf.GetConfigString("staticBaseUrl")), Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", conf.GetConfigString("staticBaseUrl")),
PasswordType: "plain", PasswordType: "plain",
PhonePrefix: "86", CountryCodes: []string{"CN"},
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")), DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
Tags: []string{}, Tags: []string{},
Languages: []string{"en", "zh", "es", "fr", "de", "ja", "ko", "ru"}, Languages: []string{"en", "zh", "es", "fr", "de", "ja", "ko", "ru", "vi"},
InitScore: 2000, InitScore: 2000,
AccountItems: []*AccountItem{ AccountItems: getBuiltInAccountItems(),
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "ID", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Name", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Display name", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Avatar", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "User type", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Password", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Email", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Phone", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Country/Region", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Location", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Affiliation", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Title", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Homepage", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is global admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
},
} }
AddOrganization(organization) AddOrganization(organization)
return false return false
@ -111,6 +114,7 @@ func initBuiltInUser() {
Avatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")), Avatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
Email: "admin@example.com", Email: "admin@example.com",
Phone: "12345678910", Phone: "12345678910",
CountryCode: "CN",
Address: []string{}, Address: []string{},
Affiliation: "Example Inc.", Affiliation: "Example Inc.",
Tag: "staff", Tag: "staff",

View File

@ -179,33 +179,7 @@ func initDefinedOrganization(organization *Organization) {
return return
} }
organization.CreatedTime = util.GetCurrentTime() organization.CreatedTime = util.GetCurrentTime()
organization.AccountItems = []*AccountItem{ organization.AccountItems = getBuiltInAccountItems()
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "ID", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Name", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Display name", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Avatar", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "User type", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Password", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Email", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Phone", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Country/Region", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Location", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Affiliation", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Title", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Homepage", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is global admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
}
AddOrganization(organization) AddOrganization(organization)
} }

View File

@ -329,7 +329,7 @@ func GetLdaps(owner string) []*Ldap {
} }
func GetLdap(id string) *Ldap { func GetLdap(id string) *Ldap {
if util.IsStrsEmpty(id) { if util.IsStringsEmpty(id) {
return nil return nil
} }

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

@ -0,0 +1,70 @@
// 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 (
"strings"
"github.com/xorm-io/xorm"
"github.com/xorm-io/xorm/migrate"
)
type Migrator_1_101_0_PR_1083 struct{}
func (*Migrator_1_101_0_PR_1083) IsMigrationNeeded() bool {
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 (*Migrator_1_101_0_PR_1083) DoMigration() *migrate.Migration {
migration := migrate.Migration{
ID: "20230209MigratePermissionRule--Use V5 instead of V1 to store permissionID",
Migrate: func(engine *xorm.Engine) error {
models := []*Model{}
err := engine.Table("model").Find(&models, &Model{})
if err != nil {
panic(err)
}
isHit := false
for _, model := range models {
if strings.Contains(model.ModelText, "permission") {
// update model table
model.ModelText = strings.Replace(model.ModelText, "permission,", "", -1)
UpdateModel(model.GetId(), model)
isHit = true
}
}
if isHit {
// update permission_rule table
sql := "UPDATE `permission_rule`SET V0 = V1, V1 = V2, V2 = V3, V3 = V4, V4 = V5 WHERE V0 IN (SELECT CONCAT(owner, '/', name) AS permission_id FROM `permission`)"
_, err = engine.Exec(sql)
if err != nil {
return err
}
}
return err
},
}
return &migration
}

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 {
@ -49,7 +49,7 @@ type Organization struct {
Favicon string `xorm:"varchar(100)" json:"favicon"` Favicon string `xorm:"varchar(100)" json:"favicon"`
PasswordType string `xorm:"varchar(100)" json:"passwordType"` PasswordType string `xorm:"varchar(100)" json:"passwordType"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"` PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"` CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"` DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"` DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
Tags []string `xorm:"mediumtext" json:"tags"` Tags []string `xorm:"mediumtext" json:"tags"`
@ -211,6 +211,10 @@ func GetAccountItemByName(name string, organization *Organization) *AccountItem
} }
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User, lang string) (bool, string) { func CheckAccountItemModifyRule(accountItem *AccountItem, user *User, lang string) (bool, string) {
if accountItem == nil {
return true, ""
}
switch accountItem.ModifyRule { switch accountItem.ModifyRule {
case "Admin": case "Admin":
if user == nil || !user.IsAdmin && !user.IsGlobalAdmin { if user == nil || !user.IsAdmin && !user.IsGlobalAdmin {

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

@ -16,10 +16,9 @@ package object
import ( import (
"fmt" "fmt"
"strings"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "github.com/xorm-io/core"
) )
type Permission struct { type Permission struct {
@ -269,33 +268,6 @@ func GetPermissionsBySubmitter(owner string, submitter string) []*Permission {
return permissions return permissions
} }
func MigratePermissionRule() {
models := []*Model{}
err := adapter.Engine.Find(&models, &Model{})
if err != nil {
panic(err)
}
isHit := false
for _, model := range models {
if strings.Contains(model.ModelText, "permission") {
// update model table
model.ModelText = strings.Replace(model.ModelText, "permission,", "", -1)
UpdateModel(model.GetId(), model)
isHit = true
}
}
if isHit {
// update permission_rule table
sql := "UPDATE `permission_rule`SET V0 = V1, V1 = V2, V2 = V3, V3 = V4, V4 = V5 WHERE V0 IN (SELECT CONCAT(owner, '/', name) AS permission_id FROM `permission`)"
_, err = adapter.Engine.Exec(sql)
if err != nil {
return
}
}
}
func ContainsAsterisk(userId string, users []string) bool { func ContainsAsterisk(userId string, users []string) bool {
containsAsterisk := false containsAsterisk := false
group, _ := util.GetOwnerAndNameFromId(userId) group, _ := util.GetOwnerAndNameFromId(userId)

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,12 +18,12 @@ 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 {
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(180) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"` CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
User string `xorm:"varchar(100)" json:"user"` User string `xorm:"varchar(100)" json:"user"`

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

@ -64,7 +64,7 @@ func NewSamlResponse(user *User, host string, certificate string, destination st
assertion.CreateAttr("IssueInstant", now) assertion.CreateAttr("IssueInstant", now)
assertion.CreateElement("saml:Issuer").SetText(host) assertion.CreateElement("saml:Issuer").SetText(host)
subject := assertion.CreateElement("saml:Subject") subject := assertion.CreateElement("saml:Subject")
subject.CreateElement("saml:NameID").SetText(user.Email) subject.CreateElement("saml:NameID").SetText(user.Name)
subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation") subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation")
subjectConfirmation.CreateAttr("Method", "urn:oasis:names:tc:SAML:2.0:cm:bearer") subjectConfirmation.CreateAttr("Method", "urn:oasis:names:tc:SAML:2.0:cm:bearer")
subjectConfirmationData := subjectConfirmation.CreateElement("saml:SubjectConfirmationData") subjectConfirmationData := subjectConfirmation.CreateElement("saml:SubjectConfirmationData")

View File

@ -15,84 +15,27 @@
package object package object
import ( import (
"fmt"
"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 +71,131 @@ 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}
get, err := adapter.Engine.Get(&session)
if err != nil {
panic(err)
}
if !get {
return nil
}
return &session
}
func UpdateSession(id string, session *Session) bool {
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
if GetSingleSession(id) == nil {
return false
}
affected, err := adapter.Engine.ID(core.PK{owner, name, application}).Update(session)
if err != nil {
panic(err)
}
return affected != 0
}
func removeExtraSessionIds(session *Session) {
if len(session.SessionId) > 100 {
session.SessionId = session.SessionId[(len(session.SessionId) - 100):]
}
}
func AddSession(session *Session) bool {
dbSession := GetSingleSession(session.GetId())
if dbSession == nil {
session.CreatedTime = util.GetCurrentTime()
affected, err := adapter.Engine.Insert(session)
if err != nil {
panic(err)
}
return affected != 0
} 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)
}
}
removeExtraSessionIds(dbSession)
return UpdateSession(dbSession.GetId(), dbSession)
}
}
func DeleteSession(id string) bool {
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
if owner == CasdoorOrganization && application == CasdoorApplication {
session := GetSingleSession(id)
if session != nil {
DeleteBeegoSession(session.SessionId)
}
}
affected, err := adapter.Engine.ID(core.PK{owner, name, application}).Delete(&Session{})
if err != nil {
panic(err)
}
return affected != 0
}
func DeleteSessionId(id string, sessionId string) bool {
session := GetSingleSession(id)
if session == nil {
return false
}
owner, _, application := util.GetOwnerAndNameAndOtherFromId(id)
if owner == CasdoorOrganization && application == CasdoorApplication {
DeleteBeegoSession([]string{sessionId})
}
session.SessionId = util.DeleteVal(session.SessionId, sessionId)
if len(session.SessionId) == 0 {
return DeleteSession(id)
} else {
return UpdateSession(id, session)
}
}
func DeleteBeegoSession(sessionIds []string) {
for _, sessionId := range sessionIds {
err := beego.GlobalSessions.GetProvider().SessionDestroy(sessionId)
if err != nil {
return
}
}
}
func (session *Session) GetId() string {
return fmt.Sprintf("%s/%s/%s", session.Owner, session.Name, session.Application)
}
func IsSessionDuplicated(id string, sessionId string) bool {
session := GetSingleSession(id)
if session == nil {
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

@ -14,7 +14,11 @@
package object package object
import "github.com/casdoor/go-sms-sender" import (
"strings"
"github.com/casdoor/go-sms-sender"
)
func SendSms(provider *Provider, content string, phoneNumbers ...string) error { func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
client, err := go_sms_sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId) client, err := go_sms_sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
@ -25,6 +29,12 @@ func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
return err return err
} }
if provider.Type == go_sms_sender.Aliyun {
for i, number := range phoneNumbers {
phoneNumbers[i] = strings.TrimPrefix(number, "+")
}
}
params := map[string]string{} params := map[string]string{}
if provider.Type == go_sms_sender.TencentCloud { if provider.Type == go_sms_sender.TencentCloud {
params["0"] = content params["0"] = content

View File

@ -18,6 +18,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"net/url" "net/url"
"path/filepath"
"strings" "strings"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
@ -54,6 +55,25 @@ func escapePath(path string) string {
return res return res
} }
func GetTruncatedPath(provider *Provider, fullFilePath string, limit int) string {
pathPrefix := util.UrlJoin(util.GetUrlPath(provider.Domain), provider.PathPrefix)
dir, file := filepath.Split(fullFilePath)
ext := filepath.Ext(file)
fileName := strings.TrimSuffix(file, ext)
for {
escapedString := escapePath(escapePath(fullFilePath))
if len(escapedString) < limit-len(pathPrefix) {
break
}
rs := []rune(fileName)
fileName = string(rs[0 : len(rs)-1])
fullFilePath = dir + fileName + ext
}
return fullFilePath
}
func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) { func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
escapedPath := util.UrlJoin(provider.PathPrefix, escapePath(fullFilePath)) escapedPath := util.UrlJoin(provider.PathPrefix, escapePath(fullFilePath))
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath) objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
@ -74,10 +94,15 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
} }
fileUrl := util.UrlJoin(host, escapePath(objectKey)) fileUrl := util.UrlJoin(host, escapePath(objectKey))
if hasTimestamp { if hasTimestamp {
fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime()) fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
} }
if provider.Type == "Tencent Cloud COS" {
objectKey = escapePath(objectKey)
}
return fileUrl, objectKey return fileUrl, objectKey
} }

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,11 +23,11 @@ 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 (
hourMinutes = 60 hourSeconds = int(time.Hour / time.Second)
InvalidRequest = "invalid_request" InvalidRequest = "invalid_request"
InvalidClient = "invalid_client" InvalidClient = "invalid_client"
InvalidGrant = "invalid_grant" InvalidGrant = "invalid_grant"
@ -306,7 +306,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
Code: util.GenerateClientId(), Code: util.GenerateClientId(),
AccessToken: accessToken, AccessToken: accessToken,
RefreshToken: refreshToken, RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourMinutes, ExpiresIn: application.ExpireInHours * hourSeconds,
Scope: scope, Scope: scope,
TokenType: "Bearer", TokenType: "Bearer",
CodeChallenge: challenge, CodeChallenge: challenge,
@ -321,7 +321,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
} }
} }
func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, username string, password string, host string, tag string, avatar string, lang string) interface{} { func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, username string, password string, host string, refreshToken string, tag string, avatar string, lang string) interface{} {
application := GetApplicationByClientId(clientId) application := GetApplicationByClientId(clientId)
if application == nil { if application == nil {
return &TokenError{ return &TokenError{
@ -348,6 +348,8 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
token, tokenError = GetPasswordToken(application, username, password, scope, host) token, tokenError = GetPasswordToken(application, username, password, scope, host)
case "client_credentials": // Client Credentials Grant case "client_credentials": // Client Credentials Grant
token, tokenError = GetClientCredentialsToken(application, clientSecret, scope, host) token, tokenError = GetClientCredentialsToken(application, clientSecret, scope, host)
case "refresh_token":
return RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
} }
if tag == "wechat_miniprogram" { if tag == "wechat_miniprogram" {
@ -440,7 +442,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
Code: util.GenerateClientId(), Code: util.GenerateClientId(),
AccessToken: newAccessToken, AccessToken: newAccessToken,
RefreshToken: newRefreshToken, RefreshToken: newRefreshToken,
ExpiresIn: application.ExpireInHours * hourMinutes, ExpiresIn: application.ExpireInHours * hourSeconds,
Scope: scope, Scope: scope,
TokenType: "Bearer", TokenType: "Bearer",
} }
@ -590,7 +592,7 @@ func GetPasswordToken(application *Application, username string, password string
Code: util.GenerateClientId(), Code: util.GenerateClientId(),
AccessToken: accessToken, AccessToken: accessToken,
RefreshToken: refreshToken, RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourMinutes, ExpiresIn: application.ExpireInHours * hourSeconds,
Scope: scope, Scope: scope,
TokenType: "Bearer", TokenType: "Bearer",
CodeIsUsed: true, CodeIsUsed: true,
@ -630,7 +632,7 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
User: nullUser.Name, User: nullUser.Name,
Code: util.GenerateClientId(), Code: util.GenerateClientId(),
AccessToken: accessToken, AccessToken: accessToken,
ExpiresIn: application.ExpireInHours * hourMinutes, ExpiresIn: application.ExpireInHours * hourSeconds,
Scope: scope, Scope: scope,
TokenType: "Bearer", TokenType: "Bearer",
CodeIsUsed: true, CodeIsUsed: true,
@ -657,7 +659,7 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
Code: util.GenerateClientId(), Code: util.GenerateClientId(),
AccessToken: accessToken, AccessToken: accessToken,
RefreshToken: refreshToken, RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourMinutes, ExpiresIn: application.ExpireInHours * hourSeconds,
Scope: scope, Scope: scope,
TokenType: "Bearer", TokenType: "Bearer",
CodeIsUsed: true, CodeIsUsed: true,

View File

@ -265,8 +265,8 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims) claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp) token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime) claimsWithoutThirdIdp.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claims.TokenType = "refresh-token" claimsWithoutThirdIdp.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp) refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
} }

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 (
@ -46,7 +46,8 @@ type User struct {
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"` PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
Email string `xorm:"varchar(100) index" json:"email"` Email string `xorm:"varchar(100) index" json:"email"`
EmailVerified bool `json:"emailVerified"` EmailVerified bool `json:"emailVerified"`
Phone string `xorm:"varchar(100) index" json:"phone"` Phone string `xorm:"varchar(20) index" json:"phone"`
CountryCode string `xorm:"varchar(6)" json:"countryCode"`
Location string `xorm:"varchar(100)" json:"location"` Location string `xorm:"varchar(100)" json:"location"`
Address []string `json:"address"` Address []string `json:"address"`
Affiliation string `xorm:"varchar(100)" json:"affiliation"` Affiliation string `xorm:"varchar(100)" json:"affiliation"`
@ -454,7 +455,7 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
} }
} }
if isGlobalAdmin { if isGlobalAdmin {
columns = append(columns, "name", "email", "phone") columns = append(columns, "name", "email", "phone", "country_code")
} }
affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols(columns...).Update(user) affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols(columns...).Update(user)
@ -578,7 +579,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 {
@ -170,3 +170,18 @@ func ClearUserOAuthProperties(user *User, providerType string) bool {
return affected != 0 return affected != 0
} }
func (user *User) GetCountryCode(countryCode string) string {
if countryCode != "" {
return countryCode
}
if user != nil && user.CountryCode != "" {
return user.CountryCode
}
if org := GetOrganizationByUser(user); org != nil && len(org.CountryCodes) > 0 {
return org.CountryCodes[0]
}
return ""
}

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 (
@ -122,11 +122,8 @@ func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordT
record.Owner = provider.Owner record.Owner = provider.Owner
record.Name = util.GenerateId() record.Name = util.GenerateId()
record.CreatedTime = util.GetCurrentTime() record.CreatedTime = util.GetCurrentTime()
if user != nil {
record.User = user.GetId()
}
record.Provider = provider.Name
record.Provider = provider.Name
record.Receiver = dest record.Receiver = dest
record.Code = code record.Code = code
record.Time = time.Now().Unix() record.Time = time.Now().Unix()

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

@ -57,7 +57,7 @@ func AutoSigninFilter(ctx *context.Context) {
return return
} }
// "/page?username=abc&password=123" // "/page?username=built-in/admin&password=123"
userId = ctx.Input.Query("username") userId = ctx.Input.Query("username")
password := ctx.Input.Query("password") password := ctx.Input.Query("password")
if userId != "" && password != "" && ctx.Input.Query("grant_type") == "" { if userId != "" && password != "" && ctx.Input.Query("grant_type") == "" {

View File

@ -19,6 +19,8 @@ import (
"strings" "strings"
"github.com/beego/beego/context" "github.com/beego/beego/context"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
@ -46,8 +48,17 @@ func responseError(ctx *context.Context, error string, data ...interface{}) {
} }
} }
func getAcceptLanguage(ctx *context.Context) string {
language := ctx.Request.Header.Get("Accept-Language")
return conf.GetLanguage(language)
}
func T(ctx *context.Context, error string) string {
return i18n.Translate(getAcceptLanguage(ctx), error)
}
func denyRequest(ctx *context.Context) { func denyRequest(ctx *context.Context) {
responseError(ctx, "Unauthorized operation") responseError(ctx, T(ctx, "auth:Unauthorized operation"))
} }
func getUsernameByClientIdSecret(ctx *context.Context) string { func getUsernameByClientIdSecret(ctx *context.Context) string {

View File

@ -119,8 +119,8 @@ func initAPI() {
beego.Router("/api/get-captcha", &controllers.ApiController{}, "GET:GetCaptcha") beego.Router("/api/get-captcha", &controllers.ApiController{}, "GET:GetCaptcha")
beego.Router("/api/get-ldap-user", &controllers.ApiController{}, "POST:GetLdapUser") beego.Router("/api/get-ldap-user", &controllers.ApiController{}, "POST:GetLdapUser")
beego.Router("/api/get-ldaps", &controllers.ApiController{}, "POST:GetLdaps") beego.Router("/api/get-ldaps", &controllers.ApiController{}, "GET:GetLdaps")
beego.Router("/api/get-ldap", &controllers.ApiController{}, "POST:GetLdap") beego.Router("/api/get-ldap", &controllers.ApiController{}, "GET:GetLdap")
beego.Router("/api/add-ldap", &controllers.ApiController{}, "POST:AddLdap") beego.Router("/api/add-ldap", &controllers.ApiController{}, "POST:AddLdap")
beego.Router("/api/update-ldap", &controllers.ApiController{}, "POST:UpdateLdap") beego.Router("/api/update-ldap", &controllers.ApiController{}, "POST:UpdateLdap")
beego.Router("/api/delete-ldap", &controllers.ApiController{}, "POST:DeleteLdap") beego.Router("/api/delete-ldap", &controllers.ApiController{}, "POST:DeleteLdap")
@ -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

@ -578,7 +578,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the product", "description": "The id ( owner/name ) of the product",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -894,8 +894,8 @@
"parameters": [ "parameters": [
{ {
"in": "query", "in": "query",
"name": "ID", "name": "id",
"description": "The ID(owner/name) of user.", "description": "The id ( owner/name )(owner/name) of user.",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -1107,7 +1107,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the application.", "description": "The id ( owner/name ) of the application.",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -1162,7 +1162,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the cert", "description": "The id ( owner/name ) of the cert",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -1340,7 +1340,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the model", "description": "The id ( owner/name ) of the model",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -1479,7 +1479,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the payment", "description": "The id ( owner/name ) of the payment",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -1534,7 +1534,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the permission", "description": "The id ( owner/name ) of the permission",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -1589,7 +1589,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the role", "description": "The id ( owner/name ) of the role",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -1638,7 +1638,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the product", "description": "The id ( owner/name ) of the product",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -1693,7 +1693,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the provider", "description": "The id ( owner/name ) of the provider",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -1840,7 +1840,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the role", "description": "The id ( owner/name ) of the role",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -1966,7 +1966,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the syncer", "description": "The id ( owner/name ) of the syncer",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -2021,7 +2021,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the user", "description": "The id ( owner/name ) of the user",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -2047,7 +2047,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of token", "description": "The id ( owner/name ) of token",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -2116,7 +2116,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the user", "description": "The id ( owner/name ) of the user",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -2160,7 +2160,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the user", "description": "The id ( owner/name ) of the user",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -2286,7 +2286,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the webhook", "description": "The id ( owner/name ) of the webhook",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -2341,7 +2341,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the payment", "description": "The id ( owner/name ) of the payment",
"required": true, "required": true,
"type": "string" "type": "string"
} }
@ -2504,8 +2504,8 @@
"parameters": [ "parameters": [
{ {
"in": "query", "in": "query",
"name": "user_id", "name": "id",
"description": "The id of user", "description": "The id ( owner/name ) of user",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -2904,7 +2904,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the application", "description": "The id ( owner/name ) of the application",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -2939,7 +2939,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the cert", "description": "The id ( owner/name ) of the cert",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -2982,7 +2982,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the model", "description": "The id ( owner/name ) of the model",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3017,7 +3017,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the organization", "description": "The id ( owner/name ) of the organization",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3052,7 +3052,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the payment", "description": "The id ( owner/name ) of the payment",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3087,7 +3087,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the permission", "description": "The id ( owner/name ) of the permission",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3122,7 +3122,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the product", "description": "The id ( owner/name ) of the product",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3157,7 +3157,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the provider", "description": "The id ( owner/name ) of the provider",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3200,7 +3200,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the role", "description": "The id ( owner/name ) of the role",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3235,7 +3235,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the syncer", "description": "The id ( owner/name ) of the syncer",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3270,7 +3270,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of token", "description": "The id ( owner/name ) of token",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3305,7 +3305,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the user", "description": "The id ( owner/name ) of the user",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3340,7 +3340,7 @@
{ {
"in": "query", "in": "query",
"name": "id", "name": "id",
"description": "The id of the webhook", "description": "The id ( owner/name ) of the webhook",
"required": true, "required": true,
"type": "string" "type": "string"
}, },
@ -3505,11 +3505,11 @@
} }
}, },
"definitions": { "definitions": {
"2268.0xc0000f9650.false": { "2346.0xc0001ce990.false": {
"title": "false", "title": "false",
"type": "object" "type": "object"
}, },
"2302.0xc0000f9680.false": { "2381.0xc0001ce9c0.false": {
"title": "false", "title": "false",
"type": "object" "type": "object"
}, },
@ -3636,10 +3636,10 @@
"type": "object", "type": "object",
"properties": { "properties": {
"data": { "data": {
"$ref": "#/definitions/2268.0xc0000f9650.false" "$ref": "#/definitions/2346.0xc0001ce990.false"
}, },
"data2": { "data2": {
"$ref": "#/definitions/2302.0xc0000f9680.false" "$ref": "#/definitions/2381.0xc0001ce9c0.false"
}, },
"msg": { "msg": {
"type": "string" "type": "string"
@ -3846,6 +3846,9 @@
"termsOfUse": { "termsOfUse": {
"type": "string" "type": "string"
}, },
"themeData": {
"$ref": "#/definitions/object.ThemeData"
},
"tokenFormat": { "tokenFormat": {
"type": "string" "type": "string"
} }
@ -4143,6 +4146,9 @@
"type": "string" "type": "string"
} }
}, },
"themeData": {
"$ref": "#/definitions/object.ThemeData"
},
"websiteUrl": { "websiteUrl": {
"type": "string" "type": "string"
} }
@ -4727,6 +4733,28 @@
} }
} }
}, },
"object.ThemeData": {
"title": "ThemeData",
"type": "object",
"properties": {
"borderRadius": {
"type": "integer",
"format": "int64"
},
"colorPrimary": {
"type": "string"
},
"isCompact": {
"type": "boolean"
},
"isEnabled": {
"type": "boolean"
},
"themeType": {
"type": "string"
}
}
},
"object.Token": { "object.Token": {
"title": "Token", "title": "Token",
"type": "object", "type": "object",
@ -4836,9 +4864,15 @@
"alipay": { "alipay": {
"type": "string" "type": "string"
}, },
"amazon": {
"type": "string"
},
"apple": { "apple": {
"type": "string" "type": "string"
}, },
"auth0": {
"type": "string"
},
"avatar": { "avatar": {
"type": "string" "type": "string"
}, },
@ -4848,6 +4882,9 @@
"baidu": { "baidu": {
"type": "string" "type": "string"
}, },
"battlenet": {
"type": "string"
},
"bilibili": { "bilibili": {
"type": "string" "type": "string"
}, },
@ -4857,9 +4894,18 @@
"birthday": { "birthday": {
"type": "string" "type": "string"
}, },
"bitbucket": {
"type": "string"
},
"box": {
"type": "string"
},
"casdoor": { "casdoor": {
"type": "string" "type": "string"
}, },
"cloudfoundry": {
"type": "string"
},
"createdIp": { "createdIp": {
"type": "string" "type": "string"
}, },
@ -4869,15 +4915,30 @@
"custom": { "custom": {
"type": "string" "type": "string"
}, },
"dailymotion": {
"type": "string"
},
"deezer": {
"type": "string"
},
"digitalocean": {
"type": "string"
},
"dingtalk": { "dingtalk": {
"type": "string" "type": "string"
}, },
"discord": {
"type": "string"
},
"displayName": { "displayName": {
"type": "string" "type": "string"
}, },
"douyin": { "douyin": {
"type": "string" "type": "string"
}, },
"dropbox": {
"type": "string"
},
"education": { "education": {
"type": "string" "type": "string"
}, },
@ -4887,15 +4948,24 @@
"emailVerified": { "emailVerified": {
"type": "boolean" "type": "boolean"
}, },
"eveonline": {
"type": "string"
},
"facebook": { "facebook": {
"type": "string" "type": "string"
}, },
"firstName": { "firstName": {
"type": "string" "type": "string"
}, },
"fitbit": {
"type": "string"
},
"gender": { "gender": {
"type": "string" "type": "string"
}, },
"gitea": {
"type": "string"
},
"gitee": { "gitee": {
"type": "string" "type": "string"
}, },
@ -4911,6 +4981,9 @@
"hash": { "hash": {
"type": "string" "type": "string"
}, },
"heroku": {
"type": "string"
},
"homepage": { "homepage": {
"type": "string" "type": "string"
}, },
@ -4923,9 +4996,18 @@
"idCardType": { "idCardType": {
"type": "string" "type": "string"
}, },
"influxcloud": {
"type": "string"
},
"infoflow": { "infoflow": {
"type": "string" "type": "string"
}, },
"instagram": {
"type": "string"
},
"intercom": {
"type": "string"
},
"isAdmin": { "isAdmin": {
"type": "boolean" "type": "boolean"
}, },
@ -4944,6 +5026,9 @@
"isOnline": { "isOnline": {
"type": "boolean" "type": "boolean"
}, },
"kakao": {
"type": "string"
},
"karma": { "karma": {
"type": "integer", "type": "integer",
"format": "int64" "format": "int64"
@ -4966,6 +5051,9 @@
"lastSigninWrongTime": { "lastSigninWrongTime": {
"type": "string" "type": "string"
}, },
"lastfm": {
"type": "string"
},
"ldap": { "ldap": {
"type": "string" "type": "string"
}, },
@ -4978,18 +5066,39 @@
"location": { "location": {
"type": "string" "type": "string"
}, },
"mailru": {
"type": "string"
},
"managedAccounts": { "managedAccounts": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/object.ManagedAccount" "$ref": "#/definitions/object.ManagedAccount"
} }
}, },
"meetup": {
"type": "string"
},
"microsoftonline": {
"type": "string"
},
"name": { "name": {
"type": "string" "type": "string"
}, },
"naver": {
"type": "string"
},
"nextcloud": {
"type": "string"
},
"okta": { "okta": {
"type": "string" "type": "string"
}, },
"onedrive": {
"type": "string"
},
"oura": {
"type": "string"
},
"owner": { "owner": {
"type": "string" "type": "string"
}, },
@ -4999,6 +5108,12 @@
"passwordSalt": { "passwordSalt": {
"type": "string" "type": "string"
}, },
"patreon": {
"type": "string"
},
"paypal": {
"type": "string"
},
"permanentAvatar": { "permanentAvatar": {
"type": "string" "type": "string"
}, },
@ -5035,10 +5150,16 @@
"$ref": "#/definitions/object.Role" "$ref": "#/definitions/object.Role"
} }
}, },
"salesforce": {
"type": "string"
},
"score": { "score": {
"type": "integer", "type": "integer",
"format": "int64" "format": "int64"
}, },
"shopify": {
"type": "string"
},
"signinWrongTimes": { "signinWrongTimes": {
"type": "integer", "type": "integer",
"format": "int64" "format": "int64"
@ -5049,21 +5170,54 @@
"slack": { "slack": {
"type": "string" "type": "string"
}, },
"soundcloud": {
"type": "string"
},
"spotify": {
"type": "string"
},
"steam": { "steam": {
"type": "string" "type": "string"
}, },
"strava": {
"type": "string"
},
"stripe": {
"type": "string"
},
"tag": { "tag": {
"type": "string" "type": "string"
}, },
"tiktok": {
"type": "string"
},
"title": { "title": {
"type": "string" "type": "string"
}, },
"tumblr": {
"type": "string"
},
"twitch": {
"type": "string"
},
"twitter": {
"type": "string"
},
"type": { "type": {
"type": "string" "type": "string"
}, },
"typetalk": {
"type": "string"
},
"uber": {
"type": "string"
},
"updatedTime": { "updatedTime": {
"type": "string" "type": "string"
}, },
"vk": {
"type": "string"
},
"webauthnCredentials": { "webauthnCredentials": {
"type": "array", "type": "array",
"items": { "items": {
@ -5078,6 +5232,24 @@
}, },
"weibo": { "weibo": {
"type": "string" "type": "string"
},
"wepay": {
"type": "string"
},
"xero": {
"type": "string"
},
"yahoo": {
"type": "string"
},
"yammer": {
"type": "string"
},
"yandex": {
"type": "string"
},
"zoom": {
"type": "string"
} }
} }
}, },

View File

@ -375,7 +375,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the product description: The id ( owner/name ) of the product
required: true required: true
type: string type: string
- in: query - in: query
@ -578,8 +578,8 @@ paths:
operationId: ApiController.DeleteSession operationId: ApiController.DeleteSession
parameters: parameters:
- in: query - in: query
name: ID name: id
description: The ID(owner/name) of user. description: The id ( owner/name )(owner/name) of user.
required: true required: true
type: string type: string
responses: responses:
@ -718,7 +718,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the application. description: The id ( owner/name ) of the application.
required: true required: true
type: string type: string
responses: responses:
@ -754,7 +754,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the cert description: The id ( owner/name ) of the cert
required: true required: true
type: string type: string
responses: responses:
@ -870,7 +870,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the model description: The id ( owner/name ) of the model
required: true required: true
type: string type: string
responses: responses:
@ -961,7 +961,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the payment description: The id ( owner/name ) of the payment
required: true required: true
type: string type: string
responses: responses:
@ -997,7 +997,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the permission description: The id ( owner/name ) of the permission
required: true required: true
type: string type: string
responses: responses:
@ -1033,7 +1033,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the role description: The id ( owner/name ) of the role
required: true required: true
type: string type: string
responses: responses:
@ -1065,7 +1065,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the product description: The id ( owner/name ) of the product
required: true required: true
type: string type: string
responses: responses:
@ -1101,7 +1101,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the provider description: The id ( owner/name ) of the provider
required: true required: true
type: string type: string
responses: responses:
@ -1197,7 +1197,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the role description: The id ( owner/name ) of the role
required: true required: true
type: string type: string
responses: responses:
@ -1280,7 +1280,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the syncer description: The id ( owner/name ) of the syncer
required: true required: true
type: string type: string
responses: responses:
@ -1316,7 +1316,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the user description: The id ( owner/name ) of the user
required: true required: true
type: string type: string
responses: responses:
@ -1333,7 +1333,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of token description: The id ( owner/name ) of token
required: true required: true
type: string type: string
responses: responses:
@ -1379,7 +1379,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the user description: The id ( owner/name ) of the user
required: true required: true
type: string type: string
- in: query - in: query
@ -1408,7 +1408,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the user description: The id ( owner/name ) of the user
required: true required: true
type: string type: string
responses: responses:
@ -1491,7 +1491,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the webhook description: The id ( owner/name ) of the webhook
required: true required: true
type: string type: string
responses: responses:
@ -1527,7 +1527,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the payment description: The id ( owner/name ) of the payment
required: true required: true
type: string type: string
responses: responses:
@ -1636,8 +1636,8 @@ paths:
operationId: ApiController.GetOAuthCode operationId: ApiController.GetOAuthCode
parameters: parameters:
- in: query - in: query
name: user_id name: id
description: The id of user description: The id ( owner/name ) of user
required: true required: true
type: string type: string
- in: query - in: query
@ -1902,7 +1902,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the application description: The id ( owner/name ) of the application
required: true required: true
type: string type: string
- in: body - in: body
@ -1925,7 +1925,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the cert description: The id ( owner/name ) of the cert
required: true required: true
type: string type: string
- in: body - in: body
@ -1953,7 +1953,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the model description: The id ( owner/name ) of the model
required: true required: true
type: string type: string
- in: body - in: body
@ -1976,7 +1976,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the organization description: The id ( owner/name ) of the organization
required: true required: true
type: string type: string
- in: body - in: body
@ -1999,7 +1999,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the payment description: The id ( owner/name ) of the payment
required: true required: true
type: string type: string
- in: body - in: body
@ -2022,7 +2022,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the permission description: The id ( owner/name ) of the permission
required: true required: true
type: string type: string
- in: body - in: body
@ -2045,7 +2045,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the product description: The id ( owner/name ) of the product
required: true required: true
type: string type: string
- in: body - in: body
@ -2068,7 +2068,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the provider description: The id ( owner/name ) of the provider
required: true required: true
type: string type: string
- in: body - in: body
@ -2096,7 +2096,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the role description: The id ( owner/name ) of the role
required: true required: true
type: string type: string
- in: body - in: body
@ -2119,7 +2119,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the syncer description: The id ( owner/name ) of the syncer
required: true required: true
type: string type: string
- in: body - in: body
@ -2142,7 +2142,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of token description: The id ( owner/name ) of token
required: true required: true
type: string type: string
- in: body - in: body
@ -2165,7 +2165,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the user description: The id ( owner/name ) of the user
required: true required: true
type: string type: string
- in: body - in: body
@ -2188,7 +2188,7 @@ paths:
parameters: parameters:
- in: query - in: query
name: id name: id
description: The id of the webhook description: The id ( owner/name ) of the webhook
required: true required: true
type: string type: string
- in: body - in: body
@ -2293,10 +2293,10 @@ paths:
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
definitions: definitions:
2268.0xc0000f9650.false: 2346.0xc0001ce990.false:
title: "false" title: "false"
type: object type: object
2302.0xc0000f9680.false: 2381.0xc0001ce9c0.false:
title: "false" title: "false"
type: object type: object
Response: Response:
@ -2383,9 +2383,9 @@ definitions:
type: object type: object
properties: properties:
data: data:
$ref: '#/definitions/2268.0xc0000f9650.false' $ref: '#/definitions/2346.0xc0001ce990.false'
data2: data2:
$ref: '#/definitions/2302.0xc0000f9680.false' $ref: '#/definitions/2381.0xc0001ce9c0.false'
msg: msg:
type: string type: string
name: name:
@ -2524,6 +2524,8 @@ definitions:
type: string type: string
termsOfUse: termsOfUse:
type: string type: string
themeData:
$ref: '#/definitions/object.ThemeData'
tokenFormat: tokenFormat:
type: string type: string
object.Cert: object.Cert:
@ -2724,6 +2726,8 @@ definitions:
type: array type: array
items: items:
type: string type: string
themeData:
$ref: '#/definitions/object.ThemeData'
websiteUrl: websiteUrl:
type: string type: string
object.Payment: object.Payment:
@ -3115,6 +3119,21 @@ definitions:
type: array type: array
items: items:
type: string type: string
object.ThemeData:
title: ThemeData
type: object
properties:
borderRadius:
type: integer
format: int64
colorPrimary:
type: string
isCompact:
type: boolean
isEnabled:
type: boolean
themeType:
type: string
object.Token: object.Token:
title: Token title: Token
type: object type: object
@ -3190,46 +3209,74 @@ definitions:
type: string type: string
alipay: alipay:
type: string type: string
amazon:
type: string
apple: apple:
type: string type: string
auth0:
type: string
avatar: avatar:
type: string type: string
azuread: azuread:
type: string type: string
baidu: baidu:
type: string type: string
battlenet:
type: string
bilibili: bilibili:
type: string type: string
bio: bio:
type: string type: string
birthday: birthday:
type: string type: string
bitbucket:
type: string
box:
type: string
casdoor: casdoor:
type: string type: string
cloudfoundry:
type: string
createdIp: createdIp:
type: string type: string
createdTime: createdTime:
type: string type: string
custom: custom:
type: string type: string
dailymotion:
type: string
deezer:
type: string
digitalocean:
type: string
dingtalk: dingtalk:
type: string type: string
discord:
type: string
displayName: displayName:
type: string type: string
douyin: douyin:
type: string type: string
dropbox:
type: string
education: education:
type: string type: string
email: email:
type: string type: string
emailVerified: emailVerified:
type: boolean type: boolean
eveonline:
type: string
facebook: facebook:
type: string type: string
firstName: firstName:
type: string type: string
fitbit:
type: string
gender: gender:
type: string type: string
gitea:
type: string
gitee: gitee:
type: string type: string
github: github:
@ -3240,6 +3287,8 @@ definitions:
type: string type: string
hash: hash:
type: string type: string
heroku:
type: string
homepage: homepage:
type: string type: string
id: id:
@ -3248,8 +3297,14 @@ definitions:
type: string type: string
idCardType: idCardType:
type: string type: string
influxcloud:
type: string
infoflow: infoflow:
type: string type: string
instagram:
type: string
intercom:
type: string
isAdmin: isAdmin:
type: boolean type: boolean
isDefaultAvatar: isDefaultAvatar:
@ -3262,6 +3317,8 @@ definitions:
type: boolean type: boolean
isOnline: isOnline:
type: boolean type: boolean
kakao:
type: string
karma: karma:
type: integer type: integer
format: int64 format: int64
@ -3277,6 +3334,8 @@ definitions:
type: string type: string
lastSigninWrongTime: lastSigninWrongTime:
type: string type: string
lastfm:
type: string
ldap: ldap:
type: string type: string
line: line:
@ -3285,20 +3344,38 @@ definitions:
type: string type: string
location: location:
type: string type: string
mailru:
type: string
managedAccounts: managedAccounts:
type: array type: array
items: items:
$ref: '#/definitions/object.ManagedAccount' $ref: '#/definitions/object.ManagedAccount'
meetup:
type: string
microsoftonline:
type: string
name: name:
type: string type: string
naver:
type: string
nextcloud:
type: string
okta: okta:
type: string type: string
onedrive:
type: string
oura:
type: string
owner: owner:
type: string type: string
password: password:
type: string type: string
passwordSalt: passwordSalt:
type: string type: string
patreon:
type: string
paypal:
type: string
permanentAvatar: permanentAvatar:
type: string type: string
permissions: permissions:
@ -3323,9 +3400,13 @@ definitions:
type: array type: array
items: items:
$ref: '#/definitions/object.Role' $ref: '#/definitions/object.Role'
salesforce:
type: string
score: score:
type: integer type: integer
format: int64 format: int64
shopify:
type: string
signinWrongTimes: signinWrongTimes:
type: integer type: integer
format: int64 format: int64
@ -3333,16 +3414,38 @@ definitions:
type: string type: string
slack: slack:
type: string type: string
soundcloud:
type: string
spotify:
type: string
steam: steam:
type: string type: string
strava:
type: string
stripe:
type: string
tag: tag:
type: string type: string
tiktok:
type: string
title: title:
type: string type: string
tumblr:
type: string
twitch:
type: string
twitter:
type: string
type: type:
type: string type: string
typetalk:
type: string
uber:
type: string
updatedTime: updatedTime:
type: string type: string
vk:
type: string
webauthnCredentials: webauthnCredentials:
type: array type: array
items: items:
@ -3353,6 +3456,18 @@ definitions:
type: string type: string
weibo: weibo:
type: string type: string
wepay:
type: string
xero:
type: string
yahoo:
type: string
yammer:
type: string
yandex:
type: string
zoom:
type: string
object.Userinfo: object.Userinfo:
title: Userinfo title: Userinfo
type: object type: object

View File

@ -14,6 +14,8 @@
package util package util
import "sort"
func DeleteVal(values []string, val string) []string { func DeleteVal(values []string, val string) []string {
newValues := []string{} newValues := []string{}
for _, v := range values { for _, v := range values {
@ -23,3 +25,8 @@ func DeleteVal(values []string, val string) []string {
} }
return newValues return newValues
} }
func ContainsString(values []string, val string) bool {
sort.Strings(values)
return sort.SearchStrings(values, val) != len(values)
}

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,12 +136,16 @@ 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[:])
} }
func IsStrsEmpty(strs ...string) bool { func IsStringsEmpty(strs ...string) bool {
for _, str := range strs { for _, str := range strs {
if len(str) == 0 { if len(str) == 0 {
return true return true
@ -214,7 +227,7 @@ func IsChinese(str string) bool {
} }
func GetMaskedPhone(phone string) string { func GetMaskedPhone(phone string) string {
return getMaskedPhone(phone) return rePhone.ReplaceAllString(phone, "$1****$2")
} }
func GetMaskedEmail(email string) string { func GetMaskedEmail(email string) string {

View File

@ -183,7 +183,7 @@ func TestIsStrsEmpty(t *testing.T) {
} }
for _, scenery := range scenarios { for _, scenery := range scenarios {
t.Run(scenery.description, func(t *testing.T) { t.Run(scenery.description, func(t *testing.T) {
actual := IsStrsEmpty(scenery.input...) actual := IsStringsEmpty(scenery.input...)
assert.Equal(t, scenery.expected, actual, "The returned value not is expected") assert.Equal(t, scenery.expected, actual, "The returned value not is expected")
}) })
} }

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

@ -31,6 +31,6 @@ func GetCurrentUnixTime() string {
func IsTokenExpired(createdTime string, expiresIn int) bool { func IsTokenExpired(createdTime string, expiresIn int) bool {
createdTimeObj, _ := time.Parse(time.RFC3339, createdTime) createdTimeObj, _ := time.Parse(time.RFC3339, createdTime)
expiresAtObj := createdTimeObj.Add(time.Duration(expiresIn) * time.Minute) expiresAtObj := createdTimeObj.Add(time.Duration(expiresIn) * time.Second)
return time.Now().After(expiresAtObj) return time.Now().After(expiresAtObj)
} }

View File

@ -56,15 +56,15 @@ func Test_IsTokenExpired(t *testing.T) {
description: "Token emitted now is valid for 60 minutes", description: "Token emitted now is valid for 60 minutes",
input: input{ input: input{
createdTime: time.Now().Format(time.RFC3339), createdTime: time.Now().Format(time.RFC3339),
expiresIn: 60, expiresIn: 3600,
}, },
expected: false, expected: false,
}, },
{ {
description: "Token emitted 60 minutes before now is valid for 60 minutes", description: "Token emitted 60 minutes before now is valid for 61 minutes",
input: input{ input: input{
createdTime: time.Now().Add(-time.Minute * 60).Format(time.RFC3339), createdTime: time.Now().Add(-time.Minute * 60).Format(time.RFC3339),
expiresIn: 61, expiresIn: 3660,
}, },
expected: false, expected: false,
}, },
@ -72,7 +72,7 @@ func Test_IsTokenExpired(t *testing.T) {
description: "Token emitted 2 hours before now is Expired after 60 minutes", description: "Token emitted 2 hours before now is Expired after 60 minutes",
input: input{ input: input{
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339), createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
expiresIn: 60, expiresIn: 3600,
}, },
expected: true, expected: true,
}, },
@ -80,23 +80,23 @@ func Test_IsTokenExpired(t *testing.T) {
description: "Token emitted 61 minutes before now is Expired after 60 minutes", description: "Token emitted 61 minutes before now is Expired after 60 minutes",
input: input{ input: input{
createdTime: time.Now().Add(-time.Minute * 61).Format(time.RFC3339), createdTime: time.Now().Add(-time.Minute * 61).Format(time.RFC3339),
expiresIn: 60, expiresIn: 3600,
}, },
expected: true, expected: true,
}, },
{ {
description: "Token emitted 2 hours before now is valid for 120 minutes", description: "Token emitted 2 hours before now is valid for 121 minutes",
input: input{ input: input{
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339), createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
expiresIn: 121, expiresIn: 7260,
}, },
expected: false, expected: false,
}, },
{ {
description: "Token emitted 159 minutes before now is Expired after 60 minutes", description: "Token emitted 159 minutes before now is Expired after 120 minutes",
input: input{ input: input{
createdTime: time.Now().Add(-time.Minute * 159).Format(time.RFC3339), createdTime: time.Now().Add(-time.Minute * 159).Format(time.RFC3339),
expiresIn: 120, expiresIn: 7200,
}, },
expected: true, expected: true,
}, },

View File

@ -17,16 +17,13 @@ package util
import ( import (
"net/mail" "net/mail"
"regexp" "regexp"
"github.com/nyaruka/phonenumbers"
) )
var ( var rePhone *regexp.Regexp
rePhoneCn *regexp.Regexp
rePhone *regexp.Regexp
)
func init() { func init() {
// https://learnku.com/articles/31543
rePhoneCn, _ = regexp.Compile(`^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$`)
rePhone, _ = regexp.Compile("(\\d{3})\\d*(\\d{4})") rePhone, _ = regexp.Compile("(\\d{3})\\d*(\\d{4})")
} }
@ -35,10 +32,19 @@ func IsEmailValid(email string) bool {
return err == nil return err == nil
} }
func IsPhoneCnValid(phone string) bool { func IsPhoneValid(phone string, countryCode string) bool {
return rePhoneCn.MatchString(phone) phoneNumber, err := phonenumbers.Parse(phone, countryCode)
if err != nil {
return false
}
return phonenumbers.IsValidNumber(phoneNumber)
} }
func getMaskedPhone(phone string) string { func IsPhoneAllowInRegin(countryCode string, allowRegions []string) bool {
return rePhone.ReplaceAllString(phone, "$1****$2") return !ContainsString(allowRegions, countryCode)
}
func GetE164Number(phone string, countryCode string) (string, bool) {
phoneNumber, _ := phonenumbers.Parse(phone, countryCode)
return phonenumbers.Format(phoneNumber, phonenumbers.E164), phonenumbers.IsValidNumber(phoneNumber)
} }

View File

@ -15,7 +15,6 @@ describe("Login test", () => {
"password": "123", "password": "123",
"autoSignin": true, "autoSignin": true,
"type": "login", "type": "login",
"phonePrefix": "86",
}, },
}).then((Response) => { }).then((Response) => {
expect(Response).property("body").property("status").to.equal("ok"); expect(Response).property("body").property("status").to.equal("ok");
@ -40,7 +39,6 @@ describe("Login test", () => {
"password": "1234", "password": "1234",
"autoSignin": true, "autoSignin": true,
"type": "login", "type": "login",
"phonePrefix": "86",
}, },
}).then((Response) => { }).then((Response) => {
expect(Response).property("body").property("status").to.equal("error"); expect(Response).property("body").property("status").to.equal("error");

View File

@ -34,7 +34,6 @@ Cypress.Commands.add('login', ()=>{
"password": "123", "password": "123",
"autoSignin": true, "autoSignin": true,
"type": "login", "type": "login",
"phonePrefix": "86",
}, },
}).then((Response) => { }).then((Response) => {
expect(Response).property("body").property("status").to.equal("ok"); expect(Response).property("body").property("status").to.equal("ok");

View File

@ -3,6 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@ant-design/cssinjs": "^1.5.6",
"@ant-design/icons": "^4.7.0", "@ant-design/icons": "^4.7.0",
"@craco/craco": "^6.4.5", "@craco/craco": "^6.4.5",
"@crowdin/cli": "^3.7.10", "@crowdin/cli": "^3.7.10",
@ -21,6 +22,7 @@
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"i18n-iso-countries": "^7.0.0", "i18n-iso-countries": "^7.0.0",
"i18next": "^19.8.9", "i18next": "^19.8.9",
"libphonenumber-js": "^1.10.19",
"moment": "^2.29.1", "moment": "^2.29.1",
"qs": "^6.10.2", "qs": "^6.10.2",
"react": "^18.2.0", "react": "^18.2.0",

View File

@ -78,6 +78,7 @@ class AccountTable extends React.Component {
{name: "Password", displayName: i18next.t("general:Password")}, {name: "Password", displayName: i18next.t("general:Password")},
{name: "Email", displayName: i18next.t("general:Email")}, {name: "Email", displayName: i18next.t("general:Email")},
{name: "Phone", displayName: i18next.t("general:Phone")}, {name: "Phone", displayName: i18next.t("general:Phone")},
{name: "Country code", displayName: i18next.t("user:Country code")},
{name: "Country/Region", displayName: i18next.t("user:Country/Region")}, {name: "Country/Region", displayName: i18next.t("user:Country/Region")},
{name: "Location", displayName: i18next.t("user:Location")}, {name: "Location", displayName: i18next.t("user:Location")},
{name: "Affiliation", displayName: i18next.t("user:Affiliation")}, {name: "Affiliation", displayName: i18next.t("user:Affiliation")},

View File

@ -261,7 +261,7 @@ class AdapterListPage extends BaseListPage {
searchedColumn: params.searchedColumn, searchedColumn: params.searchedColumn,
}); });
} else { } else {
if (res.msg.includes("Unauthorized")) { if (Setting.isResponseDenied(res)) {
this.setState({ this.setState({
loading: false, loading: false,
isAuthorized: false, isAuthorized: false,

View File

@ -16,8 +16,9 @@ import React, {Component} from "react";
import "./App.less"; import "./App.less";
import {Helmet} from "react-helmet"; import {Helmet} from "react-helmet";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import {BarsOutlined, DownOutlined, LogoutOutlined, SettingOutlined} from "@ant-design/icons"; import {StyleProvider} from "@ant-design/cssinjs";
import {Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} from "antd"; import {BarsOutlined, DownOutlined, InfoCircleFilled, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
import {Alert, Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} from "antd";
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom"; import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
import OrganizationListPage from "./OrganizationListPage"; import OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage"; import OrganizationEditPage from "./OrganizationEditPage";
@ -84,8 +85,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();
@ -554,6 +555,13 @@ class App extends Component {
}; };
renderContent() { renderContent() {
const onClick = ({key}) => {
if (key === "/swagger") {
window.open(Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger", "_blank");
} else {
this.props.history.push(key);
}
};
return ( return (
<Layout id="parent-area"> <Layout id="parent-area">
{/* https://github.com/ant-design/ant-design/issues/40394 ant design bug. If it will be fixed, we can delete the code for control the color of Header*/} {/* https://github.com/ant-design/ant-design/issues/40394 ant design bug. If it will be fixed, we can delete the code for control the color of Header*/}
@ -580,6 +588,7 @@ class App extends Component {
</Button> </Button>
</React.Fragment> : </React.Fragment> :
<Menu <Menu
onClick={onClick}
items={this.getMenuItems()} items={this.getMenuItems()}
mode={"horizontal"} mode={"horizontal"}
selectedKeys={[this.state.selectedMenuKey]} selectedKeys={[this.state.selectedMenuKey]}
@ -663,6 +672,9 @@ class App extends Component {
return ( return (
<React.Fragment> <React.Fragment>
{
this.renderBanner()
}
<FloatButton.BackTop /> <FloatButton.BackTop />
<CustomGithubCorner /> <CustomGithubCorner />
{ {
@ -672,6 +684,32 @@ class App extends Component {
); );
} }
renderBanner() {
if (!Conf.IsDemoMode) {
return null;
}
const language = Setting.getLanguage();
if (language === "en" || language === "zh") {
return null;
}
return (
<Alert type="info" banner showIcon={false} closable message={
<div style={{textAlign: "center"}}>
<InfoCircleFilled style={{color: "rgb(87,52,211)"}} />
&nbsp;&nbsp;
{i18next.t("general:Found some texts still not translated? Please help us translate at")}
&nbsp;
<a target="_blank" rel="noreferrer" href={"https://crowdin.com/project/casdoor-site"}>
Crowdin
</a>
&nbsp;!&nbsp;🙏
</div>
} />
);
}
render() { render() {
return ( return (
<React.Fragment> <React.Fragment>
@ -692,9 +730,11 @@ class App extends Component {
}, },
algorithm: Setting.getAlgorithm(this.state.themeAlgorithm), algorithm: Setting.getAlgorithm(this.state.themeAlgorithm),
}}> }}>
{ <StyleProvider hashPriority="high">
this.renderPage() {
} this.renderPage()
}
</StyleProvider>
</ConfigProvider> </ConfigProvider>
</React.Fragment> </React.Fragment>
); );

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

@ -287,7 +287,7 @@ class ApplicationListPage extends BaseListPage {
searchedColumn: params.searchedColumn, searchedColumn: params.searchedColumn,
}); });
} else { } else {
if (res.msg.includes("Unauthorized")) { if (Setting.isResponseDenied(res)) {
this.setState({ this.setState({
loading: false, loading: false,
isAuthorized: false, isAuthorized: false,

View File

@ -228,7 +228,7 @@ class CertListPage extends BaseListPage {
searchedColumn: params.searchedColumn, searchedColumn: params.searchedColumn,
}); });
} else { } else {
if (res.msg.includes("Unauthorized")) { if (Setting.isResponseDenied(res)) {
this.setState({ this.setState({
loading: false, loading: false,
isAuthorized: false, isAuthorized: false,

View File

@ -14,9 +14,17 @@
export const ShowGithubCorner = false; export const ShowGithubCorner = false;
export const GithubRepo = "https://github.com/casdoor/casdoor"; export const GithubRepo = "https://github.com/casdoor/casdoor";
export const IsDemoMode = false;
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,
};

Some files were not shown because too many files have changed in this diff Show More