mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-08 00:50:28 +08:00
Compare commits
143 Commits
Author | SHA1 | Date | |
---|---|---|---|
ae1634a4d5 | |||
bdf9864f69 | |||
72839d6bf5 | |||
2c4b1093ed | |||
d1c55d5aa7 | |||
c8aa35c9c6 | |||
6037f37b87 | |||
1b478903d8 | |||
4f5ac7a10b | |||
e81ba62234 | |||
a19060c7cb | |||
96812f676b | |||
04f0458b5c | |||
fd0bcd9a17 | |||
01a5958307 | |||
be88b00278 | |||
1bd0245e7a | |||
cc84bd37cf | |||
8302fcf805 | |||
391a533ce1 | |||
57431a59ad | |||
88a4736520 | |||
2cb6ff69ae | |||
e1e5943a3e | |||
3875896c1e | |||
7e2f265420 | |||
53ef179e9b | |||
376ef0ed14 | |||
ca183be336 | |||
e5da57a005 | |||
e4e225db32 | |||
a1add992ee | |||
2aac265ed4 | |||
2dc755f529 | |||
0dd474d5fc | |||
6998451e97 | |||
9175e5b664 | |||
dbc6b0dc45 | |||
31b7000f6a | |||
d25eaa65cd | |||
f5bcd00652 | |||
0d5f49e40a | |||
3527e070a0 | |||
0108b58db4 | |||
976b5766a5 | |||
a92d20162a | |||
204b1c2b8c | |||
49fb269170 | |||
c532a5d54d | |||
89df80baca | |||
d988ac814c | |||
e4b25055d5 | |||
4123d47174 | |||
fbdd5a926d | |||
92b6fda0f6 | |||
6a7ac35e65 | |||
fc137b9f76 | |||
11dbd5ba9a | |||
19942a8bd4 | |||
f9ee8a68cb | |||
f241336ad7 | |||
8b64d113fb | |||
a8800c4d5c | |||
75fc9ab9f7 | |||
d06da76c3d | |||
bc399837cc | |||
265abfe102 | |||
12acb24dbc | |||
ba1ddc7e50 | |||
59e07a35aa | |||
cabe830f55 | |||
78af5daec3 | |||
6c76913f71 | |||
5a0d1bcb6e | |||
37232faa07 | |||
4d9c81ef96 | |||
b0d87f60ae | |||
a5499219d1 | |||
6a813a1f8c | |||
e4cf244cf8 | |||
f5a6415e57 | |||
13e871043c | |||
a8699d0b87 | |||
6621d693de | |||
dc3131c683 | |||
042a8d0ad6 | |||
44abfb3430 | |||
53b8424a1f | |||
23c2ba3a2b | |||
3a9ffedce4 | |||
03f005389f | |||
69a8346d05 | |||
546512a0ea | |||
c4a307b9ec | |||
d731c3c934 | |||
4a68dd65cd | |||
d59148890e | |||
7f52755e32 | |||
eaa6f50085 | |||
f35a5f9a47 | |||
7481b229a4 | |||
39e485ae82 | |||
764c64e67c | |||
e755a7331d | |||
6d9d595f86 | |||
d52058d2ae | |||
bcfbfc6947 | |||
75699c4a26 | |||
3e8bfb52a8 | |||
bbbd857a45 | |||
498900df76 | |||
7e3c1a6581 | |||
6e28043dba | |||
cb200687dc | |||
23bb0ee450 | |||
117259dfc5 | |||
e71d0476f0 | |||
b5d26767b2 | |||
5c4e22288e | |||
3ac4be64b8 | |||
97db54b6b9 | |||
3a19d4c7c8 | |||
a60be2b2ab | |||
06ef97a080 | |||
167c1b0f1b | |||
7d0eae230e | |||
901867e8bb | |||
b7be1943fa | |||
bbbda1982f | |||
e593f5be5b | |||
0918757e85 | |||
ce0d45a70b | |||
c4096788b2 | |||
523186f895 | |||
ef373ca736 | |||
721a681ff1 | |||
8b1c4b0c75 | |||
540f22f8bd | |||
79f81f1356 | |||
4e145f71b5 | |||
104f975a2f | |||
71bb400559 | |||
93c3c78d42 |
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@ -108,6 +108,7 @@ jobs:
|
||||
working-directory: ./web
|
||||
- uses: cypress-io/github-action@v5
|
||||
with:
|
||||
browser: chrome
|
||||
start: yarn start
|
||||
wait-on: 'http://localhost:7001'
|
||||
wait-on-timeout: 210
|
||||
@ -127,7 +128,7 @@ jobs:
|
||||
release-and-push:
|
||||
name: Release And Push
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push'
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
|
||||
needs: [ frontend, backend, linter, e2e ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -182,14 +183,14 @@ jobs:
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Push to Docker Hub
|
||||
uses: docker/build-push-action@v3
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
with:
|
||||
context: .
|
||||
target: STANDARD
|
||||
@ -199,7 +200,7 @@ jobs:
|
||||
|
||||
- name: Push All In One Version to Docker Hub
|
||||
uses: docker/build-push-action@v3
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
with:
|
||||
context: .
|
||||
target: ALLINONE
|
||||
|
2
.github/workflows/sync.yml
vendored
2
.github/workflows/sync.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
synchronize-with-crowdin:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push'
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
|
@ -87,8 +87,7 @@ https://casdoor.org/docs/category/integrations
|
||||
## How to contact?
|
||||
|
||||
- Discord: https://discord.gg/5rPsrAzK7S
|
||||
- Forum: https://forum.casbin.com
|
||||
- Contact: https://tawk.to/chat/623352fea34c2456412b8c51/1fuc7od6e
|
||||
- Contact: https://casdoor.org/help
|
||||
|
||||
## Contribute
|
||||
|
||||
|
@ -51,7 +51,8 @@ p, *, *, GET, /api/get-account, *, *
|
||||
p, *, *, GET, /api/userinfo, *, *
|
||||
p, *, *, GET, /api/user, *, *
|
||||
p, *, *, GET, /api/health, *, *
|
||||
p, *, *, POST, /api/webhook, *, *
|
||||
p, *, *, *, /api/webhook, *, *
|
||||
p, *, *, GET, /api/get-qrcode, *, *
|
||||
p, *, *, GET, /api/get-webhook-event, *, *
|
||||
p, *, *, GET, /api/get-captcha-status, *, *
|
||||
p, *, *, *, /api/login/oauth, *, *
|
||||
@ -80,6 +81,7 @@ p, *, *, *, /.well-known/jwks, *, *
|
||||
p, *, *, GET, /api/get-saml-login, *, *
|
||||
p, *, *, POST, /api/acs, *, *
|
||||
p, *, *, GET, /api/saml/metadata, *, *
|
||||
p, *, *, *, /api/saml/redirect, *, *
|
||||
p, *, *, *, /cas, *, *
|
||||
p, *, *, *, /scim, *, *
|
||||
p, *, *, *, /api/webauthn, *, *
|
||||
@ -95,6 +97,8 @@ p, *, *, GET, /api/get-organization-names, *, *
|
||||
p, *, *, GET, /api/get-all-objects, *, *
|
||||
p, *, *, GET, /api/get-all-actions, *, *
|
||||
p, *, *, GET, /api/get-all-roles, *, *
|
||||
p, *, *, GET, /api/get-invitation-info, *, *
|
||||
p, *, *, GET, /api/faceid-signin-begin, *, *
|
||||
`
|
||||
|
||||
sa := stringadapter.NewAdapter(ruleText)
|
||||
@ -150,7 +154,7 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
|
||||
|
||||
func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
||||
if method == "POST" {
|
||||
if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/callback" || urlPath == "/api/send-verification-code" || urlPath == "/api/send-email" || urlPath == "/api/verify-captcha" || urlPath == "/api/verify-code" || urlPath == "/api/check-user-password" || strings.HasPrefix(urlPath, "/api/mfa/") {
|
||||
if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/callback" || urlPath == "/api/send-verification-code" || urlPath == "/api/send-email" || urlPath == "/api/verify-captcha" || urlPath == "/api/verify-code" || urlPath == "/api/check-user-password" || strings.HasPrefix(urlPath, "/api/mfa/") || urlPath == "/api/webhook" || urlPath == "/api/get-qrcode" {
|
||||
return true
|
||||
} else if urlPath == "/api/update-user" {
|
||||
// Allow ordinary users to update their own information
|
||||
@ -158,6 +162,11 @@ func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else if urlPath == "/api/upload-resource" {
|
||||
if subOwner == "app" && subName == "app-casibase" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
14
conf/conf.go
14
conf/conf.go
@ -71,7 +71,10 @@ func GetConfigInt64(key string) (int64, error) {
|
||||
|
||||
func GetConfigDataSourceName() string {
|
||||
dataSourceName := GetConfigString("dataSourceName")
|
||||
return ReplaceDataSourceNameByDocker(dataSourceName)
|
||||
}
|
||||
|
||||
func ReplaceDataSourceNameByDocker(dataSourceName string) string {
|
||||
runningInDocker := os.Getenv("RUNNING_IN_DOCKER")
|
||||
if runningInDocker == "true" {
|
||||
// https://stackoverflow.com/questions/48546124/what-is-linux-equivalent-of-host-docker-internal
|
||||
@ -81,7 +84,6 @@ func GetConfigDataSourceName() string {
|
||||
dataSourceName = strings.ReplaceAll(dataSourceName, "localhost", "host.docker.internal")
|
||||
}
|
||||
}
|
||||
|
||||
return dataSourceName
|
||||
}
|
||||
|
||||
@ -108,13 +110,3 @@ func GetConfigBatchSize() int {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func GetConfigRealDataSourceName(driverName string) string {
|
||||
var dataSourceName string
|
||||
if driverName != "mysql" {
|
||||
dataSourceName = GetConfigDataSourceName()
|
||||
} else {
|
||||
dataSourceName = GetConfigDataSourceName() + GetConfigString("dbName")
|
||||
}
|
||||
return dataSourceName
|
||||
}
|
||||
|
@ -93,6 +93,10 @@ func (c *ApiController) Signup() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||
return
|
||||
}
|
||||
|
||||
if !application.EnableSignUp {
|
||||
c.ResponseError(c.T("account:The application does not allow to sign up new account"))
|
||||
@ -105,6 +109,11 @@ func (c *ApiController) Signup() {
|
||||
return
|
||||
}
|
||||
|
||||
if organization == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The organization: %s does not exist"), authForm.Organization))
|
||||
return
|
||||
}
|
||||
|
||||
msg := object.CheckUserSignup(application, organization, &authForm, c.GetAcceptLanguage())
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
@ -122,7 +131,12 @@ func (c *ApiController) Signup() {
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && authForm.Email != "" {
|
||||
checkResult := object.CheckVerificationCode(authForm.Email, authForm.EmailCode, c.GetAcceptLanguage())
|
||||
var checkResult *object.VerifyResult
|
||||
checkResult, err = object.CheckVerificationCode(authForm.Email, authForm.EmailCode, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(c.T(err.Error()))
|
||||
return
|
||||
}
|
||||
if checkResult.Code != object.VerificationSuccess {
|
||||
c.ResponseError(checkResult.Msg)
|
||||
return
|
||||
@ -132,7 +146,13 @@ func (c *ApiController) Signup() {
|
||||
var checkPhone string
|
||||
if application.IsSignupItemVisible("Phone") && application.GetSignupItemRule("Phone") != "No verification" && authForm.Phone != "" {
|
||||
checkPhone, _ = util.GetE164Number(authForm.Phone, authForm.CountryCode)
|
||||
checkResult := object.CheckVerificationCode(checkPhone, authForm.PhoneCode, c.GetAcceptLanguage())
|
||||
|
||||
var checkResult *object.VerifyResult
|
||||
checkResult, err = object.CheckVerificationCode(checkPhone, authForm.PhoneCode, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(c.T(err.Error()))
|
||||
return
|
||||
}
|
||||
if checkResult.Code != object.VerificationSuccess {
|
||||
c.ResponseError(checkResult.Msg)
|
||||
return
|
||||
@ -227,7 +247,7 @@ func (c *ApiController) Signup() {
|
||||
|
||||
if invitation != nil {
|
||||
invitation.UsedCount += 1
|
||||
_, err := object.UpdateInvitation(invitation.GetId(), invitation)
|
||||
_, err := object.UpdateInvitation(invitation.GetId(), invitation, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -287,6 +307,7 @@ func (c *ApiController) Logout() {
|
||||
}
|
||||
|
||||
c.ClearUserSession()
|
||||
c.ClearTokenSession()
|
||||
owner, username := util.GetOwnerAndNameFromId(user)
|
||||
_, err := object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID())
|
||||
if err != nil {
|
||||
@ -333,6 +354,7 @@ func (c *ApiController) Logout() {
|
||||
}
|
||||
|
||||
c.ClearUserSession()
|
||||
c.ClearTokenSession()
|
||||
// TODO https://github.com/casdoor/casdoor/pull/1494#discussion_r1095675265
|
||||
owner, username := util.GetOwnerAndNameFromId(user)
|
||||
|
||||
@ -413,6 +435,17 @@ func (c *ApiController) GetAccount() {
|
||||
return
|
||||
}
|
||||
|
||||
accessToken := c.GetSessionToken()
|
||||
if accessToken == "" {
|
||||
accessToken, err = object.GetAccessTokenByUser(user, c.Ctx.Request.Host)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.SetSessionToken(accessToken)
|
||||
}
|
||||
u.AccessToken = accessToken
|
||||
|
||||
resp := Response{
|
||||
Status: "ok",
|
||||
Sub: user.Id,
|
||||
@ -439,7 +472,12 @@ func (c *ApiController) GetUserinfo() {
|
||||
|
||||
scope, aud := c.GetSessionOidc()
|
||||
host := c.Ctx.Request.Host
|
||||
userInfo := object.GetUserInfo(user, scope, aud, host)
|
||||
|
||||
userInfo, err := object.GetUserInfo(user, scope, aud, host)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = userInfo
|
||||
c.ServeJSON()
|
||||
|
@ -139,6 +139,10 @@ func (c *ApiController) GetUserApplication() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The organization: %s should have one application at least"), user.Owner))
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedApplication(application, userId))
|
||||
}
|
||||
@ -173,7 +177,7 @@ func (c *ApiController) GetOrganizationApplications() {
|
||||
return
|
||||
}
|
||||
|
||||
applications, err = object.GetAllowedApplications(applications, userId)
|
||||
applications, err = object.GetAllowedApplications(applications, userId, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -190,13 +194,19 @@ func (c *ApiController) GetOrganizationApplications() {
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
application, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
applications, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
applications := object.GetMaskedApplications(application, userId)
|
||||
applications, err = object.GetAllowedApplications(applications, userId, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
applications = object.GetMaskedApplications(applications, userId)
|
||||
c.ResponseOk(applications, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,11 @@ import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/casdoor/casdoor/captcha"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
@ -37,11 +36,6 @@ import (
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
wechatScanType string
|
||||
lock sync.RWMutex
|
||||
)
|
||||
|
||||
func codeToResponse(code *object.Code) *Response {
|
||||
if code.Code == "" {
|
||||
return &Response{Status: "error", Msg: code.Message, Data: code.Code}
|
||||
@ -333,7 +327,38 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
var user *object.User
|
||||
if authForm.Password == "" {
|
||||
if authForm.SigninMethod == "Face ID" {
|
||||
if user, err = object.GetUserByFields(authForm.Organization, authForm.Username); err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
} else if user == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(authForm.Organization, authForm.Username)))
|
||||
return
|
||||
}
|
||||
|
||||
var application *object.Application
|
||||
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||
return
|
||||
}
|
||||
|
||||
if !application.IsFaceIdEnabled() {
|
||||
c.ResponseError(c.T("auth:The login method: login with face is not enabled for the application"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := object.CheckFaceId(user, authForm.FaceId, c.GetAcceptLanguage()); err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
} else if authForm.Password == "" {
|
||||
if user, err = object.GetUserByFields(authForm.Organization, authForm.Username); err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
@ -896,6 +921,7 @@ func (c *ApiController) GetSamlLogin() {
|
||||
authURL, method, err := object.GenerateSamlRequest(providerId, relayState, c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.ResponseOk(authURL, method)
|
||||
}
|
||||
@ -906,62 +932,126 @@ func (c *ApiController) HandleSamlLogin() {
|
||||
decode, err := base64.StdEncoding.DecodeString(relayState)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
slice := strings.Split(string(decode), "&")
|
||||
relayState = url.QueryEscape(relayState)
|
||||
samlResponse = url.QueryEscape(samlResponse)
|
||||
targetUrl := fmt.Sprintf("%s?relayState=%s&samlResponse=%s",
|
||||
slice[4], relayState, samlResponse)
|
||||
c.Redirect(targetUrl, 303)
|
||||
c.Redirect(targetUrl, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// HandleOfficialAccountEvent ...
|
||||
// @Tag System API
|
||||
// @Title HandleOfficialAccountEvent
|
||||
// @router /webhook [POST]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
func (c *ApiController) HandleOfficialAccountEvent() {
|
||||
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
|
||||
if c.Ctx.Request.Method == "GET" {
|
||||
s := c.Ctx.Request.FormValue("echostr")
|
||||
echostr, _ := strconv.Atoi(s)
|
||||
c.SetData(echostr)
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
respBytes, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
signature := c.Input().Get("signature")
|
||||
timestamp := c.Input().Get("timestamp")
|
||||
nonce := c.Input().Get("nonce")
|
||||
var data struct {
|
||||
MsgType string `xml:"MsgType"`
|
||||
Event string `xml:"Event"`
|
||||
EventKey string `xml:"EventKey"`
|
||||
MsgType string `xml:"MsgType"`
|
||||
Event string `xml:"Event"`
|
||||
EventKey string `xml:"EventKey"`
|
||||
FromUserName string `xml:"FromUserName"`
|
||||
Ticket string `xml:"Ticket"`
|
||||
}
|
||||
err = xml.Unmarshal(respBytes, &data)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
if data.EventKey != "" {
|
||||
wechatScanType = data.Event
|
||||
if strings.ToUpper(data.Event) != "SCAN" && strings.ToUpper(data.Event) != "SUBSCRIBE" {
|
||||
c.Ctx.WriteString("")
|
||||
return
|
||||
}
|
||||
if data.Ticket == "" {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
providerId := data.EventKey
|
||||
provider, err := object.GetProvider(providerId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if data.Ticket == "" {
|
||||
c.ResponseError("empty ticket")
|
||||
return
|
||||
}
|
||||
if !idp.VerifyWechatSignature(provider.Content, nonce, timestamp, signature) {
|
||||
c.ResponseError("invalid signature")
|
||||
return
|
||||
}
|
||||
|
||||
idp.Lock.Lock()
|
||||
if idp.WechatCacheMap == nil {
|
||||
idp.WechatCacheMap = make(map[string]idp.WechatCacheMapValue)
|
||||
}
|
||||
idp.WechatCacheMap[data.Ticket] = idp.WechatCacheMapValue{
|
||||
IsScanned: true,
|
||||
WechatUnionId: data.FromUserName,
|
||||
}
|
||||
idp.Lock.Unlock()
|
||||
|
||||
c.Ctx.WriteString("")
|
||||
}
|
||||
|
||||
// GetWebhookEventType ...
|
||||
// @Tag System API
|
||||
// @Title GetWebhookEventType
|
||||
// @router /get-webhook-event [GET]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
// @Param ticket query string true "The eventId of QRCode"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
func (c *ApiController) GetWebhookEventType() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
resp := &Response{
|
||||
Status: "ok",
|
||||
Msg: "",
|
||||
Data: wechatScanType,
|
||||
ticket := c.Input().Get("ticket")
|
||||
|
||||
idp.Lock.RLock()
|
||||
_, ok := idp.WechatCacheMap[ticket]
|
||||
idp.Lock.RUnlock()
|
||||
if !ok {
|
||||
c.ResponseError("ticket not found")
|
||||
return
|
||||
}
|
||||
c.Data["json"] = resp
|
||||
wechatScanType = ""
|
||||
c.ServeJSON()
|
||||
|
||||
c.ResponseOk("SCAN", ticket)
|
||||
}
|
||||
|
||||
// GetQRCode
|
||||
// @Tag System API
|
||||
// @Title GetWechatQRCode
|
||||
// @router /get-qrcode [GET]
|
||||
// @Param id query string true "The id ( owner/name ) of provider"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
func (c *ApiController) GetQRCode() {
|
||||
providerId := c.Input().Get("id")
|
||||
provider, err := object.GetProvider(providerId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
code, ticket, err := idp.GetWechatOfficialAccountQRCode(provider.ClientId2, provider.ClientSecret2, providerId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(code, ticket)
|
||||
}
|
||||
|
||||
// GetCaptchaStatus
|
||||
|
@ -122,6 +122,15 @@ func (c *ApiController) GetSessionUsername() string {
|
||||
return user.(string)
|
||||
}
|
||||
|
||||
func (c *ApiController) GetSessionToken() string {
|
||||
accessToken := c.GetSession("accessToken")
|
||||
if accessToken == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return accessToken.(string)
|
||||
}
|
||||
|
||||
func (c *ApiController) GetSessionApplication() *object.Application {
|
||||
clientId := c.GetSession("aud")
|
||||
if clientId == nil {
|
||||
@ -141,6 +150,10 @@ func (c *ApiController) ClearUserSession() {
|
||||
c.SetSessionData(nil)
|
||||
}
|
||||
|
||||
func (c *ApiController) ClearTokenSession() {
|
||||
c.SetSessionToken("")
|
||||
}
|
||||
|
||||
func (c *ApiController) GetSessionOidc() (string, string) {
|
||||
sessionData := c.GetSessionData()
|
||||
if sessionData != nil &&
|
||||
@ -167,6 +180,10 @@ func (c *ApiController) SetSessionUsername(user string) {
|
||||
c.SetSession("username", user)
|
||||
}
|
||||
|
||||
func (c *ApiController) SetSessionToken(accessToken string) {
|
||||
c.SetSession("accessToken", accessToken)
|
||||
}
|
||||
|
||||
// GetSessionData ...
|
||||
func (c *ApiController) GetSessionData() *SessionData {
|
||||
session := c.GetSession("SessionData")
|
||||
|
@ -85,7 +85,7 @@ func (c *ApiController) Enforce() {
|
||||
return
|
||||
}
|
||||
if permission == nil {
|
||||
c.ResponseError(fmt.Sprintf("permission: %s doesn't exist", permissionId))
|
||||
c.ResponseError(fmt.Sprintf(c.T("permission:The permission: \"%s\" doesn't exist"), permissionId))
|
||||
return
|
||||
}
|
||||
|
||||
@ -121,6 +121,10 @@ func (c *ApiController) Enforce() {
|
||||
}
|
||||
} else if owner != "" {
|
||||
permissions, err = object.GetPermissions(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.ResponseError(c.T("general:Missing parameter"))
|
||||
return
|
||||
@ -205,7 +209,7 @@ func (c *ApiController) BatchEnforce() {
|
||||
return
|
||||
}
|
||||
if permission == nil {
|
||||
c.ResponseError(fmt.Sprintf("permission: %s doesn't exist", permissionId))
|
||||
c.ResponseError(fmt.Sprintf(c.T("permission:The permission: \"%s\" doesn't exist"), permissionId))
|
||||
return
|
||||
}
|
||||
|
||||
@ -235,6 +239,10 @@ func (c *ApiController) BatchEnforce() {
|
||||
}
|
||||
} else if owner != "" {
|
||||
permissions, err = object.GetPermissions(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.ResponseError(c.T("general:Missing parameter"))
|
||||
return
|
||||
|
55
controllers/face.go
Normal file
55
controllers/face.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
// Casdoor will expose its providers as services to SDK
|
||||
// We are going to implement those services as APIs here
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// FaceIDSigninBegin
|
||||
// @Title FaceIDSigninBegin
|
||||
// @Tag Login API
|
||||
// @Description FaceId Login Flow 1st stage
|
||||
// @Param owner query string true "owner"
|
||||
// @Param name query string true "name"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /faceid-signin-begin [get]
|
||||
func (c *ApiController) FaceIDSigninBegin() {
|
||||
userOwner := c.Input().Get("owner")
|
||||
userName := c.Input().Get("name")
|
||||
|
||||
user, err := object.GetUserByFields(userOwner, userName)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(userOwner, userName)))
|
||||
return
|
||||
}
|
||||
|
||||
if len(user.FaceIds) == 0 {
|
||||
c.ResponseError(c.T("check:Face data does not exist, cannot log in"))
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk()
|
||||
}
|
@ -84,6 +84,32 @@ func (c *ApiController) GetInvitation() {
|
||||
c.ResponseOk(invitation)
|
||||
}
|
||||
|
||||
// GetInvitationCodeInfo
|
||||
// @Title GetInvitationCodeInfo
|
||||
// @Tag Invitation API
|
||||
// @Description get invitation code information
|
||||
// @Param code query string true "Invitation code"
|
||||
// @Success 200 {object} object.Invitation The Response object
|
||||
// @router /get-invitation-info [get]
|
||||
func (c *ApiController) GetInvitationCodeInfo() {
|
||||
code := c.Input().Get("code")
|
||||
applicationId := c.Input().Get("applicationId")
|
||||
|
||||
application, err := object.GetApplication(applicationId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
invitation, msg := object.GetInvitationByCode(code, application.Organization, c.GetAcceptLanguage())
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedInvitation(invitation))
|
||||
}
|
||||
|
||||
// UpdateInvitation
|
||||
// @Title UpdateInvitation
|
||||
// @Tag Invitation API
|
||||
@ -102,7 +128,7 @@ func (c *ApiController) UpdateInvitation() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateInvitation(id, &invitation))
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateInvitation(id, &invitation, c.GetAcceptLanguage()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@ -121,7 +147,7 @@ func (c *ApiController) AddInvitation() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddInvitation(&invitation))
|
||||
c.Data["json"] = wrapActionResponse(object.AddInvitation(&invitation, c.GetAcceptLanguage()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,14 @@ import (
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
MfaRecoveryCodesSession = "mfa_recovery_codes"
|
||||
MfaCountryCodeSession = "mfa_country_code"
|
||||
MfaDestSession = "mfa_dest"
|
||||
MfaTotpSecretSession = "mfa_totp_secret"
|
||||
)
|
||||
|
||||
// MfaSetupInitiate
|
||||
@ -57,12 +65,20 @@ func (c *ApiController) MfaSetupInitiate() {
|
||||
return
|
||||
}
|
||||
|
||||
mfaProps, err := MfaUtil.Initiate(c.Ctx, user.GetId())
|
||||
mfaProps, err := MfaUtil.Initiate(user.GetId())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
recoveryCode := uuid.NewString()
|
||||
c.SetSession(MfaRecoveryCodesSession, recoveryCode)
|
||||
if mfaType == object.TotpType {
|
||||
c.SetSession(MfaTotpSecretSession, mfaProps.Secret)
|
||||
}
|
||||
|
||||
mfaProps.RecoveryCodes = []string{recoveryCode}
|
||||
|
||||
resp := mfaProps
|
||||
c.ResponseOk(resp)
|
||||
}
|
||||
@ -83,13 +99,46 @@ func (c *ApiController) MfaSetupVerify() {
|
||||
c.ResponseError("missing auth type or passcode")
|
||||
return
|
||||
}
|
||||
mfaUtil := object.GetMfaUtil(mfaType, nil)
|
||||
|
||||
config := &object.MfaProps{
|
||||
MfaType: mfaType,
|
||||
}
|
||||
if mfaType == object.TotpType {
|
||||
secret := c.GetSession(MfaTotpSecretSession)
|
||||
if secret == nil {
|
||||
c.ResponseError("totp secret is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = secret.(string)
|
||||
} else if mfaType == object.SmsType {
|
||||
dest := c.GetSession(MfaDestSession)
|
||||
if dest == nil {
|
||||
c.ResponseError("destination is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = dest.(string)
|
||||
countryCode := c.GetSession(MfaCountryCodeSession)
|
||||
if countryCode == nil {
|
||||
c.ResponseError("country code is missing")
|
||||
return
|
||||
}
|
||||
config.CountryCode = countryCode.(string)
|
||||
} else if mfaType == object.EmailType {
|
||||
dest := c.GetSession(MfaDestSession)
|
||||
if dest == nil {
|
||||
c.ResponseError("destination is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = dest.(string)
|
||||
}
|
||||
|
||||
mfaUtil := object.GetMfaUtil(mfaType, config)
|
||||
if mfaUtil == nil {
|
||||
c.ResponseError("Invalid multi-factor authentication type")
|
||||
return
|
||||
}
|
||||
|
||||
err := mfaUtil.SetupVerify(c.Ctx, passcode)
|
||||
err := mfaUtil.SetupVerify(passcode)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
} else {
|
||||
@ -122,18 +171,69 @@ func (c *ApiController) MfaSetupEnable() {
|
||||
return
|
||||
}
|
||||
|
||||
mfaUtil := object.GetMfaUtil(mfaType, nil)
|
||||
config := &object.MfaProps{
|
||||
MfaType: mfaType,
|
||||
}
|
||||
|
||||
if mfaType == object.TotpType {
|
||||
secret := c.GetSession(MfaTotpSecretSession)
|
||||
if secret == nil {
|
||||
c.ResponseError("totp secret is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = secret.(string)
|
||||
} else if mfaType == object.EmailType {
|
||||
if user.Email == "" {
|
||||
dest := c.GetSession(MfaDestSession)
|
||||
if dest == nil {
|
||||
c.ResponseError("destination is missing")
|
||||
return
|
||||
}
|
||||
user.Email = dest.(string)
|
||||
}
|
||||
} else if mfaType == object.SmsType {
|
||||
if user.Phone == "" {
|
||||
dest := c.GetSession(MfaDestSession)
|
||||
if dest == nil {
|
||||
c.ResponseError("destination is missing")
|
||||
return
|
||||
}
|
||||
user.Phone = dest.(string)
|
||||
countryCode := c.GetSession(MfaCountryCodeSession)
|
||||
if countryCode == nil {
|
||||
c.ResponseError("country code is missing")
|
||||
return
|
||||
}
|
||||
user.CountryCode = countryCode.(string)
|
||||
}
|
||||
}
|
||||
recoveryCodes := c.GetSession(MfaRecoveryCodesSession)
|
||||
if recoveryCodes == nil {
|
||||
c.ResponseError("recovery codes is missing")
|
||||
return
|
||||
}
|
||||
config.RecoveryCodes = []string{recoveryCodes.(string)}
|
||||
|
||||
mfaUtil := object.GetMfaUtil(mfaType, config)
|
||||
if mfaUtil == nil {
|
||||
c.ResponseError("Invalid multi-factor authentication type")
|
||||
return
|
||||
}
|
||||
|
||||
err = mfaUtil.Enable(c.Ctx, user)
|
||||
err = mfaUtil.Enable(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.DelSession(MfaRecoveryCodesSession)
|
||||
if mfaType == object.TotpType {
|
||||
c.DelSession(MfaTotpSecretSession)
|
||||
} else {
|
||||
c.DelSession(MfaCountryCodeSession)
|
||||
c.DelSession(MfaDestSession)
|
||||
}
|
||||
|
||||
c.ResponseOk(http.StatusText(http.StatusOK))
|
||||
}
|
||||
|
||||
|
128
controllers/record.go
Normal file
128
controllers/record.go
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2021 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
|
||||
|
||||
"github.com/beego/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetRecords
|
||||
// @Title GetRecords
|
||||
// @Tag Record API
|
||||
// @Description get all records
|
||||
// @Param pageSize query string true "The size of each page"
|
||||
// @Param p query string true "The number of the page"
|
||||
// @Success 200 {object} object.Record The Response object
|
||||
// @router /get-records [get]
|
||||
func (c *ApiController) GetRecords() {
|
||||
organization, ok := c.RequireAdmin()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
organizationName := c.Input().Get("organizationName")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
records, err := object.GetRecords()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(records)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
if c.IsGlobalAdmin() && organizationName != "" {
|
||||
organization = organizationName
|
||||
}
|
||||
filterRecord := &casvisorsdk.Record{Organization: organization}
|
||||
count, err := object.GetRecordCount(field, value, filterRecord)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
records, err := object.GetPaginationRecords(paginator.Offset(), limit, field, value, sortField, sortOrder, filterRecord)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(records, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// GetRecordsByFilter
|
||||
// @Tag Record API
|
||||
// @Title GetRecordsByFilter
|
||||
// @Description get records by filter
|
||||
// @Param filter body string true "filter Record message"
|
||||
// @Success 200 {object} object.Record The Response object
|
||||
// @router /get-records-filter [post]
|
||||
func (c *ApiController) GetRecordsByFilter() {
|
||||
_, ok := c.RequireAdmin()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
body := string(c.Ctx.Input.RequestBody)
|
||||
|
||||
record := &casvisorsdk.Record{}
|
||||
err := util.JsonToStruct(body, record)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
records, err := object.GetRecordsByField(record)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(records)
|
||||
}
|
||||
|
||||
// AddRecord
|
||||
// @Title AddRecord
|
||||
// @Tag Record API
|
||||
// @Description add a record
|
||||
// @Param body body object.Record true "The details of the record"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-record [post]
|
||||
func (c *ApiController) AddRecord() {
|
||||
var record casvisorsdk.Record
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &record)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddRecord(&record))
|
||||
c.ServeJSON()
|
||||
}
|
@ -52,6 +52,15 @@ func (c *ApiController) GetResources() {
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
isOrgAdmin, ok := c.IsOrgAdmin()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if isOrgAdmin {
|
||||
user = ""
|
||||
}
|
||||
|
||||
if sortField == "Direct" {
|
||||
provider, err := c.GetProviderFromContext("Storage")
|
||||
if err != nil {
|
||||
|
@ -16,6 +16,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
@ -34,7 +35,13 @@ func (c *ApiController) GetSamlMeta() {
|
||||
return
|
||||
}
|
||||
|
||||
metadata, err := object.GetSamlMeta(application, host)
|
||||
enablePostBinding, err := c.GetBool("enablePostBinding", false)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
metadata, err := object.GetSamlMeta(application, host, enablePostBinding)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -43,3 +50,17 @@ func (c *ApiController) GetSamlMeta() {
|
||||
c.Data["xml"] = metadata
|
||||
c.ServeXML()
|
||||
}
|
||||
|
||||
func (c *ApiController) HandleSamlRedirect() {
|
||||
host := c.Ctx.Request.Host
|
||||
|
||||
owner := c.Ctx.Input.Param(":owner")
|
||||
application := c.Ctx.Input.Param(":application")
|
||||
|
||||
relayState := c.Input().Get("RelayState")
|
||||
samlRequest := c.Input().Get("SAMLRequest")
|
||||
|
||||
targetURL := object.GetSamlRedirectAddress(owner, application, relayState, samlRequest, host)
|
||||
|
||||
c.Redirect(targetURL, http.StatusSeeOther)
|
||||
}
|
||||
|
@ -60,7 +60,6 @@ func (c *ApiController) SendEmail() {
|
||||
}
|
||||
|
||||
var emailForm EmailForm
|
||||
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &emailForm)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@ -87,7 +86,7 @@ func (c *ApiController) SendEmail() {
|
||||
|
||||
// when receiver is the reserved keyword: "TestSmtpServer", it means to test the SMTP server instead of sending a real Email
|
||||
if len(emailForm.Receivers) == 1 && emailForm.Receivers[0] == "TestSmtpServer" {
|
||||
err := object.DailSmtpServer(provider)
|
||||
err = object.DailSmtpServer(provider)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -112,20 +111,23 @@ func (c *ApiController) SendEmail() {
|
||||
return
|
||||
}
|
||||
|
||||
code := "123456"
|
||||
content := emailForm.Content
|
||||
if content == "" {
|
||||
code := "123456"
|
||||
|
||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||
content := strings.Replace(provider.Content, "%s", code, 1)
|
||||
if !strings.HasPrefix(userId, "app/") {
|
||||
var user *object.User
|
||||
user, err = object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||
content = strings.Replace(provider.Content, "%s", code, 1)
|
||||
if !strings.HasPrefix(userId, "app/") {
|
||||
var user *object.User
|
||||
user, err = object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
|
||||
if user != nil {
|
||||
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,3 +168,20 @@ func (c *ApiController) RunSyncer() {
|
||||
|
||||
c.ResponseOk()
|
||||
}
|
||||
|
||||
func (c *ApiController) TestSyncerDb() {
|
||||
var syncer object.Syncer
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &syncer)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = object.TestSyncerDb(syncer)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk()
|
||||
}
|
||||
|
@ -47,6 +47,11 @@ func (c *ApiController) GetSystemInfo() {
|
||||
// @router /get-version-info [get]
|
||||
func (c *ApiController) GetVersionInfo() {
|
||||
versionInfo, err := util.GetVersionInfo()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if versionInfo.Version != "" {
|
||||
c.ResponseOk(versionInfo)
|
||||
return
|
||||
|
@ -271,6 +271,14 @@ func (c *ApiController) RefreshToken() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) ResponseTokenError(errorMsg string) {
|
||||
c.Data["json"] = &object.TokenError{
|
||||
Error: errorMsg,
|
||||
}
|
||||
c.SetTokenErrorHttpStatus()
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// IntrospectToken
|
||||
// @Title IntrospectToken
|
||||
// @Tag Login API
|
||||
@ -293,40 +301,34 @@ func (c *ApiController) IntrospectToken() {
|
||||
clientId = c.Input().Get("client_id")
|
||||
clientSecret = c.Input().Get("client_secret")
|
||||
if clientId == "" || clientSecret == "" {
|
||||
c.ResponseError(c.T("token:Empty clientId or clientSecret"))
|
||||
c.Data["json"] = &object.TokenError{
|
||||
Error: object.InvalidRequest,
|
||||
}
|
||||
c.SetTokenErrorHttpStatus()
|
||||
c.ServeJSON()
|
||||
c.ResponseTokenError(object.InvalidRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
application, err := object.GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
c.ResponseTokenError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil || application.ClientSecret != clientSecret {
|
||||
c.ResponseError(c.T("token:Invalid application or wrong clientSecret"))
|
||||
c.Data["json"] = &object.TokenError{
|
||||
Error: object.InvalidClient,
|
||||
}
|
||||
c.SetTokenErrorHttpStatus()
|
||||
return
|
||||
}
|
||||
token, err := object.GetTokenByTokenAndApplication(tokenValue, application.Name)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
c.ResponseTokenError(c.T("token:Invalid application or wrong clientSecret"))
|
||||
return
|
||||
}
|
||||
|
||||
tokenTypeHint := c.Input().Get("token_type_hint")
|
||||
token, err := object.GetTokenByTokenValue(tokenValue, tokenTypeHint)
|
||||
if err != nil {
|
||||
c.ResponseTokenError(err.Error())
|
||||
return
|
||||
}
|
||||
if token == nil {
|
||||
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
jwtToken, err := object.ParseJwtTokenByApplication(tokenValue, application)
|
||||
if err != nil || jwtToken.Valid() != nil {
|
||||
// and token revoked case. but we not implement
|
||||
|
167
controllers/transaction.go
Normal file
167
controllers/transaction.go
Normal file
@ -0,0 +1,167 @@
|
||||
// Copyright 2024 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/beego/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetTransactions
|
||||
// @Title GetTransactions
|
||||
// @Tag Transaction API
|
||||
// @Description get transactions
|
||||
// @Param owner query string true "The owner of transactions"
|
||||
// @Success 200 {array} object.Transaction The Response object
|
||||
// @router /get-transactions [get]
|
||||
func (c *ApiController) GetTransactions() {
|
||||
owner := c.Input().Get("owner")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
transactions, err := object.GetTransactions(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(transactions)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetTransactionCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
transactions, err := object.GetPaginationTransactions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(transactions, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserTransactions
|
||||
// @Title GetUserTransaction
|
||||
// @Tag Transaction API
|
||||
// @Description get transactions for a user
|
||||
// @Param owner query string true "The owner of transactions"
|
||||
// @Param organization query string true "The organization of the user"
|
||||
// @Param user query string true "The username of the user"
|
||||
// @Success 200 {array} object.Transaction The Response object
|
||||
// @router /get-user-transactions [get]
|
||||
func (c *ApiController) GetUserTransactions() {
|
||||
owner := c.Input().Get("owner")
|
||||
user := c.Input().Get("user")
|
||||
|
||||
transactions, err := object.GetUserTransactions(owner, user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(transactions)
|
||||
}
|
||||
|
||||
// GetTransaction
|
||||
// @Title GetTransaction
|
||||
// @Tag Transaction API
|
||||
// @Description get transaction
|
||||
// @Param id query string true "The id ( owner/name ) of the transaction"
|
||||
// @Success 200 {object} object.Transaction The Response object
|
||||
// @router /get-transaction [get]
|
||||
func (c *ApiController) GetTransaction() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
transaction, err := object.GetTransaction(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(transaction)
|
||||
}
|
||||
|
||||
// UpdateTransaction
|
||||
// @Title UpdateTransaction
|
||||
// @Tag Transaction API
|
||||
// @Description update transaction
|
||||
// @Param id query string true "The id ( owner/name ) of the transaction"
|
||||
// @Param body body object.Transaction true "The details of the transaction"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-transaction [post]
|
||||
func (c *ApiController) UpdateTransaction() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var transaction object.Transaction
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &transaction)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateTransaction(id, &transaction))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddTransaction
|
||||
// @Title AddTransaction
|
||||
// @Tag Transaction API
|
||||
// @Description add transaction
|
||||
// @Param body body object.Transaction true "The details of the transaction"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-transaction [post]
|
||||
func (c *ApiController) AddTransaction() {
|
||||
var transaction object.Transaction
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &transaction)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddTransaction(&transaction))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteTransaction
|
||||
// @Title DeleteTransaction
|
||||
// @Tag Transaction API
|
||||
// @Description delete transaction
|
||||
// @Param body body object.Transaction true "The details of the transaction"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-transaction [post]
|
||||
func (c *ApiController) DeleteTransaction() {
|
||||
var transaction object.Transaction
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &transaction)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteTransaction(&transaction))
|
||||
c.ServeJSON()
|
||||
}
|
@ -156,6 +156,10 @@ func (c *ApiController) GetUser() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if userFromUserId == nil {
|
||||
c.ResponseOk(nil)
|
||||
return
|
||||
}
|
||||
|
||||
id = util.GetId(userFromUserId.Owner, userFromUserId.Name)
|
||||
}
|
||||
@ -200,7 +204,7 @@ func (c *ApiController) GetUser() {
|
||||
return
|
||||
}
|
||||
if organization == nil {
|
||||
c.ResponseError(fmt.Sprintf("the organization: %s is not found", owner))
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The organization: %s does not exist"), owner))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -108,12 +108,12 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
|
||||
c.ResponseError(err.Error())
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
c.ClearUserSession()
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return user, true
|
||||
}
|
||||
|
||||
@ -127,9 +127,39 @@ func (c *ApiController) RequireAdmin() (string, bool) {
|
||||
if user.Owner == "built-in" {
|
||||
return "", true
|
||||
}
|
||||
|
||||
if !user.IsAdmin {
|
||||
c.ResponseError(c.T("general:this operation requires administrator to perform"))
|
||||
return "", false
|
||||
}
|
||||
|
||||
return user.Owner, true
|
||||
}
|
||||
|
||||
func (c *ApiController) IsOrgAdmin() (bool, bool) {
|
||||
userId, ok := c.RequireSignedIn()
|
||||
if !ok {
|
||||
return false, true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(userId, "app/") {
|
||||
return true, true
|
||||
}
|
||||
|
||||
user, err := object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return false, false
|
||||
}
|
||||
if user == nil {
|
||||
c.ClearUserSession()
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
|
||||
return false, false
|
||||
}
|
||||
|
||||
return user.IsAdmin, true
|
||||
}
|
||||
|
||||
// IsMaskedEnabled ...
|
||||
func (c *ApiController) IsMaskedEnabled() (bool, bool) {
|
||||
isMaskEnabled := true
|
||||
|
@ -161,16 +161,16 @@ func (c *ApiController) SendVerificationCode() {
|
||||
vform.Dest = mfaProps.Secret
|
||||
}
|
||||
} else if vform.Method == MfaSetupVerification {
|
||||
c.SetSession(object.MfaDestSession, vform.Dest)
|
||||
c.SetSession(MfaDestSession, vform.Dest)
|
||||
}
|
||||
|
||||
provider, err := application.GetEmailProvider()
|
||||
provider, err = application.GetEmailProvider(vform.Method)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if provider == nil {
|
||||
c.ResponseError(fmt.Sprintf("please add an Email provider to the \"Providers\" list for the application: %s", application.Name))
|
||||
c.ResponseError(fmt.Sprintf(c.T("verification:please add an Email provider to the \"Providers\" list for the application: %s"), application.Name))
|
||||
return
|
||||
}
|
||||
|
||||
@ -198,8 +198,8 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
|
||||
if vform.Method == MfaSetupVerification {
|
||||
c.SetSession(object.MfaCountryCodeSession, vform.CountryCode)
|
||||
c.SetSession(object.MfaDestSession, vform.Dest)
|
||||
c.SetSession(MfaCountryCodeSession, vform.CountryCode)
|
||||
c.SetSession(MfaDestSession, vform.Dest)
|
||||
}
|
||||
} else if vform.Method == MfaAuthVerification {
|
||||
mfaProps := user.GetPreferredMfaProps(false)
|
||||
@ -210,13 +210,13 @@ func (c *ApiController) SendVerificationCode() {
|
||||
vform.CountryCode = mfaProps.CountryCode
|
||||
}
|
||||
|
||||
provider, err := application.GetSmsProvider()
|
||||
provider, err = application.GetSmsProvider(vform.Method)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if provider == nil {
|
||||
c.ResponseError(fmt.Sprintf("please add a SMS provider to the \"Providers\" list for the application: %s", application.Name))
|
||||
c.ResponseError(fmt.Sprintf(c.T("verification:please add a SMS provider to the \"Providers\" list for the application: %s"), application.Name))
|
||||
return
|
||||
}
|
||||
|
||||
@ -343,7 +343,12 @@ func (c *ApiController) ResetEmailOrPhone() {
|
||||
}
|
||||
}
|
||||
|
||||
if result := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); result.Code != object.VerificationSuccess {
|
||||
result, err := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(c.T(err.Error()))
|
||||
return
|
||||
}
|
||||
if result.Code != object.VerificationSuccess {
|
||||
c.ResponseError(result.Msg)
|
||||
return
|
||||
}
|
||||
@ -425,16 +430,22 @@ func (c *ApiController) VerifyCode() {
|
||||
}
|
||||
}
|
||||
|
||||
if result := object.CheckVerificationCode(checkDest, authForm.Code, c.GetAcceptLanguage()); result.Code != object.VerificationSuccess {
|
||||
result, err := object.CheckVerificationCode(checkDest, authForm.Code, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(c.T(err.Error()))
|
||||
return
|
||||
}
|
||||
if result.Code != object.VerificationSuccess {
|
||||
c.ResponseError(result.Msg)
|
||||
return
|
||||
}
|
||||
|
||||
err = object.DisableVerificationCode(checkDest)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.SetSession("verifiedCode", authForm.Code)
|
||||
|
||||
c.SetSession("verifiedCode", authForm.Code)
|
||||
c.ResponseOk()
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ func GetCredManager(passwordType string) CredManager {
|
||||
return NewPlainCredManager()
|
||||
} else if passwordType == "salt" {
|
||||
return NewSha256SaltCredManager()
|
||||
} else if passwordType == "sha512-salt" {
|
||||
return NewSha512SaltCredManager()
|
||||
} else if passwordType == "md5-salt" {
|
||||
return NewMd5UserSaltCredManager()
|
||||
} else if passwordType == "bcrypt" {
|
||||
|
50
cred/sha512-salt.go
Normal file
50
cred/sha512-salt.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2024 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 cred
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
type Sha512SaltCredManager struct{}
|
||||
|
||||
func getSha512(data []byte) []byte {
|
||||
hash := sha512.Sum512(data)
|
||||
return hash[:]
|
||||
}
|
||||
|
||||
func getSha512HexDigest(s string) string {
|
||||
b := getSha512([]byte(s))
|
||||
res := hex.EncodeToString(b)
|
||||
return res
|
||||
}
|
||||
|
||||
func NewSha512SaltCredManager() *Sha512SaltCredManager {
|
||||
cm := &Sha512SaltCredManager{}
|
||||
return cm
|
||||
}
|
||||
|
||||
func (cm *Sha512SaltCredManager) GetHashedPassword(password string, userSalt string, organizationSalt string) string {
|
||||
res := getSha512HexDigest(password)
|
||||
if organizationSalt != "" {
|
||||
res = getSha512HexDigest(res + organizationSalt)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (cm *Sha512SaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
|
||||
return hashedPwd == cm.GetHashedPassword(plainPwd, userSalt, organizationSalt)
|
||||
}
|
@ -27,7 +27,10 @@ import (
|
||||
)
|
||||
|
||||
func deployStaticFiles(provider *object.Provider) {
|
||||
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
|
||||
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if storageProvider == nil {
|
||||
panic(fmt.Sprintf("the provider type: %s is not supported", provider.Type))
|
||||
}
|
||||
|
@ -61,6 +61,8 @@ type AuthForm struct {
|
||||
|
||||
Plan string `json:"plan"`
|
||||
Pricing string `json:"pricing"`
|
||||
|
||||
FaceId []float64 `json:"faceId"`
|
||||
}
|
||||
|
||||
func GetAuthFormFieldValue(form *AuthForm, fieldName string) (bool, string) {
|
||||
|
8
go.mod
8
go.mod
@ -9,12 +9,12 @@ require (
|
||||
github.com/beego/beego v1.12.12
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/casbin/casbin/v2 v2.77.2
|
||||
github.com/casdoor/go-sms-sender v0.19.0
|
||||
github.com/casdoor/go-sms-sender v0.20.0
|
||||
github.com/casdoor/gomail/v2 v2.0.1
|
||||
github.com/casdoor/notify v0.45.0
|
||||
github.com/casdoor/oss v1.5.0
|
||||
github.com/casdoor/oss v1.6.0
|
||||
github.com/casdoor/xorm-adapter/v3 v3.1.0
|
||||
github.com/casvisor/casvisor-go-sdk v1.0.3
|
||||
github.com/casvisor/casvisor-go-sdk v1.3.0
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/denisenkom/go-mssqldb v0.9.0
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
|
||||
@ -59,7 +59,7 @@ require (
|
||||
github.com/xorm-io/core v0.7.4
|
||||
github.com/xorm-io/xorm v1.1.6
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/crypto v0.19.0
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/oauth2 v0.13.0
|
||||
google.golang.org/api v0.150.0
|
||||
|
24
go.sum
24
go.sum
@ -1083,18 +1083,18 @@ github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3
|
||||
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
|
||||
github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs=
|
||||
github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc=
|
||||
github.com/casdoor/go-sms-sender v0.19.0 h1:qVz7RLXx8aGgfzLUGvhNe6pbhxDqP2/qNY+XPepfpyQ=
|
||||
github.com/casdoor/go-sms-sender v0.19.0/go.mod h1:cQs7qqohMJBgIVZebOCB8ko09naG1vzFJEH59VNIscs=
|
||||
github.com/casdoor/go-sms-sender v0.20.0 h1:yLbCakV04DzzehhgBklOrSeCFjMwpfKBeemz9b+Y8OM=
|
||||
github.com/casdoor/go-sms-sender v0.20.0/go.mod h1:cQs7qqohMJBgIVZebOCB8ko09naG1vzFJEH59VNIscs=
|
||||
github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w=
|
||||
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
|
||||
github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk=
|
||||
github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ=
|
||||
github.com/casdoor/oss v1.5.0 h1:mi1htaXR5fynskDry1S3wk+Dd2nRY1z1pVcnGsqMqP4=
|
||||
github.com/casdoor/oss v1.5.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
|
||||
github.com/casdoor/oss v1.6.0 h1:IOWrGLJ+VO82qS796eaRnzFPPA1Sn3cotYTi7O/VIlQ=
|
||||
github.com/casdoor/oss v1.6.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
|
||||
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
|
||||
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
|
||||
github.com/casvisor/casvisor-go-sdk v1.0.3 h1:TKJQWKnhtznEBhzLPEdNsp7nJK2GgdD8JsB0lFPMW7U=
|
||||
github.com/casvisor/casvisor-go-sdk v1.0.3/go.mod h1:frnNtH5GA0wxzAQLyZxxfL0RSsSub9GQPi2Ybe86ocE=
|
||||
github.com/casvisor/casvisor-go-sdk v1.3.0 h1:HVgm2g3lWpNX2wBNidzR743QY4O5kAjLUJ9tS2juO8g=
|
||||
github.com/casvisor/casvisor-go-sdk v1.3.0/go.mod h1:frnNtH5GA0wxzAQLyZxxfL0RSsSub9GQPi2Ybe86ocE=
|
||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
||||
@ -2117,8 +2117,9 @@ golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@ -2447,8 +2448,9 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@ -2465,8 +2467,9 @@ golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -2486,8 +2489,9 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "Die Anmeldeart \"Anmeldung mit Passwort\" ist für die Anwendung nicht aktiviert",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert",
|
||||
"Unauthorized operation": "Nicht autorisierte Operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Zugehörigkeit darf nicht leer sein",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"DisplayName cannot be blank": "Anzeigename kann nicht leer sein",
|
||||
"DisplayName is not valid real name": "DisplayName ist kein gültiger Vorname",
|
||||
"Email already exists": "E-Mail existiert bereits",
|
||||
"Email cannot be empty": "E-Mail darf nicht leer sein",
|
||||
"Email is invalid": "E-Mail ist ungültig",
|
||||
"Empty username.": "Leerer Benutzername.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "Vorname darf nicht leer sein",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "Dem Benutzer ist der Zugang verboten, bitte kontaktieren Sie den Administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.": "Der Benutzername darf nur alphanumerische Zeichen, Unterstriche oder Bindestriche enthalten, keine aufeinanderfolgenden Bindestriche oder Unterstriche haben und darf nicht mit einem Bindestrich oder Unterstrich beginnen oder enden.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Benutzername existiert bereits",
|
||||
"Username cannot be an email address": "Benutzername kann keine E-Mail-Adresse sein",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Fehlender Parameter",
|
||||
"Please login first": "Bitte zuerst einloggen",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "Der Benutzer %s existiert nicht",
|
||||
"don't support captchaProvider: ": "Unterstütze captchaProvider nicht:",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "Das %s ist unveränderlich.",
|
||||
"Unknown modify rule %s.": "Unbekannte Änderungsregel %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Ungültige Anwendungs-ID",
|
||||
"the provider: %s does not exist": "Der Anbieter %s existiert nicht"
|
||||
@ -116,7 +126,6 @@
|
||||
"The provider type: %s is not supported": "Der Anbieter-Typ %s wird nicht unterstützt"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "Leerer clientId oder clientSecret",
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s wird von dieser Anwendung nicht unterstützt",
|
||||
"Invalid application or wrong clientSecret": "Ungültige Anwendung oder falsches clientSecret",
|
||||
"Invalid client_id": "Ungültige client_id",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unbekannter Typ",
|
||||
"Wrong verification code!": "Falscher Bestätigungscode!",
|
||||
"You should verify your code in %d min!": "Du solltest deinen Code in %d Minuten verifizieren!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "Der Benutzer existiert nicht, bitte zuerst anmelden"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "El método de inicio de sesión: inicio de sesión con contraseña no está habilitado para la aplicación",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "El proveedor: %s no está habilitado para la aplicación",
|
||||
"Unauthorized operation": "Operación no autorizada",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Afiliación no puede estar en blanco",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"DisplayName cannot be blank": "El nombre de visualización no puede estar en blanco",
|
||||
"DisplayName is not valid real name": "El nombre de pantalla no es un nombre real válido",
|
||||
"Email already exists": "El correo electrónico ya existe",
|
||||
"Email cannot be empty": "El correo electrónico no puede estar vacío",
|
||||
"Email is invalid": "El correo electrónico no es válido",
|
||||
"Empty username.": "Nombre de usuario vacío.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "El nombre no puede estar en blanco",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "El usuario no está autorizado a iniciar sesión, por favor contacte al administrador",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.": "El nombre de usuario solo puede contener caracteres alfanuméricos, guiones bajos o guiones, no puede tener guiones o subrayados consecutivos, y no puede comenzar ni terminar con un guión o subrayado.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "El nombre de usuario ya existe",
|
||||
"Username cannot be an email address": "Nombre de usuario no puede ser una dirección de correo electrónico",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Parámetro faltante",
|
||||
"Please login first": "Por favor, inicia sesión primero",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "El usuario: %s no existe",
|
||||
"don't support captchaProvider: ": "No apoyo a captchaProvider",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "El %s es inmutable.",
|
||||
"Unknown modify rule %s.": "Regla de modificación desconocida %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Identificación de aplicación no válida",
|
||||
"the provider: %s does not exist": "El proveedor: %s no existe"
|
||||
@ -116,7 +126,6 @@
|
||||
"The provider type: %s is not supported": "El tipo de proveedor: %s no es compatible"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "ClienteId o clienteSecret vacío",
|
||||
"Grant_type: %s is not supported in this application": "El tipo de subvención: %s no es compatible con esta aplicación",
|
||||
"Invalid application or wrong clientSecret": "Solicitud inválida o clientSecret incorrecto",
|
||||
"Invalid client_id": "Identificador de cliente no válido",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Tipo desconocido",
|
||||
"Wrong verification code!": "¡Código de verificación incorrecto!",
|
||||
"You should verify your code in %d min!": "¡Deberías verificar tu código en %d minutos!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "El usuario no existe, por favor regístrese primero"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "La méthode de connexion : connexion avec mot de passe n'est pas activée pour l'application",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "Le fournisseur :%s n'est pas activé pour l'application",
|
||||
"Unauthorized operation": "Opération non autorisée",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation ne peut pas être vide",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"DisplayName cannot be blank": "Le nom d'affichage ne peut pas être vide",
|
||||
"DisplayName is not valid real name": "DisplayName n'est pas un nom réel valide",
|
||||
"Email already exists": "E-mail déjà existant",
|
||||
"Email cannot be empty": "L'e-mail ne peut pas être vide",
|
||||
"Email is invalid": "L'adresse e-mail est invalide",
|
||||
"Empty username.": "Nom d'utilisateur vide.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "Le prénom ne peut pas être laissé vide",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "L'utilisateur est interdit de se connecter, veuillez contacter l'administrateur",
|
||||
"The user: %s doesn't exist in LDAP server": "L'utilisateur %s n'existe pas sur le serveur LDAP",
|
||||
"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.": "Le nom d'utilisateur ne peut contenir que des caractères alphanumériques, des traits soulignés ou des tirets, ne peut pas avoir de tirets ou de traits soulignés consécutifs et ne peut pas commencer ou se terminer par un tiret ou un trait souligné.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Nom d'utilisateur existe déjà",
|
||||
"Username cannot be an email address": "Nom d'utilisateur ne peut pas être une adresse e-mail",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Paramètre manquant",
|
||||
"Please login first": "Veuillez d'abord vous connecter",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "L'utilisateur : %s n'existe pas",
|
||||
"don't support captchaProvider: ": "ne prend pas en charge captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "cette opération n’est pas autorisée en mode démo"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "Le %s est immuable.",
|
||||
"Unknown modify rule %s.": "Règle de modification inconnue %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Identifiant d'application invalide",
|
||||
"the provider: %s does not exist": "Le fournisseur : %s n'existe pas"
|
||||
@ -116,7 +126,6 @@
|
||||
"The provider type: %s is not supported": "Le type de fournisseur : %s n'est pas pris en charge"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "clientId ou clientSecret vide",
|
||||
"Grant_type: %s is not supported in this application": "Type_de_subvention : %s n'est pas pris en charge dans cette application",
|
||||
"Invalid application or wrong clientSecret": "Application invalide ou clientSecret incorrect",
|
||||
"Invalid client_id": "Identifiant de client invalide",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Type inconnu",
|
||||
"Wrong verification code!": "Mauvais code de vérification !",
|
||||
"You should verify your code in %d min!": "Vous devriez vérifier votre code en %d min !",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "L'utilisateur n'existe pas, veuillez vous inscrire d'abord"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "Metode login: login dengan kata sandi tidak diaktifkan untuk aplikasi tersebut",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
|
||||
"Unauthorized operation": "Operasi tidak sah",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Keterkaitan tidak boleh kosong",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"DisplayName cannot be blank": "Nama Pengguna tidak boleh kosong",
|
||||
"DisplayName is not valid real name": "DisplayName bukanlah nama asli yang valid",
|
||||
"Email already exists": "Email sudah ada",
|
||||
"Email cannot be empty": "Email tidak boleh kosong",
|
||||
"Email is invalid": "Email tidak valid",
|
||||
"Empty username.": "Nama pengguna kosong.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "Nama depan tidak boleh kosong",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "Pengguna dilarang masuk, silakan hubungi administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.": "Nama pengguna hanya bisa menggunakan karakter alfanumerik, garis bawah atau tanda hubung, tidak boleh memiliki dua tanda hubung atau garis bawah berurutan, dan tidak boleh diawali atau diakhiri dengan tanda hubung atau garis bawah.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Nama pengguna sudah ada",
|
||||
"Username cannot be an email address": "Username tidak bisa menjadi alamat email",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Parameter hilang",
|
||||
"Please login first": "Silahkan login terlebih dahulu",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "Pengguna: %s tidak ada",
|
||||
"don't support captchaProvider: ": "Jangan mendukung captchaProvider:",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "%s tidak dapat diubah.",
|
||||
"Unknown modify rule %s.": "Aturan modifikasi tidak diketahui %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "ID aplikasi tidak valid",
|
||||
"the provider: %s does not exist": "provider: %s tidak ada"
|
||||
@ -116,7 +126,6 @@
|
||||
"The provider type: %s is not supported": "Jenis penyedia: %s tidak didukung"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "Kosong clientId atau clientSecret",
|
||||
"Grant_type: %s is not supported in this application": "Jenis grant (grant_type) %s tidak didukung dalam aplikasi ini",
|
||||
"Invalid application or wrong clientSecret": "Aplikasi tidak valid atau clientSecret salah",
|
||||
"Invalid client_id": "Invalid client_id = ID klien tidak valid",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Tipe tidak diketahui",
|
||||
"Wrong verification code!": "Kode verifikasi salah!",
|
||||
"You should verify your code in %d min!": "Anda harus memverifikasi kode Anda dalam %d menit!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "Pengguna tidak ada, silakan daftar terlebih dahulu"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません",
|
||||
"Unauthorized operation": "不正操作",
|
||||
"Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "所属は空白にできません",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"DisplayName cannot be blank": "表示名は空白にできません",
|
||||
"DisplayName is not valid real name": "表示名は有効な実名ではありません",
|
||||
"Email already exists": "メールは既に存在します",
|
||||
"Email cannot be empty": "メールが空白にできません",
|
||||
"Email is invalid": "電子メールは無効です",
|
||||
"Empty username.": "空のユーザー名。",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "ファーストネームは空白にできません",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "ユーザーはサインインできません。管理者に連絡してください",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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 value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "ユーザー名はすでに存在しています",
|
||||
"Username cannot be an email address": "ユーザー名には電子メールアドレスを使用できません",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "不足しているパラメーター",
|
||||
"Please login first": "最初にログインしてください",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "そのユーザー:%sは存在しません",
|
||||
"don't support captchaProvider: ": "captchaProviderをサポートしないでください",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "%sは不変です。",
|
||||
"Unknown modify rule %s.": "未知の変更ルール%s。"
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "アプリケーションIDが無効です",
|
||||
"the provider: %s does not exist": "プロバイダー%sは存在しません"
|
||||
@ -116,7 +126,6 @@
|
||||
"The provider type: %s is not supported": "プロバイダータイプ:%sはサポートされていません"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "クライアントIDまたはクライアントシークレットが空です",
|
||||
"Grant_type: %s is not supported in this application": "grant_type:%sはこのアプリケーションでサポートされていません",
|
||||
"Invalid application or wrong clientSecret": "無効なアプリケーションまたは誤ったクライアントシークレットです",
|
||||
"Invalid client_id": "client_idが無効です",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "不明なタイプ",
|
||||
"Wrong verification code!": "誤った検証コードです!",
|
||||
"You should verify your code in %d min!": "あなたは%d分であなたのコードを確認する必要があります!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "ユーザーは存在しません。まず登録してください"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "어플리케이션에서는 암호를 사용한 로그인 방법이 활성화되어 있지 않습니다",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
|
||||
"Unauthorized operation": "무단 조작",
|
||||
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "소속은 비워 둘 수 없습니다",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"DisplayName cannot be blank": "DisplayName는 비어 있을 수 없습니다",
|
||||
"DisplayName is not valid real name": "DisplayName는 유효한 실제 이름이 아닙니다",
|
||||
"Email already exists": "이메일이 이미 존재합니다",
|
||||
"Email cannot be empty": "이메일은 비어 있을 수 없습니다",
|
||||
"Email is invalid": "이메일이 유효하지 않습니다",
|
||||
"Empty username.": "빈 사용자 이름.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "이름은 공백일 수 없습니다",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "사용자는 로그인이 금지되어 있습니다. 관리자에게 문의하십시오",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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 value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "사용자 이름이 이미 존재합니다",
|
||||
"Username cannot be an email address": "사용자 이름은 이메일 주소가 될 수 없습니다",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "누락된 매개변수",
|
||||
"Please login first": "먼저 로그인 하십시오",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "사용자 %s는 존재하지 않습니다",
|
||||
"don't support captchaProvider: ": "CaptchaProvider를 지원하지 마세요",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "%s 는 변경할 수 없습니다.",
|
||||
"Unknown modify rule %s.": "미확인 수정 규칙 %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "잘못된 애플리케이션 ID입니다",
|
||||
"the provider: %s does not exist": "제공자 %s가 존재하지 않습니다"
|
||||
@ -116,7 +126,6 @@
|
||||
"The provider type: %s is not supported": "제공자 유형: %s은/는 지원되지 않습니다"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "클라이언트 ID 또는 클라이언트 비밀번호가 비어 있습니다",
|
||||
"Grant_type: %s is not supported in this application": "그랜트 유형: %s은(는) 이 어플리케이션에서 지원되지 않습니다",
|
||||
"Invalid application or wrong clientSecret": "잘못된 어플리케이션 또는 올바르지 않은 클라이언트 시크릿입니다",
|
||||
"Invalid client_id": "잘못된 클라이언트 ID입니다",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "알 수 없는 유형",
|
||||
"Wrong verification code!": "잘못된 인증 코드입니다!",
|
||||
"You should verify your code in %d min!": "당신은 %d분 안에 코드를 검증해야 합니다!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "사용자가 존재하지 않습니다. 먼저 회원 가입 해주세요"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "O %s é imutável.",
|
||||
"Unknown modify rule %s.": "Regra de modificação %s desconhecida."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Id do aplicativo inválido",
|
||||
"the provider: %s does not exist": "o provedor: %s não existe"
|
||||
@ -116,7 +126,6 @@
|
||||
"The provider type: %s is not supported": "The provider type: %s is not supported"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "ClientId ou clientSecret vazio",
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
|
||||
"Invalid application or wrong clientSecret": "Aplicativo inválido ou clientSecret errado",
|
||||
"Invalid client_id": "client_id inválido",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
|
||||
"Unauthorized operation": "Несанкционированная операция",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Принадлежность не может быть пустым значением",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"DisplayName cannot be blank": "Имя отображения не может быть пустым",
|
||||
"DisplayName is not valid real name": "DisplayName не является действительным именем",
|
||||
"Email already exists": "Электронная почта уже существует",
|
||||
"Email cannot be empty": "Электронная почта не может быть пустой",
|
||||
"Email is invalid": "Адрес электронной почты недействительный",
|
||||
"Empty username.": "Пустое имя пользователя.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "Имя не может быть пустым",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "Пользователю запрещен вход, пожалуйста, обратитесь к администратору",
|
||||
"The user: %s doesn't exist in LDAP server": "Пользователь %s не существует на LDAP сервере",
|
||||
"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 value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Имя пользователя уже существует",
|
||||
"Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Отсутствующий параметр",
|
||||
"Please login first": "Пожалуйста, сначала войдите в систему",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "Пользователь %s не существует",
|
||||
"don't support captchaProvider: ": "неподдерживаемый captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "эта операция не разрешена в демо-режиме"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "%s неизменяемый.",
|
||||
"Unknown modify rule %s.": "Неизвестное изменение правила %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Неверный идентификатор приложения",
|
||||
"the provider: %s does not exist": "провайдер: %s не существует"
|
||||
@ -116,7 +126,6 @@
|
||||
"The provider type: %s is not supported": "Тип провайдера: %s не поддерживается"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "Пустой идентификатор клиента или секрет клиента",
|
||||
"Grant_type: %s is not supported in this application": "Тип предоставления: %s не поддерживается в данном приложении",
|
||||
"Invalid application or wrong clientSecret": "Недействительное приложение или неправильный clientSecret",
|
||||
"Invalid client_id": "Недействительный идентификатор клиента",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Неизвестный тип",
|
||||
"Wrong verification code!": "Неправильный код подтверждения!",
|
||||
"You should verify your code in %d min!": "Вы должны проверить свой код через %d минут!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Kullanıcı adı zaten var",
|
||||
"Username cannot be an email address": "Kullanıcı adı bir e-mail adresi olamaz",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"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 organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"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.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
@ -116,7 +126,6 @@
|
||||
"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",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,7 +18,9 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "Phương thức đăng nhập: đăng nhập bằng mật khẩu không được kích hoạt cho ứng dụng",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "Nhà cung cấp: %s không được kích hoạt cho ứng dụng",
|
||||
"Unauthorized operation": "Hoạt động không được ủy quyền",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s",
|
||||
@ -30,12 +32,15 @@
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Tình trạng liên kết không thể để trống",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"DisplayName cannot be blank": "Tên hiển thị không thể để trống",
|
||||
"DisplayName is not valid real name": "DisplayName không phải là tên thật hợp lệ",
|
||||
"Email already exists": "Email đã tồn tại",
|
||||
"Email cannot be empty": "Email không thể để trống",
|
||||
"Email is invalid": "Địa chỉ email không hợp lệ",
|
||||
"Empty username.": "Tên đăng nhập trống.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "Tên không được để trống",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "Người dùng bị cấm đăng nhập, vui lòng liên hệ với quản trị viên",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"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.": "Tên người dùng chỉ có thể chứa các ký tự chữ và số, gạch dưới hoặc gạch ngang, không được có hai ký tự gạch dưới hoặc gạch ngang liền kề và không được bắt đầu hoặc kết thúc bằng dấu gạch dưới hoặc gạch ngang.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Tên đăng nhập đã tồn tại",
|
||||
"Username cannot be an email address": "Tên người dùng không thể là địa chỉ email",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "Thiếu tham số",
|
||||
"Please login first": "Vui lòng đăng nhập trước",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "Người dùng: %s không tồn tại",
|
||||
"don't support captchaProvider: ": "không hỗ trợ captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "%s không thể thay đổi được.",
|
||||
"Unknown modify rule %s.": "Quy tắc thay đổi không xác định %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Sai ID ứng dụng",
|
||||
"the provider: %s does not exist": "Nhà cung cấp: %s không tồn tại"
|
||||
@ -116,7 +126,6 @@
|
||||
"The provider type: %s is not supported": "Loại nhà cung cấp: %s không được hỗ trợ"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "ClientId hoặc clientSecret trống",
|
||||
"Grant_type: %s is not supported in this application": "Loại cấp phép: %s không được hỗ trợ trong ứng dụng này",
|
||||
"Invalid application or wrong clientSecret": "Đơn đăng ký không hợp lệ hoặc sai clientSecret",
|
||||
"Invalid client_id": "Client_id không hợp lệ",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "Loại không xác định",
|
||||
"Wrong verification code!": "Mã xác thực sai!",
|
||||
"You should verify your code in %d min!": "Bạn nên kiểm tra mã của mình trong %d phút!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "Người dùng không tồn tại, vui lòng đăng ký trước"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -18,28 +18,33 @@
|
||||
"The login method: login with LDAP is not enabled for the application": "该应用禁止采用LDAP登录方式",
|
||||
"The login method: login with SMS is not enabled for the application": "该应用禁止采用短信登录方式",
|
||||
"The login method: login with email is not enabled for the application": "该应用禁止采用邮箱登录方式",
|
||||
"The login method: login with face is not enabled for the application": "该应用禁止采用人脸登录",
|
||||
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",
|
||||
"The organization: %s does not exist": "组织: %s 不存在",
|
||||
"The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
|
||||
"Unauthorized operation": "未授权的操作",
|
||||
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s",
|
||||
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s 没有激活或正在等待订阅并且应用: %s 没有默认值"
|
||||
},
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "服务%s与%s不匹配"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "工作单位不可为空",
|
||||
"Default code does not match the code's matching rules": "邀请码默认值和邀请码规则不匹配",
|
||||
"DisplayName cannot be blank": "显示名称不可为空",
|
||||
"DisplayName is not valid real name": "显示名称必须是真实姓名",
|
||||
"Email already exists": "该邮箱已存在",
|
||||
"Email cannot be empty": "邮箱不可为空",
|
||||
"Email is invalid": "无效邮箱",
|
||||
"Empty username.": "用户名不可为空",
|
||||
"Face data does not exist, cannot log in": "未录入人脸数据,无法登录",
|
||||
"Face data mismatch": "人脸不匹配",
|
||||
"FirstName cannot be blank": "名不可以为空",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code cannot be blank": "邀请码不能为空",
|
||||
"Invitation code exhausted": "邀请码使用次数已耗尽",
|
||||
"Invitation code is invalid": "Invitation code is invalid",
|
||||
"Invitation code is invalid": "邀请码无效",
|
||||
"Invitation code suspended": "邀请码已被禁止使用",
|
||||
"LDAP user name or password incorrect": "LDAP密码错误",
|
||||
"LastName cannot be blank": "姓不可以为空",
|
||||
@ -56,6 +61,7 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "该用户被禁止登录,请联系管理员",
|
||||
"The user: %s doesn't exist in LDAP server": "用户: %s 在LDAP服务器中未找到",
|
||||
"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 value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "值 \\\"%s\\\"在账户信息字段\\\"%s\\\" 中与应用的账户项正则表达式不匹配",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "值\\\"%s\\\"在注册字段\\\"%s\\\"中与应用\\\"%s\\\"的注册项正则表达式不匹配",
|
||||
"Username already exists": "用户名已存在",
|
||||
"Username cannot be an email address": "用户名不可以是邮箱地址",
|
||||
@ -72,6 +78,7 @@
|
||||
"general": {
|
||||
"Missing parameter": "缺少参数",
|
||||
"Please login first": "请先登录",
|
||||
"The organization: %s should have one application at least": "组织: %s 应该拥有至少一个应用",
|
||||
"The user: %s doesn't exist": "用户: %s不存在",
|
||||
"don't support captchaProvider: ": "不支持验证码提供商: ",
|
||||
"this operation is not allowed in demo mode": "demo模式下不允许该操作"
|
||||
@ -92,6 +99,9 @@
|
||||
"The %s is immutable.": "%s 是不可变的",
|
||||
"Unknown modify rule %s.": "未知的修改规则: %s"
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "权限: \\\"%s\\\" 不存在"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "无效的应用ID",
|
||||
"the provider: %s does not exist": "提供商: %s不存在"
|
||||
@ -116,7 +126,6 @@
|
||||
"The provider type: %s is not supported": "不支持的提供商类型: %s"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "clientId或clientSecret为空",
|
||||
"Grant_type: %s is not supported in this application": "该应用不支持Grant_type: %s",
|
||||
"Invalid application or wrong clientSecret": "无效应用或错误的clientSecret",
|
||||
"Invalid client_id": "无效的ClientId",
|
||||
@ -145,6 +154,8 @@
|
||||
"Unknown type": "未知类型",
|
||||
"Wrong verification code!": "验证码错误!",
|
||||
"You should verify your code in %d min!": "请在 %d 分钟内输入正确验证码",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "请添加一个SMS提供商到应用: %s 的 \\\"提供商 \\\"列表",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "请添加一个Email提供商到应用: %s 的 \\\"提供商 \\\"列表",
|
||||
"the user does not exist, please sign up first": "用户不存在,请先注册"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -121,6 +121,9 @@ func (idp *AdfsIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var respKeys struct {
|
||||
Keys []interface{} `json:"keys"`
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ import (
|
||||
"github.com/markbates/goth/providers/twitterv2"
|
||||
"github.com/markbates/goth/providers/typetalk"
|
||||
"github.com/markbates/goth/providers/uber"
|
||||
"github.com/markbates/goth/providers/vk"
|
||||
"github.com/markbates/goth/providers/wepay"
|
||||
"github.com/markbates/goth/providers/xero"
|
||||
"github.com/markbates/goth/providers/yahoo"
|
||||
@ -371,6 +372,11 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
|
||||
Provider: uber.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &uber.Session{},
|
||||
}
|
||||
case "VK":
|
||||
idp = GothIdProvider{
|
||||
Provider: vk.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &vk.Session{},
|
||||
}
|
||||
case "Wepay":
|
||||
idp = GothIdProvider{
|
||||
Provider: wepay.New(clientId, clientSecret, redirectUrl),
|
||||
|
104
idp/wechat.go
104
idp/wechat.go
@ -16,25 +16,38 @@ package idp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/skip2/go-qrcode"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
WechatCacheMap map[string]WechatCacheMapValue
|
||||
Lock sync.RWMutex
|
||||
)
|
||||
|
||||
type WeChatIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
}
|
||||
|
||||
type WechatCacheMapValue struct {
|
||||
IsScanned bool
|
||||
WechatUnionId string
|
||||
}
|
||||
|
||||
func NewWeChatIdProvider(clientId string, clientSecret string, redirectUrl string) *WeChatIdProvider {
|
||||
idp := &WeChatIdProvider{}
|
||||
|
||||
@ -77,6 +90,15 @@ type WechatAccessToken struct {
|
||||
// GetToken use code get access_token (*operation of getting code ought to be done in front)
|
||||
// get more detail via: https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
|
||||
func (idp *WeChatIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
if strings.HasPrefix(code, "wechat_oa:") {
|
||||
token := oauth2.Token{
|
||||
AccessToken: code,
|
||||
TokenType: "WeChatAccessToken",
|
||||
Expiry: time.Time{},
|
||||
}
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("grant_type", "authorization_code")
|
||||
params.Add("appid", idp.Config.ClientID)
|
||||
@ -157,6 +179,29 @@ type WechatUserInfo struct {
|
||||
func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
var wechatUserInfo WechatUserInfo
|
||||
accessToken := token.AccessToken
|
||||
|
||||
if strings.HasPrefix(accessToken, "wechat_oa:") {
|
||||
Lock.RLock()
|
||||
mapValue, ok := WechatCacheMap[accessToken[10:]]
|
||||
Lock.RUnlock()
|
||||
|
||||
if !ok || mapValue.WechatUnionId == "" {
|
||||
return nil, fmt.Errorf("error ticket")
|
||||
}
|
||||
|
||||
Lock.Lock()
|
||||
delete(WechatCacheMap, accessToken[10:])
|
||||
Lock.Unlock()
|
||||
|
||||
userInfo := UserInfo{
|
||||
Id: mapValue.WechatUnionId,
|
||||
Username: "wx_user_" + mapValue.WechatUnionId,
|
||||
DisplayName: "wx_user_" + mapValue.WechatUnionId,
|
||||
AvatarUrl: "",
|
||||
}
|
||||
return &userInfo, nil
|
||||
}
|
||||
|
||||
openid := token.Extra("Openid")
|
||||
|
||||
userInfoUrl := fmt.Sprintf("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s", accessToken, openid)
|
||||
@ -204,60 +249,70 @@ func BuildWechatOpenIdKey(appId string) string {
|
||||
return fmt.Sprintf("wechat_openid_%s", appId)
|
||||
}
|
||||
|
||||
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
|
||||
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, string, error) {
|
||||
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", clientId, clientSecret)
|
||||
request, err := http.NewRequest("GET", accessTokenUrl, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
client := new(http.Client)
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var data struct {
|
||||
ExpireIn int `json:"expires_in"`
|
||||
AccessToken string `json:"access_token"`
|
||||
ErrCode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
}
|
||||
err = json.Unmarshal(respBytes, &data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return data.AccessToken, nil
|
||||
return data.AccessToken, data.Errmsg, nil
|
||||
}
|
||||
|
||||
func GetWechatOfficialAccountQRCode(clientId string, clientSecret string) (string, error) {
|
||||
accessToken, err := GetWechatOfficialAccountAccessToken(clientId, clientSecret)
|
||||
func GetWechatOfficialAccountQRCode(clientId string, clientSecret string, providerId string) (string, string, error) {
|
||||
accessToken, errMsg, err := GetWechatOfficialAccountAccessToken(clientId, clientSecret)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if errMsg != "" {
|
||||
return "", "", fmt.Errorf("Fail to fetch WeChat QRcode: %s", errMsg)
|
||||
}
|
||||
|
||||
client := new(http.Client)
|
||||
|
||||
weChatEndpoint := "https://api.weixin.qq.com/cgi-bin/qrcode/create"
|
||||
qrCodeUrl := fmt.Sprintf("%s?access_token=%s", weChatEndpoint, accessToken)
|
||||
params := `{"action_name": "QR_LIMIT_STR_SCENE", "action_info": {"scene": {"scene_str": "test"}}}`
|
||||
params := fmt.Sprintf(`{"expire_seconds": 3600, "action_name": "QR_STR_SCENE", "action_info": {"scene": {"scene_str": "%s"}}}`, providerId)
|
||||
|
||||
bodyData := bytes.NewReader([]byte(params))
|
||||
requeset, err := http.NewRequest("POST", qrCodeUrl, bodyData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
resp, err := client.Do(requeset)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
var data struct {
|
||||
Ticket string `json:"ticket"`
|
||||
@ -266,11 +321,26 @@ func GetWechatOfficialAccountQRCode(clientId string, clientSecret string) (strin
|
||||
}
|
||||
err = json.Unmarshal(respBytes, &data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var png []byte
|
||||
png, err = qrcode.Encode(data.URL, qrcode.Medium, 256)
|
||||
base64Image := base64.StdEncoding.EncodeToString(png)
|
||||
return base64Image, nil
|
||||
return base64Image, data.Ticket, nil
|
||||
}
|
||||
|
||||
func VerifyWechatSignature(token string, nonce string, timestamp string, signature string) bool {
|
||||
// verify the signature
|
||||
tmpArr := sort.StringSlice{token, timestamp, nonce}
|
||||
sort.Sort(tmpArr)
|
||||
|
||||
tmpStr := ""
|
||||
for _, str := range tmpArr {
|
||||
tmpStr = tmpStr + str
|
||||
}
|
||||
|
||||
b := sha1.Sum([]byte(tmpStr))
|
||||
res := hex.EncodeToString(b[:])
|
||||
return res == signature
|
||||
}
|
||||
|
@ -8,12 +8,62 @@
|
||||
"favicon": "",
|
||||
"passwordType": "plain",
|
||||
"passwordSalt": "",
|
||||
"passwordOptions": ["AtLeast6"],
|
||||
"countryCodes": ["US", "GB", "ES", "FR", "DE", "CN", "JP", "KR", "VN", "ID", "SG", "IN", "IT", "MY", "TR", "DZ", "IL", "PH", "NL", "PL", "FI", "SE", "UA", "KZ"],
|
||||
"passwordOptions": [
|
||||
"AtLeast6"
|
||||
],
|
||||
"countryCodes": [
|
||||
"US",
|
||||
"GB",
|
||||
"ES",
|
||||
"FR",
|
||||
"DE",
|
||||
"CN",
|
||||
"JP",
|
||||
"KR",
|
||||
"VN",
|
||||
"ID",
|
||||
"SG",
|
||||
"IN",
|
||||
"IT",
|
||||
"MY",
|
||||
"TR",
|
||||
"DZ",
|
||||
"IL",
|
||||
"PH",
|
||||
"NL",
|
||||
"PL",
|
||||
"FI",
|
||||
"SE",
|
||||
"UA",
|
||||
"KZ"
|
||||
],
|
||||
"defaultAvatar": "",
|
||||
"defaultApplication": "",
|
||||
"tags": [],
|
||||
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr","ar", "he", "nl", "pl", "fi", "sv", "uk", "kk", "fa"],
|
||||
"languages": [
|
||||
"en",
|
||||
"zh",
|
||||
"es",
|
||||
"fr",
|
||||
"de",
|
||||
"id",
|
||||
"ja",
|
||||
"ko",
|
||||
"ru",
|
||||
"vi",
|
||||
"it",
|
||||
"ms",
|
||||
"tr",
|
||||
"ar",
|
||||
"he",
|
||||
"nl",
|
||||
"pl",
|
||||
"fi",
|
||||
"sv",
|
||||
"uk",
|
||||
"kk",
|
||||
"fa"
|
||||
],
|
||||
"masterPassword": "",
|
||||
"defaultPassword": "",
|
||||
"initScore": 2000,
|
||||
@ -49,18 +99,23 @@
|
||||
{
|
||||
"name": "Password",
|
||||
"displayName": "Password",
|
||||
"rule": "All",
|
||||
"rule": "All"
|
||||
},
|
||||
{
|
||||
"name": "Verification code",
|
||||
"displayName": "Verification code",
|
||||
"rule": "All",
|
||||
"rule": "All"
|
||||
},
|
||||
{
|
||||
"name": "WebAuthn",
|
||||
"displayName": "WebAuthn",
|
||||
"rule": "None",
|
||||
"rule": "None"
|
||||
},
|
||||
{
|
||||
"name": "Face ID",
|
||||
"displayName": "Face ID",
|
||||
"rule": "None"
|
||||
}
|
||||
],
|
||||
"signupItems": [
|
||||
{
|
||||
@ -72,55 +127,67 @@
|
||||
},
|
||||
{
|
||||
"name": "Username",
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
},
|
||||
{
|
||||
"name": "Display name",
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
},
|
||||
{
|
||||
"name": "Password",
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
},
|
||||
{
|
||||
"name": "Confirm password",
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
},
|
||||
{
|
||||
"name": "Email",
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
},
|
||||
{
|
||||
"name": "Phone",
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
},
|
||||
{
|
||||
"name": "Agreement",
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
"visible": true,
|
||||
"required": true,
|
||||
"prompted": false,
|
||||
"rule": "None"
|
||||
}
|
||||
],
|
||||
"redirectUris": [""],
|
||||
"grantTypes": [
|
||||
"authorization_code",
|
||||
"password",
|
||||
"client_credentials",
|
||||
"token",
|
||||
"id_token",
|
||||
"refresh_token"
|
||||
],
|
||||
"redirectUris": [
|
||||
"http://localhost:9000/callback"
|
||||
],
|
||||
"tokenFormat": "JWT",
|
||||
"tokenFields": [],
|
||||
"expireInHours": 168,
|
||||
"failedSigninLimit": 5,
|
||||
"failedSigninFrozenTime": 15
|
||||
@ -146,7 +213,8 @@
|
||||
"isForbidden": false,
|
||||
"isDeleted": false,
|
||||
"signupApplication": "",
|
||||
"createdIp": ""
|
||||
"createdIp": "",
|
||||
"groups": []
|
||||
}
|
||||
],
|
||||
"providers": [
|
||||
@ -349,5 +417,74 @@
|
||||
"owner": "",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"owner": "",
|
||||
"name": "",
|
||||
"displayName": "",
|
||||
"manager": "",
|
||||
"contactEmail": "",
|
||||
"type": "",
|
||||
"parent_id": "",
|
||||
"isTopGroup": true,
|
||||
"title": "",
|
||||
"key": "",
|
||||
"children": "",
|
||||
"isEnabled": true
|
||||
}
|
||||
],
|
||||
"adapters": [
|
||||
{
|
||||
"owner": "",
|
||||
"name": "",
|
||||
"table": "",
|
||||
"useSameDb": true,
|
||||
"type": "",
|
||||
"databaseType": "",
|
||||
"database": "",
|
||||
"host": "",
|
||||
"port": 0,
|
||||
"user": "",
|
||||
"password": ""
|
||||
}
|
||||
],
|
||||
"enforcers": [
|
||||
{
|
||||
"owner": "",
|
||||
"name": "",
|
||||
"displayName": "",
|
||||
"description": "",
|
||||
"model": "",
|
||||
"adapter": "",
|
||||
"enforcer": ""
|
||||
}
|
||||
],
|
||||
"plans": [
|
||||
{
|
||||
"owner": "",
|
||||
"name": "",
|
||||
"displayName": "",
|
||||
"description": "",
|
||||
"price": 0,
|
||||
"currency": "",
|
||||
"period": "",
|
||||
"product": "",
|
||||
"paymentProviders": [],
|
||||
"isEnabled": true,
|
||||
"role": ""
|
||||
}
|
||||
],
|
||||
"pricings": [
|
||||
{
|
||||
"owner": "",
|
||||
"name": "",
|
||||
"displayName": "",
|
||||
"description": "",
|
||||
"plans": [],
|
||||
"isEnabled": true,
|
||||
"trialDuration": 0,
|
||||
"application": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
2
main.go
2
main.go
@ -36,12 +36,12 @@ func main() {
|
||||
object.CreateTables()
|
||||
|
||||
object.InitDb()
|
||||
object.InitFromFile()
|
||||
object.InitDefaultStorageProvider()
|
||||
object.InitLdapAutoSynchronizer()
|
||||
proxy.InitHttpClient()
|
||||
authz.InitApi()
|
||||
object.InitUserManager()
|
||||
object.InitFromFile()
|
||||
object.InitCasvisorConfig()
|
||||
|
||||
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
|
||||
|
@ -37,7 +37,7 @@ type Adapter struct {
|
||||
Host string `xorm:"varchar(100)" json:"host"`
|
||||
Port int `json:"port"`
|
||||
User string `xorm:"varchar(100)" json:"user"`
|
||||
Password string `xorm:"varchar(100)" json:"password"`
|
||||
Password string `xorm:"varchar(150)" json:"password"`
|
||||
Database string `xorm:"varchar(100)" json:"database"`
|
||||
|
||||
*xormadapter.Adapter `xorm:"-" json:"-"`
|
||||
@ -178,6 +178,7 @@ func (adapter *Adapter) InitAdapter() error {
|
||||
dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.")
|
||||
}
|
||||
|
||||
dataSourceName = conf.ReplaceDataSourceNameByDocker(dataSourceName)
|
||||
engine, err := xorm.NewEngine(driverName, dataSourceName)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/idp"
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
@ -41,6 +41,15 @@ type SignupItem struct {
|
||||
Rule string `json:"rule"`
|
||||
}
|
||||
|
||||
type SigninItem struct {
|
||||
Name string `json:"name"`
|
||||
Visible bool `json:"visible"`
|
||||
Label string `json:"label"`
|
||||
Placeholder string `json:"placeholder"`
|
||||
Rule string `json:"rule"`
|
||||
IsCustom bool `json:"isCustom"`
|
||||
}
|
||||
|
||||
type SamlItem struct {
|
||||
Name string `json:"name"`
|
||||
NameFormat string `json:"nameFormat"`
|
||||
@ -52,31 +61,34 @@ type Application struct {
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Logo string `xorm:"varchar(200)" json:"logo"`
|
||||
HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
|
||||
Description string `xorm:"varchar(100)" json:"description"`
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
Cert string `xorm:"varchar(100)" json:"cert"`
|
||||
EnablePassword bool `json:"enablePassword"`
|
||||
EnableSignUp bool `json:"enableSignUp"`
|
||||
EnableSigninSession bool `json:"enableSigninSession"`
|
||||
EnableAutoSignin bool `json:"enableAutoSignin"`
|
||||
EnableCodeSignin bool `json:"enableCodeSignin"`
|
||||
EnableSamlCompress bool `json:"enableSamlCompress"`
|
||||
EnableSamlC14n10 bool `json:"enableSamlC14n10"`
|
||||
EnableWebAuthn bool `json:"enableWebAuthn"`
|
||||
EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
|
||||
OrgChoiceMode string `json:"orgChoiceMode"`
|
||||
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
|
||||
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
|
||||
SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"`
|
||||
SignupItems []*SignupItem `xorm:"varchar(2000)" json:"signupItems"`
|
||||
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
|
||||
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
|
||||
CertPublicKey string `xorm:"-" json:"certPublicKey"`
|
||||
Tags []string `xorm:"mediumtext" json:"tags"`
|
||||
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Logo string `xorm:"varchar(200)" json:"logo"`
|
||||
HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
|
||||
Description string `xorm:"varchar(100)" json:"description"`
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
Cert string `xorm:"varchar(100)" json:"cert"`
|
||||
HeaderHtml string `xorm:"mediumtext" json:"headerHtml"`
|
||||
EnablePassword bool `json:"enablePassword"`
|
||||
EnableSignUp bool `json:"enableSignUp"`
|
||||
EnableSigninSession bool `json:"enableSigninSession"`
|
||||
EnableAutoSignin bool `json:"enableAutoSignin"`
|
||||
EnableCodeSignin bool `json:"enableCodeSignin"`
|
||||
EnableSamlCompress bool `json:"enableSamlCompress"`
|
||||
EnableSamlC14n10 bool `json:"enableSamlC14n10"`
|
||||
EnableSamlPostBinding bool `json:"enableSamlPostBinding"`
|
||||
EnableWebAuthn bool `json:"enableWebAuthn"`
|
||||
EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
|
||||
OrgChoiceMode string `json:"orgChoiceMode"`
|
||||
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
|
||||
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
|
||||
SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"`
|
||||
SignupItems []*SignupItem `xorm:"varchar(2000)" json:"signupItems"`
|
||||
SigninItems []*SigninItem `xorm:"mediumtext" json:"signinItems"`
|
||||
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
|
||||
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
|
||||
CertPublicKey string `xorm:"-" json:"certPublicKey"`
|
||||
Tags []string `xorm:"mediumtext" json:"tags"`
|
||||
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
|
||||
|
||||
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
||||
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
||||
@ -93,6 +105,7 @@ type Application struct {
|
||||
SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
|
||||
SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
|
||||
ThemeData *ThemeData `xorm:"json" json:"themeData"`
|
||||
FooterHtml string `xorm:"mediumtext" json:"footerHtml"`
|
||||
FormCss string `xorm:"text" json:"formCss"`
|
||||
FormCssMobile string `xorm:"text" json:"formCssMobile"`
|
||||
FormOffset int `json:"formOffset"`
|
||||
@ -163,15 +176,6 @@ func getProviderMap(owner string) (m map[string]*Provider, err error) {
|
||||
|
||||
m = map[string]*Provider{}
|
||||
for _, provider := range providers {
|
||||
// Get QRCode only once
|
||||
if provider.Type == "WeChat" && provider.DisableSsl && provider.Content == "" {
|
||||
provider.Content, err = idp.GetWechatOfficialAccountQRCode(provider.ClientId2, provider.ClientSecret2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
UpdateProvider(provider.Owner+"/"+provider.Name, provider)
|
||||
}
|
||||
|
||||
m[provider.Name] = GetMaskedProvider(provider, true)
|
||||
}
|
||||
|
||||
@ -199,6 +203,100 @@ func extendApplicationWithOrg(application *Application) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func extendApplicationWithSigninItems(application *Application) (err error) {
|
||||
if len(application.SigninItems) == 0 {
|
||||
signinItem := &SigninItem{
|
||||
Name: "Back button",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .back-button {\n top: 65px;\n left: 15px;\n position: absolute;\n }\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Languages",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .login-languages {\n top: 55px;\n right: 5px;\n position: absolute;\n }\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Logo",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .login-logo-box {\n }\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Signin methods",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .signin-methods {\n }\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Username",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .login-username {\n }\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Password",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .login-password {\n }\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Agreement",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .login-agreement {\n }\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Forgot password?",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .login-forget-password {\n display: inline-flex;\n justify-content: space-between;\n width: 320px;\n margin-bottom: 25px;\n }\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Login button",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .login-button-box {\n margin-bottom: 5px;\n }\n .login-button {\n width: 100%;\n }\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Signup link",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .login-signup-link {\n margin-bottom: 24px;\n display: flex;\n justify-content: end;\n}\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Providers",
|
||||
Visible: true,
|
||||
Label: "\n<style>\n .provider-img {\n width: 30px;\n margin: 5px;\n }\n .provider-big-img {\n margin-bottom: 10px;\n }\n</style>\n",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func extendApplicationWithSigninMethods(application *Application) (err error) {
|
||||
if len(application.SigninMethods) == 0 {
|
||||
if application.EnablePassword {
|
||||
@ -213,6 +311,9 @@ func extendApplicationWithSigninMethods(application *Application) (err error) {
|
||||
signinMethod := &SigninMethod{Name: "WebAuthn", DisplayName: "WebAuthn", Rule: "None"}
|
||||
application.SigninMethods = append(application.SigninMethods, signinMethod)
|
||||
}
|
||||
|
||||
signinMethod := &SigninMethod{Name: "Face ID", DisplayName: "Face ID", Rule: "None"}
|
||||
application.SigninMethods = append(application.SigninMethods, signinMethod)
|
||||
}
|
||||
|
||||
if len(application.SigninMethods) == 0 {
|
||||
@ -249,6 +350,10 @@ func getApplication(owner string, name string) (*Application, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = extendApplicationWithSigninItems(&application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &application, nil
|
||||
} else {
|
||||
@ -279,6 +384,11 @@ func GetApplicationByOrganizationName(organization string) (*Application, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = extendApplicationWithSigninItems(&application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &application, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
@ -331,6 +441,11 @@ func GetApplicationByClientId(clientId string) (*Application, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = extendApplicationWithSigninItems(&application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &application, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
@ -358,36 +473,71 @@ func GetMaskedApplication(application *Application, userId string) *Application
|
||||
application.FailedSigninFrozenTime = DefaultFailedSigninFrozenTime
|
||||
}
|
||||
|
||||
isOrgUser := false
|
||||
if userId != "" {
|
||||
if isUserIdGlobalAdmin(userId) {
|
||||
return application
|
||||
}
|
||||
|
||||
user, _ := GetUser(userId)
|
||||
if user != nil && user.IsApplicationAdmin(application) {
|
||||
return application
|
||||
user, err := GetUser(userId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if user != nil {
|
||||
if user.IsApplicationAdmin(application) {
|
||||
return application
|
||||
}
|
||||
|
||||
if user.Owner == application.Organization {
|
||||
isOrgUser = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if application.ClientSecret != "" {
|
||||
application.ClientSecret = "***"
|
||||
application.ClientSecret = "***"
|
||||
application.Cert = "***"
|
||||
application.EnablePassword = false
|
||||
application.EnableSigninSession = false
|
||||
application.EnableCodeSignin = false
|
||||
application.EnableSamlCompress = false
|
||||
application.EnableSamlC14n10 = false
|
||||
application.EnableSamlPostBinding = false
|
||||
application.EnableWebAuthn = false
|
||||
application.EnableLinkWithEmail = false
|
||||
application.SamlReplyUrl = "***"
|
||||
|
||||
providerItems := []*ProviderItem{}
|
||||
for _, providerItem := range application.Providers {
|
||||
if providerItem.Provider != nil && (providerItem.Provider.Category == "OAuth" || providerItem.Provider.Category == "Web3" || providerItem.Provider.Category == "Captcha") {
|
||||
providerItems = append(providerItems, providerItem)
|
||||
}
|
||||
}
|
||||
application.Providers = providerItems
|
||||
|
||||
application.GrantTypes = nil
|
||||
application.Tags = nil
|
||||
application.RedirectUris = nil
|
||||
application.TokenFormat = "***"
|
||||
application.TokenFields = nil
|
||||
application.ExpireInHours = -1
|
||||
application.RefreshExpireInHours = -1
|
||||
application.FailedSigninLimit = -1
|
||||
application.FailedSigninFrozenTime = -1
|
||||
|
||||
if application.OrganizationObj != nil {
|
||||
if application.OrganizationObj.MasterPassword != "" {
|
||||
application.OrganizationObj.MasterPassword = "***"
|
||||
}
|
||||
if application.OrganizationObj.DefaultPassword != "" {
|
||||
application.OrganizationObj.DefaultPassword = "***"
|
||||
}
|
||||
if application.OrganizationObj.MasterVerificationCode != "" {
|
||||
application.OrganizationObj.MasterVerificationCode = "***"
|
||||
}
|
||||
if application.OrganizationObj.PasswordType != "" {
|
||||
application.OrganizationObj.PasswordType = "***"
|
||||
}
|
||||
if application.OrganizationObj.PasswordSalt != "" {
|
||||
application.OrganizationObj.PasswordSalt = "***"
|
||||
application.OrganizationObj.MasterPassword = "***"
|
||||
application.OrganizationObj.DefaultPassword = "***"
|
||||
application.OrganizationObj.MasterVerificationCode = "***"
|
||||
application.OrganizationObj.PasswordType = "***"
|
||||
application.OrganizationObj.PasswordSalt = "***"
|
||||
application.OrganizationObj.InitScore = -1
|
||||
application.OrganizationObj.EnableSoftDeletion = false
|
||||
|
||||
if !isOrgUser {
|
||||
application.OrganizationObj.MfaItems = nil
|
||||
if !application.OrganizationObj.IsProfilePublic {
|
||||
application.OrganizationObj.AccountItems = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -405,8 +555,12 @@ func GetMaskedApplications(applications []*Application, userId string) []*Applic
|
||||
return applications
|
||||
}
|
||||
|
||||
func GetAllowedApplications(applications []*Application, userId string) ([]*Application, error) {
|
||||
if userId == "" || isUserIdGlobalAdmin(userId) {
|
||||
func GetAllowedApplications(applications []*Application, userId string, lang string) ([]*Application, error) {
|
||||
if userId == "" {
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
}
|
||||
|
||||
if isUserIdGlobalAdmin(userId) {
|
||||
return applications, nil
|
||||
}
|
||||
|
||||
@ -414,7 +568,11 @@ func GetAllowedApplications(applications []*Application, userId string) ([]*Appl
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user != nil && user.IsAdmin {
|
||||
if user == nil {
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
}
|
||||
|
||||
if user.IsAdmin {
|
||||
return applications, nil
|
||||
}
|
||||
|
||||
@ -529,7 +687,7 @@ func (application *Application) GetId() string {
|
||||
}
|
||||
|
||||
func (application *Application) IsRedirectUriValid(redirectUri string) bool {
|
||||
redirectUris := append([]string{"http://localhost:", "https://localhost:", "http://127.0.0.1:", "http://casdoor-app"}, application.RedirectUris...)
|
||||
redirectUris := append([]string{"http://localhost:", "https://localhost:", "http://127.0.0.1:", "http://casdoor-app", ".chromiumapp.org"}, application.RedirectUris...)
|
||||
for _, targetUri := range redirectUris {
|
||||
targetUriRegex := regexp.MustCompile(targetUri)
|
||||
if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) {
|
||||
@ -602,6 +760,17 @@ func (application *Application) IsLdapEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (application *Application) IsFaceIdEnabled() bool {
|
||||
if len(application.SigninMethods) > 0 {
|
||||
for _, signinMethod := range application.SigninMethods {
|
||||
if signinMethod.Name == "Face ID" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsOriginAllowed(origin string) (bool, error) {
|
||||
applications, err := GetApplications("")
|
||||
if err != nil {
|
||||
|
@ -38,12 +38,38 @@ func (application *Application) GetProviderByCategory(category string) (*Provide
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (application *Application) GetEmailProvider() (*Provider, error) {
|
||||
return application.GetProviderByCategory("Email")
|
||||
func (application *Application) GetProviderByCategoryAndRule(category string, method string) (*Provider, error) {
|
||||
providers, err := GetProviders(application.Organization)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := map[string]*Provider{}
|
||||
for _, provider := range providers {
|
||||
if provider.Category != category {
|
||||
continue
|
||||
}
|
||||
|
||||
m[provider.Name] = provider
|
||||
}
|
||||
|
||||
for _, providerItem := range application.Providers {
|
||||
if providerItem.Rule == method || (providerItem.Rule == "all" || providerItem.Rule == "" || providerItem.Rule == "None") {
|
||||
if provider, ok := m[providerItem.Name]; ok {
|
||||
return provider, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (application *Application) GetSmsProvider() (*Provider, error) {
|
||||
return application.GetProviderByCategory("SMS")
|
||||
func (application *Application) GetEmailProvider(method string) (*Provider, error) {
|
||||
return application.GetProviderByCategoryAndRule("Email", method)
|
||||
}
|
||||
|
||||
func (application *Application) GetSmsProvider(method string) (*Provider, error) {
|
||||
return application.GetProviderByCategoryAndRule("SMS", method)
|
||||
}
|
||||
|
||||
func (application *Application) GetStorageProvider() (*Provider, error) {
|
||||
|
@ -205,16 +205,41 @@ func (p *Cert) GetId() string {
|
||||
}
|
||||
|
||||
func (p *Cert) populateContent() error {
|
||||
if p.Certificate == "" || p.PrivateKey == "" {
|
||||
certificate, privateKey, err := generateRsaKeys(p.BitSize, p.ExpireInYears, p.Name, p.Owner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Certificate = certificate
|
||||
p.PrivateKey = privateKey
|
||||
if p.Certificate != "" && p.PrivateKey != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(p.CryptoAlgorithm) < 3 {
|
||||
err := fmt.Errorf("populateContent() error, unsupported crypto algorithm: %s", p.CryptoAlgorithm)
|
||||
return err
|
||||
}
|
||||
|
||||
if p.CryptoAlgorithm == "RSA" {
|
||||
p.CryptoAlgorithm = "RS256"
|
||||
}
|
||||
|
||||
sigAlgorithm := p.CryptoAlgorithm[:2]
|
||||
shaSize, err := util.ParseIntWithError(p.CryptoAlgorithm[2:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var certificate, privateKey string
|
||||
if sigAlgorithm == "RS" {
|
||||
certificate, privateKey, err = generateRsaKeys(p.BitSize, shaSize, p.ExpireInYears, p.Name, p.Owner)
|
||||
} else if sigAlgorithm == "ES" {
|
||||
certificate, privateKey, err = generateEsKeys(shaSize, p.ExpireInYears, p.Name, p.Owner)
|
||||
} else if sigAlgorithm == "PS" {
|
||||
certificate, privateKey, err = generateRsaPssKeys(p.BitSize, shaSize, p.ExpireInYears, p.Name, p.Owner)
|
||||
} else {
|
||||
err = fmt.Errorf("populateContent() error, unsupported signature algorithm: %s", sigAlgorithm)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Certificate = certificate
|
||||
p.PrivateKey = privateKey
|
||||
return nil
|
||||
}
|
||||
|
||||
|
BIN
object/cert.go~
BIN
object/cert.go~
Binary file not shown.
@ -184,6 +184,15 @@ func CheckInvitationCode(application *Application, organization *Organization, a
|
||||
}
|
||||
}
|
||||
|
||||
func CheckInvitationDefaultCode(code string, defaultCode string, lang string) error {
|
||||
if matched, err := util.IsInvitationCodeMatch(code, defaultCode); err != nil {
|
||||
return err
|
||||
} else if !matched {
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Default code does not match the code's matching rules"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSigninErrorTimes(user *User, lang string) error {
|
||||
failedSigninLimit, failedSigninFrozenTime, err := GetFailedSigninConfigByUser(user)
|
||||
if err != nil {
|
||||
|
@ -28,6 +28,10 @@ type Dashboard struct {
|
||||
}
|
||||
|
||||
func GetDashboard(owner string) (*Dashboard, error) {
|
||||
if owner == "All" {
|
||||
owner = ""
|
||||
}
|
||||
|
||||
dashboard := &Dashboard{
|
||||
OrganizationCounts: make([]int, 31),
|
||||
UserCounts: make([]int, 31),
|
||||
@ -36,14 +40,13 @@ func GetDashboard(owner string) (*Dashboard, error) {
|
||||
SubscriptionCounts: make([]int, 31),
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
organizations := []Organization{}
|
||||
users := []User{}
|
||||
providers := []Provider{}
|
||||
applications := []Application{}
|
||||
subscriptions := []Subscription{}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(5)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
@ -184,6 +184,7 @@ func initBuiltInApplication() {
|
||||
{Name: "Password", DisplayName: "Password", Rule: "All"},
|
||||
{Name: "Verification code", DisplayName: "Verification code", Rule: "All"},
|
||||
{Name: "WebAuthn", DisplayName: "WebAuthn", Rule: "None"},
|
||||
{Name: "Face ID", DisplayName: "Face ID", Rule: "None"},
|
||||
},
|
||||
SignupItems: []*SignupItem{
|
||||
{Name: "ID", Visible: false, Required: true, Prompted: false, Rule: "Random"},
|
||||
@ -197,6 +198,7 @@ func initBuiltInApplication() {
|
||||
},
|
||||
Tags: []string{},
|
||||
RedirectUris: []string{},
|
||||
TokenFormat: "JWT",
|
||||
TokenFields: []string{},
|
||||
ExpireInHours: 168,
|
||||
FormOffset: 2,
|
||||
|
@ -17,24 +17,35 @@ package object
|
||||
import (
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
|
||||
)
|
||||
|
||||
type InitData struct {
|
||||
Organizations []*Organization `json:"organizations"`
|
||||
Applications []*Application `json:"applications"`
|
||||
Users []*User `json:"users"`
|
||||
Certs []*Cert `json:"certs"`
|
||||
Providers []*Provider `json:"providers"`
|
||||
Ldaps []*Ldap `json:"ldaps"`
|
||||
Models []*Model `json:"models"`
|
||||
Permissions []*Permission `json:"permissions"`
|
||||
Payments []*Payment `json:"payments"`
|
||||
Products []*Product `json:"products"`
|
||||
Resources []*Resource `json:"resources"`
|
||||
Roles []*Role `json:"roles"`
|
||||
Syncers []*Syncer `json:"syncers"`
|
||||
Tokens []*Token `json:"tokens"`
|
||||
Webhooks []*Webhook `json:"webhooks"`
|
||||
Organizations []*Organization `json:"organizations"`
|
||||
Applications []*Application `json:"applications"`
|
||||
Users []*User `json:"users"`
|
||||
Certs []*Cert `json:"certs"`
|
||||
Providers []*Provider `json:"providers"`
|
||||
Ldaps []*Ldap `json:"ldaps"`
|
||||
Models []*Model `json:"models"`
|
||||
Permissions []*Permission `json:"permissions"`
|
||||
Payments []*Payment `json:"payments"`
|
||||
Products []*Product `json:"products"`
|
||||
Resources []*Resource `json:"resources"`
|
||||
Roles []*Role `json:"roles"`
|
||||
Syncers []*Syncer `json:"syncers"`
|
||||
Tokens []*Token `json:"tokens"`
|
||||
Webhooks []*Webhook `json:"webhooks"`
|
||||
Groups []*Group `json:"groups"`
|
||||
Adapters []*Adapter `json:"adapters"`
|
||||
Enforcers []*Enforcer `json:"enforcers"`
|
||||
Plans []*Plan `json:"plans"`
|
||||
Pricings []*Pricing `json:"pricings"`
|
||||
Invitations []*Invitation `json:"invitations"`
|
||||
Records []*casvisorsdk.Record `json:"records"`
|
||||
Sessions []*Session `json:"sessions"`
|
||||
Subscriptions []*Subscription `json:"subscriptions"`
|
||||
Transactions []*Transaction `json:"transactions"`
|
||||
}
|
||||
|
||||
func InitFromFile() {
|
||||
@ -94,6 +105,36 @@ func InitFromFile() {
|
||||
for _, webhook := range initData.Webhooks {
|
||||
initDefinedWebhook(webhook)
|
||||
}
|
||||
for _, group := range initData.Groups {
|
||||
initDefinedGroup(group)
|
||||
}
|
||||
for _, adapter := range initData.Adapters {
|
||||
initDefinedAdapter(adapter)
|
||||
}
|
||||
for _, enforcer := range initData.Enforcers {
|
||||
initDefinedEnforcer(enforcer)
|
||||
}
|
||||
for _, plan := range initData.Plans {
|
||||
initDefinedPlan(plan)
|
||||
}
|
||||
for _, pricing := range initData.Pricings {
|
||||
initDefinedPricing(pricing)
|
||||
}
|
||||
for _, invitation := range initData.Invitations {
|
||||
initDefinedInvitation(invitation)
|
||||
}
|
||||
for _, record := range initData.Records {
|
||||
initDefinedRecord(record)
|
||||
}
|
||||
for _, session := range initData.Sessions {
|
||||
initDefinedSession(session)
|
||||
}
|
||||
for _, subscription := range initData.Subscriptions {
|
||||
initDefinedSubscription(subscription)
|
||||
}
|
||||
for _, transaction := range initData.Transactions {
|
||||
initDefinedTransaction(transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,6 +161,16 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
|
||||
Syncers: []*Syncer{},
|
||||
Tokens: []*Token{},
|
||||
Webhooks: []*Webhook{},
|
||||
Groups: []*Group{},
|
||||
Adapters: []*Adapter{},
|
||||
Enforcers: []*Enforcer{},
|
||||
Plans: []*Plan{},
|
||||
Pricings: []*Pricing{},
|
||||
Invitations: []*Invitation{},
|
||||
Records: []*casvisorsdk.Record{},
|
||||
Sessions: []*Session{},
|
||||
Subscriptions: []*Subscription{},
|
||||
Transactions: []*Transaction{},
|
||||
}
|
||||
err := util.JsonToStruct(s, data)
|
||||
if err != nil {
|
||||
@ -190,7 +241,21 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
|
||||
webhook.Headers = []*Header{}
|
||||
}
|
||||
}
|
||||
|
||||
for _, plan := range data.Plans {
|
||||
if plan.PaymentProviders == nil {
|
||||
plan.PaymentProviders = []string{}
|
||||
}
|
||||
}
|
||||
for _, pricing := range data.Pricings {
|
||||
if pricing.Plans == nil {
|
||||
pricing.Plans = []string{}
|
||||
}
|
||||
}
|
||||
for _, session := range data.Sessions {
|
||||
if session.SessionId == nil {
|
||||
session.SessionId = []string{}
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@ -434,3 +499,136 @@ func initDefinedWebhook(webhook *Webhook) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedGroup(group *Group) {
|
||||
existed, err := getGroup(group.Owner, group.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if existed != nil {
|
||||
return
|
||||
}
|
||||
group.CreatedTime = util.GetCurrentTime()
|
||||
_, err = AddGroup(group)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedAdapter(adapter *Adapter) {
|
||||
existed, err := getAdapter(adapter.Owner, adapter.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if existed != nil {
|
||||
return
|
||||
}
|
||||
adapter.CreatedTime = util.GetCurrentTime()
|
||||
_, err = AddAdapter(adapter)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedEnforcer(enforcer *Enforcer) {
|
||||
existed, err := getEnforcer(enforcer.Owner, enforcer.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if existed != nil {
|
||||
return
|
||||
}
|
||||
enforcer.CreatedTime = util.GetCurrentTime()
|
||||
_, err = AddEnforcer(enforcer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedPlan(plan *Plan) {
|
||||
existed, err := getPlan(plan.Owner, plan.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if existed != nil {
|
||||
return
|
||||
}
|
||||
plan.CreatedTime = util.GetCurrentTime()
|
||||
_, err = AddPlan(plan)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedPricing(pricing *Pricing) {
|
||||
existed, err := getPlan(pricing.Owner, pricing.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if existed != nil {
|
||||
return
|
||||
}
|
||||
pricing.CreatedTime = util.GetCurrentTime()
|
||||
_, err = AddPricing(pricing)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedInvitation(invitation *Invitation) {
|
||||
existed, err := getInvitation(invitation.Owner, invitation.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if existed != nil {
|
||||
return
|
||||
}
|
||||
invitation.CreatedTime = util.GetCurrentTime()
|
||||
_, err = AddInvitation(invitation, "en")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedRecord(record *casvisorsdk.Record) {
|
||||
record.CreatedTime = util.GetCurrentTime()
|
||||
_ = AddRecord(record)
|
||||
}
|
||||
|
||||
func initDefinedSession(session *Session) {
|
||||
session.CreatedTime = util.GetCurrentTime()
|
||||
_, err := AddSession(session)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedSubscription(subscription *Subscription) {
|
||||
existed, err := getSubscription(subscription.Owner, subscription.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if existed != nil {
|
||||
return
|
||||
}
|
||||
subscription.CreatedTime = util.GetCurrentTime()
|
||||
_, err = AddSubscription(subscription)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedTransaction(transaction *Transaction) {
|
||||
existed, err := getTransaction(transaction.Owner, transaction.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if existed != nil {
|
||||
return
|
||||
}
|
||||
transaction.CreatedTime = util.GetCurrentTime()
|
||||
_, err = AddTransaction(transaction)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,56 @@ func writeInitDataToFile(filePath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
groups, err := GetGroups("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
adapters, err := GetAdapters("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enforcers, err := GetEnforcers("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
plans, err := GetPlans("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pricings, err := GetPricings("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
invitations, err := GetInvitations("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
records, err := GetRecords()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sessions, err := GetSessions("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subscriptions, err := GetSubscriptions("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transactions, err := GetTransactions("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := &InitData{
|
||||
Organizations: organizations,
|
||||
Applications: applications,
|
||||
@ -112,6 +162,16 @@ func writeInitDataToFile(filePath string) error {
|
||||
Syncers: syncers,
|
||||
Tokens: tokens,
|
||||
Webhooks: webhooks,
|
||||
Groups: groups,
|
||||
Adapters: adapters,
|
||||
Enforcers: enforcers,
|
||||
Plans: plans,
|
||||
Pricings: pricings,
|
||||
Invitations: invitations,
|
||||
Records: records,
|
||||
Sessions: sessions,
|
||||
Subscriptions: subscriptions,
|
||||
Transactions: transactions,
|
||||
}
|
||||
|
||||
text := util.StructToJsonFormatted(data)
|
||||
|
@ -40,6 +40,7 @@ type Invitation struct {
|
||||
Phone string `xorm:"varchar(100)" json:"phone"`
|
||||
|
||||
SignupGroup string `xorm:"varchar(100)" json:"signupGroup"`
|
||||
DefaultCode string `xorm:"varchar(100)" json:"defaultCode"`
|
||||
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
}
|
||||
@ -93,7 +94,45 @@ func GetInvitation(id string) (*Invitation, error) {
|
||||
return getInvitation(owner, name)
|
||||
}
|
||||
|
||||
func UpdateInvitation(id string, invitation *Invitation) (bool, error) {
|
||||
func GetInvitationByCode(code string, organizationName string, lang string) (*Invitation, string) {
|
||||
invitations, err := GetInvitations(organizationName)
|
||||
if err != nil {
|
||||
return nil, err.Error()
|
||||
}
|
||||
errMsg := ""
|
||||
for _, invitation := range invitations {
|
||||
if isValid, msg := invitation.SimpleCheckInvitationCode(code, lang); isValid {
|
||||
return invitation, msg
|
||||
} else if msg != "" && errMsg == "" {
|
||||
errMsg = msg
|
||||
}
|
||||
}
|
||||
|
||||
if errMsg != "" {
|
||||
return nil, errMsg
|
||||
} else {
|
||||
return nil, i18n.Translate(lang, "check:Invitation code is invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func GetMaskedInvitation(invitation *Invitation) *Invitation {
|
||||
if invitation == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
invitation.CreatedTime = ""
|
||||
invitation.UpdatedTime = ""
|
||||
invitation.Code = "***"
|
||||
invitation.DefaultCode = "***"
|
||||
invitation.IsRegexp = false
|
||||
invitation.Quota = -1
|
||||
invitation.UsedCount = -1
|
||||
invitation.SignupGroup = ""
|
||||
|
||||
return invitation
|
||||
}
|
||||
|
||||
func UpdateInvitation(id string, invitation *Invitation, lang string) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if p, err := getInvitation(owner, name); err != nil {
|
||||
return false, err
|
||||
@ -107,6 +146,11 @@ func UpdateInvitation(id string, invitation *Invitation) (bool, error) {
|
||||
invitation.IsRegexp = isRegexp
|
||||
}
|
||||
|
||||
err := CheckInvitationDefaultCode(invitation.Code, invitation.DefaultCode, lang)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(invitation)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@ -115,13 +159,18 @@ func UpdateInvitation(id string, invitation *Invitation) (bool, error) {
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func AddInvitation(invitation *Invitation) (bool, error) {
|
||||
func AddInvitation(invitation *Invitation, lang string) (bool, error) {
|
||||
if isRegexp, err := util.IsRegexp(invitation.Code); err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
invitation.IsRegexp = isRegexp
|
||||
}
|
||||
|
||||
err := CheckInvitationDefaultCode(invitation.Code, invitation.DefaultCode, lang)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.Insert(invitation)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@ -147,7 +196,7 @@ func VerifyInvitation(id string) (payment *Payment, attachInfo map[string]interf
|
||||
return nil, nil, fmt.Errorf("the invitation: %s does not exist", id)
|
||||
}
|
||||
|
||||
func (invitation *Invitation) IsInvitationCodeValid(application *Application, invitationCode string, username string, email string, phone string, lang string) (bool, string) {
|
||||
func (invitation *Invitation) SimpleCheckInvitationCode(invitationCode string, lang string) (bool, string) {
|
||||
if matched, err := util.IsInvitationCodeMatch(invitation.Code, invitationCode); err != nil {
|
||||
return false, err.Error()
|
||||
} else if !matched {
|
||||
@ -160,15 +209,6 @@ func (invitation *Invitation) IsInvitationCodeValid(application *Application, in
|
||||
if invitation.UsedCount >= invitation.Quota {
|
||||
return false, i18n.Translate(lang, "check:Invitation code exhausted")
|
||||
}
|
||||
if application.IsSignupItemRequired("Username") && invitation.Username != "" && invitation.Username != username {
|
||||
return false, i18n.Translate(lang, "check:Please register using the username corresponding to the invitation code")
|
||||
}
|
||||
if application.IsSignupItemRequired("Email") && invitation.Email != "" && invitation.Email != email {
|
||||
return false, i18n.Translate(lang, "check:Please register using the email corresponding to the invitation code")
|
||||
}
|
||||
if application.IsSignupItemRequired("Phone") && invitation.Phone != "" && invitation.Phone != phone {
|
||||
return false, i18n.Translate(lang, "check:Please register using the phone corresponding to the invitation code")
|
||||
}
|
||||
|
||||
// Determine whether the invitation code is in the form of a regular expression other than pure numbers and letters
|
||||
if invitation.IsRegexp {
|
||||
@ -179,3 +219,19 @@ func (invitation *Invitation) IsInvitationCodeValid(application *Application, in
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (invitation *Invitation) IsInvitationCodeValid(application *Application, invitationCode string, username string, email string, phone string, lang string) (bool, string) {
|
||||
if isValid, msg := invitation.SimpleCheckInvitationCode(invitationCode, lang); !isValid {
|
||||
return false, msg
|
||||
}
|
||||
if application.IsSignupItemRequired("Username") && invitation.Username != "" && invitation.Username != username {
|
||||
return false, i18n.Translate(lang, "check:Please register using the username corresponding to the invitation code")
|
||||
}
|
||||
if application.IsSignupItemRequired("Email") && invitation.Email != "" && invitation.Email != email {
|
||||
return false, i18n.Translate(lang, "check:Please register using the email corresponding to the invitation code")
|
||||
}
|
||||
if application.IsSignupItemRequired("Phone") && invitation.Phone != "" && invitation.Phone != phone {
|
||||
return false, i18n.Translate(lang, "check:Please register using the phone corresponding to the invitation code")
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
@ -18,12 +18,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
)
|
||||
|
||||
const MfaRecoveryCodesSession = "mfa_recovery_codes"
|
||||
|
||||
type MfaProps struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
IsPreferred bool `json:"isPreferred"`
|
||||
@ -35,9 +31,9 @@ type MfaProps struct {
|
||||
}
|
||||
|
||||
type MfaInterface interface {
|
||||
Initiate(ctx *context.Context, userId string) (*MfaProps, error)
|
||||
SetupVerify(ctx *context.Context, passcode string) error
|
||||
Enable(ctx *context.Context, user *User) error
|
||||
Initiate(userId string) (*MfaProps, error)
|
||||
SetupVerify(passcode string) error
|
||||
Enable(user *User) error
|
||||
Verify(passcode string) error
|
||||
}
|
||||
|
||||
|
@ -16,90 +16,51 @@ package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
MfaCountryCodeSession = "mfa_country_code"
|
||||
MfaDestSession = "mfa_dest"
|
||||
)
|
||||
|
||||
type SmsMfa struct {
|
||||
Config *MfaProps
|
||||
*MfaProps
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Initiate(ctx *context.Context, userId string) (*MfaProps, error) {
|
||||
recoveryCode := uuid.NewString()
|
||||
|
||||
err := ctx.Input.CruSession.Set(MfaRecoveryCodesSession, []string{recoveryCode})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Initiate(userId string) (*MfaProps, error) {
|
||||
mfaProps := MfaProps{
|
||||
MfaType: mfa.Config.MfaType,
|
||||
RecoveryCodes: []string{recoveryCode},
|
||||
MfaType: mfa.MfaType,
|
||||
}
|
||||
return &mfaProps, nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) SetupVerify(ctx *context.Context, passCode string) error {
|
||||
destSession := ctx.Input.CruSession.Get(MfaDestSession)
|
||||
if destSession == nil {
|
||||
return errors.New("dest session is missing")
|
||||
}
|
||||
dest := destSession.(string)
|
||||
|
||||
if !util.IsEmailValid(dest) {
|
||||
countryCodeSession := ctx.Input.CruSession.Get(MfaCountryCodeSession)
|
||||
if countryCodeSession == nil {
|
||||
return errors.New("country code is missing")
|
||||
}
|
||||
countryCode := countryCodeSession.(string)
|
||||
|
||||
dest, _ = util.GetE164Number(dest, countryCode)
|
||||
func (mfa *SmsMfa) SetupVerify(passCode string) error {
|
||||
if !util.IsEmailValid(mfa.Secret) {
|
||||
mfa.Secret, _ = util.GetE164Number(mfa.Secret, mfa.CountryCode)
|
||||
}
|
||||
|
||||
if result := CheckVerificationCode(dest, passCode, "en"); result.Code != VerificationSuccess {
|
||||
result, err := CheckVerificationCode(mfa.Secret, passCode, "en")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.Code != VerificationSuccess {
|
||||
return errors.New(result.Msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Enable(ctx *context.Context, user *User) error {
|
||||
recoveryCodes := ctx.Input.CruSession.Get(MfaRecoveryCodesSession).([]string)
|
||||
if len(recoveryCodes) == 0 {
|
||||
return fmt.Errorf("recovery codes is missing")
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Enable(user *User) error {
|
||||
columns := []string{"recovery_codes", "preferred_mfa_type"}
|
||||
|
||||
user.RecoveryCodes = append(user.RecoveryCodes, recoveryCodes...)
|
||||
user.RecoveryCodes = append(user.RecoveryCodes, mfa.RecoveryCodes...)
|
||||
if user.PreferredMfaType == "" {
|
||||
user.PreferredMfaType = mfa.Config.MfaType
|
||||
user.PreferredMfaType = mfa.MfaType
|
||||
}
|
||||
|
||||
if mfa.Config.MfaType == SmsType {
|
||||
if mfa.MfaType == SmsType {
|
||||
user.MfaPhoneEnabled = true
|
||||
columns = append(columns, "mfa_phone_enabled")
|
||||
|
||||
if user.Phone == "" {
|
||||
user.Phone = ctx.Input.CruSession.Get(MfaDestSession).(string)
|
||||
user.CountryCode = ctx.Input.CruSession.Get(MfaCountryCodeSession).(string)
|
||||
columns = append(columns, "phone", "country_code")
|
||||
}
|
||||
} else if mfa.Config.MfaType == EmailType {
|
||||
columns = append(columns, "mfa_phone_enabled", "phone", "country_code")
|
||||
} else if mfa.MfaType == EmailType {
|
||||
user.MfaEmailEnabled = true
|
||||
columns = append(columns, "mfa_email_enabled")
|
||||
|
||||
if user.Email == "" {
|
||||
user.Email = ctx.Input.CruSession.Get(MfaDestSession).(string)
|
||||
columns = append(columns, "email")
|
||||
}
|
||||
columns = append(columns, "mfa_email_enabled", "email")
|
||||
}
|
||||
|
||||
_, err := UpdateUser(user.GetId(), user, columns, false)
|
||||
@ -107,20 +68,22 @@ func (mfa *SmsMfa) Enable(ctx *context.Context, user *User) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Input.CruSession.Delete(MfaRecoveryCodesSession)
|
||||
ctx.Input.CruSession.Delete(MfaDestSession)
|
||||
ctx.Input.CruSession.Delete(MfaCountryCodeSession)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Verify(passCode string) error {
|
||||
if !util.IsEmailValid(mfa.Config.Secret) {
|
||||
mfa.Config.Secret, _ = util.GetE164Number(mfa.Config.Secret, mfa.Config.CountryCode)
|
||||
if !util.IsEmailValid(mfa.Secret) {
|
||||
mfa.Secret, _ = util.GetE164Number(mfa.Secret, mfa.CountryCode)
|
||||
}
|
||||
if result := CheckVerificationCode(mfa.Config.Secret, passCode, "en"); result.Code != VerificationSuccess {
|
||||
|
||||
result, err := CheckVerificationCode(mfa.Secret, passCode, "en")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.Code != VerificationSuccess {
|
||||
return errors.New(result.Msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -131,7 +94,7 @@ func NewSmsMfaUtil(config *MfaProps) *SmsMfa {
|
||||
}
|
||||
}
|
||||
return &SmsMfa{
|
||||
Config: config,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,6 +105,6 @@ func NewEmailMfaUtil(config *MfaProps) *SmsMfa {
|
||||
}
|
||||
}
|
||||
return &SmsMfa{
|
||||
Config: config,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
@ -16,28 +16,24 @@ package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
const (
|
||||
MfaTotpSecretSession = "mfa_totp_secret"
|
||||
MfaTotpPeriodInSeconds = 30
|
||||
)
|
||||
|
||||
type TotpMfa struct {
|
||||
Config *MfaProps
|
||||
*MfaProps
|
||||
period uint
|
||||
secretSize uint
|
||||
digits otp.Digits
|
||||
}
|
||||
|
||||
func (mfa *TotpMfa) Initiate(ctx *context.Context, userId string) (*MfaProps, error) {
|
||||
func (mfa *TotpMfa) Initiate(userId string) (*MfaProps, error) {
|
||||
//issuer := beego.AppConfig.String("appname")
|
||||
//if issuer == "" {
|
||||
// issuer = "casdoor"
|
||||
@ -55,33 +51,16 @@ func (mfa *TotpMfa) Initiate(ctx *context.Context, userId string) (*MfaProps, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ctx.Input.CruSession.Set(MfaTotpSecretSession, key.Secret())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
recoveryCode := uuid.NewString()
|
||||
err = ctx.Input.CruSession.Set(MfaRecoveryCodesSession, []string{recoveryCode})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mfaProps := MfaProps{
|
||||
MfaType: mfa.Config.MfaType,
|
||||
RecoveryCodes: []string{recoveryCode},
|
||||
Secret: key.Secret(),
|
||||
URL: key.URL(),
|
||||
MfaType: mfa.MfaType,
|
||||
Secret: key.Secret(),
|
||||
URL: key.URL(),
|
||||
}
|
||||
return &mfaProps, nil
|
||||
}
|
||||
|
||||
func (mfa *TotpMfa) SetupVerify(ctx *context.Context, passcode string) error {
|
||||
secret := ctx.Input.CruSession.Get(MfaTotpSecretSession)
|
||||
if secret == nil {
|
||||
return errors.New("totp secret is missing")
|
||||
}
|
||||
|
||||
result, err := totp.ValidateCustom(passcode, secret.(string), time.Now().UTC(), totp.ValidateOpts{
|
||||
func (mfa *TotpMfa) SetupVerify(passcode string) error {
|
||||
result, err := totp.ValidateCustom(passcode, mfa.Secret, time.Now().UTC(), totp.ValidateOpts{
|
||||
Period: MfaTotpPeriodInSeconds,
|
||||
Skew: 1,
|
||||
Digits: otp.DigitsSix,
|
||||
@ -98,22 +77,13 @@ func (mfa *TotpMfa) SetupVerify(ctx *context.Context, passcode string) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (mfa *TotpMfa) Enable(ctx *context.Context, user *User) error {
|
||||
recoveryCodes := ctx.Input.CruSession.Get(MfaRecoveryCodesSession).([]string)
|
||||
if len(recoveryCodes) == 0 {
|
||||
return fmt.Errorf("recovery codes is missing")
|
||||
}
|
||||
secret := ctx.Input.CruSession.Get(MfaTotpSecretSession).(string)
|
||||
if secret == "" {
|
||||
return fmt.Errorf("totp secret is missing")
|
||||
}
|
||||
|
||||
func (mfa *TotpMfa) Enable(user *User) error {
|
||||
columns := []string{"recovery_codes", "preferred_mfa_type", "totp_secret"}
|
||||
|
||||
user.RecoveryCodes = append(user.RecoveryCodes, recoveryCodes...)
|
||||
user.TotpSecret = secret
|
||||
user.RecoveryCodes = append(user.RecoveryCodes, mfa.RecoveryCodes...)
|
||||
user.TotpSecret = mfa.Secret
|
||||
if user.PreferredMfaType == "" {
|
||||
user.PreferredMfaType = mfa.Config.MfaType
|
||||
user.PreferredMfaType = mfa.MfaType
|
||||
}
|
||||
|
||||
_, err := updateUser(user.GetId(), user, columns)
|
||||
@ -121,14 +91,11 @@ func (mfa *TotpMfa) Enable(ctx *context.Context, user *User) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Input.CruSession.Delete(MfaRecoveryCodesSession)
|
||||
ctx.Input.CruSession.Delete(MfaTotpSecretSession)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *TotpMfa) Verify(passcode string) error {
|
||||
result, err := totp.ValidateCustom(passcode, mfa.Config.Secret, time.Now().UTC(), totp.ValidateOpts{
|
||||
result, err := totp.ValidateCustom(passcode, mfa.Secret, time.Now().UTC(), totp.ValidateOpts{
|
||||
Period: MfaTotpPeriodInSeconds,
|
||||
Skew: 1,
|
||||
Digits: otp.DigitsSix,
|
||||
@ -153,7 +120,7 @@ func NewTotpMfaUtil(config *MfaProps) *TotpMfa {
|
||||
}
|
||||
|
||||
return &TotpMfa{
|
||||
Config: config,
|
||||
MfaProps: config,
|
||||
period: MfaTotpPeriodInSeconds,
|
||||
secretSize: 20,
|
||||
digits: otp.DigitsSix,
|
||||
|
@ -31,6 +31,7 @@ type AccountItem struct {
|
||||
Visible bool `json:"visible"`
|
||||
ViewRule string `json:"viewRule"`
|
||||
ModifyRule string `json:"modifyRule"`
|
||||
Regex string `json:"regex"`
|
||||
}
|
||||
|
||||
type ThemeData struct {
|
||||
@ -53,6 +54,8 @@ type Organization struct {
|
||||
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
|
||||
Logo string `xorm:"varchar(200)" json:"logo"`
|
||||
LogoDark string `xorm:"varchar(200)" json:"logoDark"`
|
||||
Favicon string `xorm:"varchar(100)" json:"favicon"`
|
||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||
@ -342,6 +345,11 @@ func GetDefaultApplication(id string) (*Application, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = extendApplicationWithSigninItems(defaultApplication)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return defaultApplication, nil
|
||||
}
|
||||
|
||||
@ -453,6 +461,16 @@ func organizationChangeTrigger(oldName string, newName string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
record := new(Record)
|
||||
record.Owner = newName
|
||||
record.Organization = newName
|
||||
_, err = session.Where("organization=?", oldName).Update(record)
|
||||
if err != nil {
|
||||
if err.Error() != "no columns found to be updated" {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
resource := new(Resource)
|
||||
resource.Owner = newName
|
||||
_, err = session.Where("owner=?", oldName).Update(resource)
|
||||
|
@ -23,6 +23,8 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
|
||||
|
||||
"github.com/beego/beego"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
@ -30,8 +32,9 @@ import (
|
||||
_ "github.com/denisenkom/go-mssqldb" // db = mssql
|
||||
_ "github.com/go-sql-driver/mysql" // db = mysql
|
||||
_ "github.com/lib/pq" // db = postgres
|
||||
"github.com/xorm-io/core"
|
||||
"github.com/xorm-io/xorm"
|
||||
"github.com/xorm-io/xorm/core"
|
||||
"github.com/xorm-io/xorm/names"
|
||||
_ "modernc.org/sqlite" // db = sqlite
|
||||
)
|
||||
|
||||
@ -96,7 +99,7 @@ func InitAdapter() {
|
||||
}
|
||||
|
||||
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||
tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, tableNamePrefix)
|
||||
tbMapper := names.NewPrefixMapper(names.SnakeMapper{}, tableNamePrefix)
|
||||
ormer.Engine.SetTableMapper(tbMapper)
|
||||
}
|
||||
|
||||
@ -116,6 +119,7 @@ type Ormer struct {
|
||||
driverName string
|
||||
dataSourceName string
|
||||
dbName string
|
||||
Db *sql.DB
|
||||
Engine *xorm.Engine
|
||||
}
|
||||
|
||||
@ -125,6 +129,13 @@ func finalizer(a *Ormer) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if a.Db != nil {
|
||||
err = a.Db.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewAdapter is the constructor for Ormer.
|
||||
@ -146,6 +157,26 @@ func NewAdapter(driverName string, dataSourceName string, dbName string) (*Ormer
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// NewAdapterFromdb is the constructor for Ormer.
|
||||
func NewAdapterFromDb(driverName string, dataSourceName string, dbName string, db *sql.DB) (*Ormer, error) {
|
||||
a := &Ormer{}
|
||||
a.driverName = driverName
|
||||
a.dataSourceName = dataSourceName
|
||||
a.dbName = dbName
|
||||
a.Db = db
|
||||
|
||||
// Open the DB, create it if not existed.
|
||||
err := a.openFromDb(a.Db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Call the destructor when the object is released.
|
||||
runtime.SetFinalizer(a, finalizer)
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func refineDataSourceNameForPostgres(dataSourceName string) string {
|
||||
reg := regexp.MustCompile(`dbname=[^ ]+\s*`)
|
||||
return reg.ReplaceAllString(dataSourceName, "")
|
||||
@ -224,6 +255,30 @@ func (a *Ormer) open() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Ormer) openFromDb(db *sql.DB) error {
|
||||
dataSourceName := a.dataSourceName + a.dbName
|
||||
if a.driverName != "mysql" {
|
||||
dataSourceName = a.dataSourceName
|
||||
}
|
||||
|
||||
xormDb := core.FromDB(db)
|
||||
|
||||
engine, err := xorm.NewEngineWithDB(a.driverName, dataSourceName, xormDb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.driverName == "postgres" {
|
||||
schema := util.GetValueFromDataSourceName("search_path", dataSourceName)
|
||||
if schema != "" {
|
||||
engine.SetSchema(schema)
|
||||
}
|
||||
}
|
||||
|
||||
a.Engine = engine
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Ormer) close() {
|
||||
_ = a.Engine.Close()
|
||||
a.Engine = nil
|
||||
@ -333,11 +388,21 @@ func (a *Ormer) createTable() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Transaction))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Syncer))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(casvisorsdk.Record))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Webhook))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -312,8 +312,6 @@ func GetPaymentProvider(p *Provider) (pp.PaymentProvider, error) {
|
||||
} else {
|
||||
return nil, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *Provider) GetId() string {
|
||||
|
@ -47,6 +47,12 @@ func NewRecord(ctx *context.Context) *casvisorsdk.Record {
|
||||
object = string(ctx.Input.RequestBody)
|
||||
}
|
||||
|
||||
language := ctx.Request.Header.Get("Accept-Language")
|
||||
if len(language) > 2 {
|
||||
language = language[0:2]
|
||||
}
|
||||
languageCode := conf.GetLanguage(language)
|
||||
|
||||
record := casvisorsdk.Record{
|
||||
Name: util.GenerateId(),
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
@ -55,7 +61,9 @@ func NewRecord(ctx *context.Context) *casvisorsdk.Record {
|
||||
Method: ctx.Request.Method,
|
||||
RequestUri: requestUri,
|
||||
Action: action,
|
||||
Language: languageCode,
|
||||
Object: object,
|
||||
Response: "",
|
||||
IsTriggered: false,
|
||||
}
|
||||
return &record
|
||||
@ -82,7 +90,12 @@ func AddRecord(record *casvisorsdk.Record) bool {
|
||||
}
|
||||
|
||||
if casvisorsdk.GetClient() == nil {
|
||||
return false
|
||||
affected, err := ormer.Engine.Insert(record)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
affected, err := casvisorsdk.AddRecord(record)
|
||||
@ -93,13 +106,55 @@ func AddRecord(record *casvisorsdk.Record) bool {
|
||||
return affected
|
||||
}
|
||||
|
||||
func getFilteredWebhooks(webhooks []*Webhook, action string) []*Webhook {
|
||||
func GetRecordCount(field, value string, filterRecord *casvisorsdk.Record) (int64, error) {
|
||||
session := GetSession("", -1, -1, field, value, "", "")
|
||||
return session.Count(filterRecord)
|
||||
}
|
||||
|
||||
func GetRecords() ([]*casvisorsdk.Record, error) {
|
||||
records := []*casvisorsdk.Record{}
|
||||
err := ormer.Engine.Desc("id").Find(&records)
|
||||
if err != nil {
|
||||
return records, err
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func GetPaginationRecords(offset, limit int, field, value, sortField, sortOrder string, filterRecord *casvisorsdk.Record) ([]*casvisorsdk.Record, error) {
|
||||
records := []*casvisorsdk.Record{}
|
||||
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&records, filterRecord)
|
||||
if err != nil {
|
||||
return records, err
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func GetRecordsByField(record *casvisorsdk.Record) ([]*casvisorsdk.Record, error) {
|
||||
records := []*casvisorsdk.Record{}
|
||||
err := ormer.Engine.Find(&records, record)
|
||||
if err != nil {
|
||||
return records, err
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func getFilteredWebhooks(webhooks []*Webhook, organization string, action string) []*Webhook {
|
||||
res := []*Webhook{}
|
||||
for _, webhook := range webhooks {
|
||||
if !webhook.IsEnabled {
|
||||
continue
|
||||
}
|
||||
|
||||
if webhook.SingleOrgOnly {
|
||||
if webhook.Organization != organization {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
matched := false
|
||||
for _, event := range webhook.Events {
|
||||
if action == event {
|
||||
@ -116,13 +171,13 @@ func getFilteredWebhooks(webhooks []*Webhook, action string) []*Webhook {
|
||||
}
|
||||
|
||||
func SendWebhooks(record *casvisorsdk.Record) error {
|
||||
webhooks, err := getWebhooksByOrganization(record.Organization)
|
||||
webhooks, err := getWebhooksByOrganization("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
webhooks = getFilteredWebhooks(webhooks, record.Action)
|
||||
webhooks = getFilteredWebhooks(webhooks, record.Organization, record.Action)
|
||||
for _, webhook := range webhooks {
|
||||
var user *User
|
||||
if webhook.IsUserExtended {
|
||||
|
@ -27,7 +27,7 @@ func getCasvisorApplication() *Application {
|
||||
}
|
||||
|
||||
for _, application := range applications {
|
||||
if strings.Contains(strings.ToLower(application.Name), "casvisor") {
|
||||
if strings.Contains(strings.ToLower(application.Name), "casvisor-my") {
|
||||
return application
|
||||
}
|
||||
}
|
||||
|
@ -179,8 +179,8 @@ type IdpSSODescriptor struct {
|
||||
}
|
||||
|
||||
type NameIDFormat struct {
|
||||
XMLName xml.Name
|
||||
Value string `xml:",innerxml"`
|
||||
// XMLName xml.Name
|
||||
Value string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
type SingleSignOnService struct {
|
||||
@ -190,7 +190,7 @@ type SingleSignOnService struct {
|
||||
}
|
||||
|
||||
type Attribute struct {
|
||||
XMLName xml.Name
|
||||
// XMLName xml.Name
|
||||
Name string `xml:"Name,attr"`
|
||||
NameFormat string `xml:"NameFormat,attr"`
|
||||
FriendlyName string `xml:"FriendlyName,attr"`
|
||||
@ -198,7 +198,7 @@ type Attribute struct {
|
||||
Values []string `xml:"AttributeValue"`
|
||||
}
|
||||
|
||||
func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, error) {
|
||||
func GetSamlMeta(application *Application, host string, enablePostBinding bool) (*IdpEntityDescriptor, error) {
|
||||
cert, err := getCertByApplication(application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -217,6 +217,13 @@ func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, e
|
||||
|
||||
originFrontend, originBackend := getOriginFromHost(host)
|
||||
|
||||
idpLocation := ""
|
||||
if enablePostBinding {
|
||||
idpLocation = fmt.Sprintf("%s/api/saml/redirect/%s/%s", originBackend, application.Owner, application.Name)
|
||||
} else {
|
||||
idpLocation = fmt.Sprintf("%s/login/saml/authorize/%s/%s", originFrontend, application.Owner, application.Name)
|
||||
}
|
||||
|
||||
d := IdpEntityDescriptor{
|
||||
XMLName: xml.Name{
|
||||
Local: "md:EntityDescriptor",
|
||||
@ -248,7 +255,7 @@ func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, e
|
||||
},
|
||||
SingleSignOnService: SingleSignOnService{
|
||||
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
|
||||
Location: fmt.Sprintf("%s/login/saml/authorize/%s/%s", originFrontend, application.Owner, application.Name),
|
||||
Location: idpLocation,
|
||||
},
|
||||
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
|
||||
},
|
||||
@ -266,7 +273,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
// base64 decode
|
||||
defated, err := base64.StdEncoding.DecodeString(samlRequest)
|
||||
if err != nil {
|
||||
return "", "", method, fmt.Errorf("err: Failed to decode SAML request , %s", err.Error())
|
||||
return "", "", "", fmt.Errorf("err: Failed to decode SAML request, %s", err.Error())
|
||||
}
|
||||
|
||||
// decompress
|
||||
@ -274,7 +281,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
rdr := flate.NewReader(bytes.NewReader(defated))
|
||||
|
||||
for {
|
||||
_, err := io.CopyN(&buffer, rdr, 1024)
|
||||
_, err = io.CopyN(&buffer, rdr, 1024)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
@ -286,12 +293,12 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
var authnRequest saml.AuthNRequest
|
||||
err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
|
||||
if err != nil {
|
||||
return "", "", method, fmt.Errorf("err: Failed to unmarshal AuthnRequest, please check the SAML request. %s", err.Error())
|
||||
return "", "", "", fmt.Errorf("err: Failed to unmarshal AuthnRequest, please check the SAML request, %s", err.Error())
|
||||
}
|
||||
|
||||
// verify samlRequest
|
||||
if isValid := application.IsRedirectUriValid(authnRequest.Issuer); !isValid {
|
||||
return "", "", method, fmt.Errorf("err: Issuer URI: %s doesn't exist in the allowed Redirect URI list", authnRequest.Issuer)
|
||||
return "", "", "", fmt.Errorf("err: Issuer URI: %s doesn't exist in the allowed Redirect URI list", authnRequest.Issuer)
|
||||
}
|
||||
|
||||
// get certificate string
|
||||
@ -316,8 +323,13 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
}
|
||||
|
||||
_, originBackend := getOriginFromHost(host)
|
||||
|
||||
// build signedResponse
|
||||
samlResponse, _ := NewSamlResponse(application, user, originBackend, certificate, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer, authnRequest.ID, application.RedirectUris)
|
||||
samlResponse, err := NewSamlResponse(application, user, originBackend, certificate, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer, authnRequest.ID, application.RedirectUris)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("err: NewSamlResponse() error, %s", err.Error())
|
||||
}
|
||||
|
||||
randomKeyStore := &X509Key{
|
||||
PrivateKey: cert.PrivateKey,
|
||||
X509Certificate: certificate,
|
||||
@ -329,18 +341,23 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
ctx.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList("")
|
||||
}
|
||||
|
||||
//signedXML, err := ctx.SignEnvelopedLimix(samlResponse)
|
||||
//if err != nil {
|
||||
// signedXML, err := ctx.SignEnvelopedLimix(samlResponse)
|
||||
// if err != nil {
|
||||
// return "", "", fmt.Errorf("err: %s", err.Error())
|
||||
//}
|
||||
// }
|
||||
|
||||
sig, err := ctx.ConstructSignature(samlResponse, true)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("err: Failed to serializes the SAML request into bytes, %s", err.Error())
|
||||
}
|
||||
|
||||
samlResponse.InsertChildAt(1, sig)
|
||||
|
||||
doc := etree.NewDocument()
|
||||
doc.SetRoot(samlResponse)
|
||||
xmlBytes, err := doc.WriteToBytes()
|
||||
if err != nil {
|
||||
return "", "", method, fmt.Errorf("err: Failed to serializes the SAML request into bytes, %s", err.Error())
|
||||
return "", "", "", fmt.Errorf("err: Failed to serializes the SAML request into bytes, %s", err.Error())
|
||||
}
|
||||
|
||||
// compress
|
||||
@ -348,16 +365,19 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
flated := bytes.NewBuffer(nil)
|
||||
writer, err := flate.NewWriter(flated, flate.DefaultCompression)
|
||||
if err != nil {
|
||||
return "", "", method, err
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
_, err = writer.Write(xmlBytes)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
xmlBytes = flated.Bytes()
|
||||
}
|
||||
// base64 encode
|
||||
@ -366,12 +386,12 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
}
|
||||
|
||||
// NewSamlResponse11 return a saml1.1 response(not 2.0)
|
||||
func NewSamlResponse11(user *User, requestID string, host string) *etree.Element {
|
||||
func NewSamlResponse11(user *User, requestID string, host string) (*etree.Element, error) {
|
||||
samlResponse := &etree.Element{
|
||||
Space: "samlp",
|
||||
Tag: "Response",
|
||||
}
|
||||
// create samlresponse
|
||||
|
||||
samlResponse.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:1.0:protocol")
|
||||
samlResponse.CreateAttr("MajorVersion", "1")
|
||||
samlResponse.CreateAttr("MinorVersion", "1")
|
||||
@ -424,11 +444,15 @@ func NewSamlResponse11(user *User, requestID string, host string) *etree.Element
|
||||
subjectConfirmationInAttribute := subjectInAttribute.CreateElement("saml:SubjectConfirmation")
|
||||
subjectConfirmationInAttribute.CreateElement("saml:ConfirmationMethod").SetText("urn:oasis:names:tc:SAML:1.0:cm:artifact")
|
||||
|
||||
data, _ := json.Marshal(user)
|
||||
tmp := map[string]string{}
|
||||
err := json.Unmarshal(data, &tmp)
|
||||
data, err := json.Marshal(user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tmp := map[string]string{}
|
||||
err = json.Unmarshal(data, &tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range tmp {
|
||||
@ -440,5 +464,10 @@ func NewSamlResponse11(user *User, requestID string, host string) *etree.Element
|
||||
}
|
||||
}
|
||||
|
||||
return samlResponse
|
||||
return samlResponse, nil
|
||||
}
|
||||
|
||||
func GetSamlRedirectAddress(owner string, application string, relayState string, samlRequest string, host string) string {
|
||||
originF, _ := getOriginFromHost(host)
|
||||
return fmt.Sprintf("%s/login/saml/authorize/%s/%s?relayState=%s&samlRequest=%s", originF, owner, application, relayState, samlRequest)
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func getSmsClient(provider *Provider) (sender.SmsClient, error) {
|
||||
if provider.Type == sender.HuaweiCloud || provider.Type == sender.AzureACS {
|
||||
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.ProviderUrl, provider.AppId)
|
||||
} else if provider.Type == "Custom HTTP SMS" {
|
||||
client, err = newHttpSmsClient(provider.Endpoint, provider.Method, provider.Title)
|
||||
client, err = newHttpSmsClient(provider.Endpoint, provider.Method, provider.Title, provider.TemplateCode)
|
||||
} else {
|
||||
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
|
||||
}
|
||||
|
@ -27,20 +27,26 @@ type HttpSmsClient struct {
|
||||
endpoint string
|
||||
method string
|
||||
paramName string
|
||||
template string
|
||||
}
|
||||
|
||||
func newHttpSmsClient(endpoint string, method string, paramName string) (*HttpSmsClient, error) {
|
||||
func newHttpSmsClient(endpoint, method, paramName, template string) (*HttpSmsClient, error) {
|
||||
if template == "" {
|
||||
template = "%s"
|
||||
}
|
||||
client := &HttpSmsClient{
|
||||
endpoint: endpoint,
|
||||
method: method,
|
||||
paramName: paramName,
|
||||
template: template,
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (c *HttpSmsClient) SendMessage(param map[string]string, targetPhoneNumber ...string) error {
|
||||
phoneNumber := targetPhoneNumber[0]
|
||||
content := param["code"]
|
||||
code := param["code"]
|
||||
content := fmt.Sprintf(c.template, code)
|
||||
|
||||
var req *http.Request
|
||||
var err error
|
||||
|
@ -109,14 +109,17 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
|
||||
|
||||
func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) {
|
||||
endpoint := getProviderEndpoint(provider)
|
||||
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
|
||||
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if storageProvider == nil {
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
|
||||
}
|
||||
|
||||
if provider.Domain == "" {
|
||||
provider.Domain = storageProvider.GetEndpoint()
|
||||
_, err := UpdateProvider(provider.GetId(), provider)
|
||||
_, err = UpdateProvider(provider.GetId(), provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -39,11 +39,17 @@ type Syncer struct {
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
|
||||
SslMode string `xorm:"varchar(100)" json:"sslMode"`
|
||||
SshType string `xorm:"varchar(100)" json:"sshType"`
|
||||
|
||||
Host string `xorm:"varchar(100)" json:"host"`
|
||||
Port int `json:"port"`
|
||||
User string `xorm:"varchar(100)" json:"user"`
|
||||
Password string `xorm:"varchar(100)" json:"password"`
|
||||
Password string `xorm:"varchar(150)" json:"password"`
|
||||
SshHost string `xorm:"varchar(100)" json:"sshHost"`
|
||||
SshPort int `json:"sshPort"`
|
||||
SshUser string `xorm:"varchar(100)" json:"sshUser"`
|
||||
SshPassword string `xorm:"varchar(150)" json:"sshPassword"`
|
||||
Cert string `xorm:"varchar(100)" json:"cert"`
|
||||
Database string `xorm:"varchar(100)" json:"database"`
|
||||
Table string `xorm:"varchar(100)" json:"table"`
|
||||
TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"`
|
||||
@ -279,3 +285,25 @@ func RunSyncer(syncer *Syncer) error {
|
||||
|
||||
return syncer.syncUsers()
|
||||
}
|
||||
|
||||
func TestSyncerDb(syncer Syncer) error {
|
||||
oldSyncer, err := getSyncer(syncer.Owner, syncer.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if syncer.Password == "***" {
|
||||
syncer.Password = oldSyncer.Password
|
||||
}
|
||||
|
||||
err = syncer.initAdapter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = syncer.Ormer.Engine.Ping()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -15,12 +15,17 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
type OriginalUser = User
|
||||
@ -124,6 +129,19 @@ func (syncer *Syncer) calculateHash(user *OriginalUser) string {
|
||||
return util.GetMd5Hash(s)
|
||||
}
|
||||
|
||||
type dsnConnector struct {
|
||||
dsn string
|
||||
driver driver.Driver
|
||||
}
|
||||
|
||||
func (t dsnConnector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
return t.driver.Open(t.dsn)
|
||||
}
|
||||
|
||||
func (t dsnConnector) Driver() driver.Driver {
|
||||
return t.driver
|
||||
}
|
||||
|
||||
func (syncer *Syncer) initAdapter() error {
|
||||
if syncer.Ormer != nil {
|
||||
return nil
|
||||
@ -142,12 +160,38 @@ func (syncer *Syncer) initAdapter() error {
|
||||
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", syncer.User, syncer.Password, syncer.Host, syncer.Port)
|
||||
}
|
||||
|
||||
var db *sql.DB
|
||||
var err error
|
||||
|
||||
if syncer.SshType != "" && (syncer.DatabaseType == "mysql" || syncer.DatabaseType == "postgres" || syncer.DatabaseType == "mssql") {
|
||||
var dial *ssh.Client
|
||||
if syncer.SshType == "password" {
|
||||
dial, err = DialWithPassword(syncer.SshUser, syncer.SshPassword, syncer.SshHost, syncer.SshPort)
|
||||
} else {
|
||||
dial, err = DialWithCert(syncer.SshUser, syncer.Owner+"/"+syncer.Cert, syncer.SshHost, syncer.SshPort)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if syncer.DatabaseType == "mysql" {
|
||||
dataSourceName = fmt.Sprintf("%s:%s@%s(%s:%d)/", syncer.User, syncer.Password, syncer.Owner+syncer.Name, syncer.Host, syncer.Port)
|
||||
mysql.RegisterDialContext(syncer.Owner+syncer.Name, (&ViaSSHDialer{Client: dial, Context: nil}).MysqlDial)
|
||||
} else if syncer.DatabaseType == "postgres" || syncer.DatabaseType == "mssql" {
|
||||
db = sql.OpenDB(dsnConnector{dsn: dataSourceName, driver: &ViaSSHDialer{Client: dial, Context: nil, DatabaseType: syncer.DatabaseType}})
|
||||
}
|
||||
}
|
||||
|
||||
if !isCloudIntranet {
|
||||
dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.")
|
||||
}
|
||||
|
||||
var err error
|
||||
syncer.Ormer, err = NewAdapter(syncer.DatabaseType, dataSourceName, syncer.Database)
|
||||
if db != nil {
|
||||
syncer.Ormer, err = NewAdapterFromDb(syncer.DatabaseType, dataSourceName, syncer.Database, db)
|
||||
} else {
|
||||
syncer.Ormer, err = NewAdapter(syncer.DatabaseType, dataSourceName, syncer.Database)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,8 @@ func (syncer *Syncer) setUserByKeyValue(user *User, key string, value string) {
|
||||
user.CreatedTime = value
|
||||
case "UpdatedTime":
|
||||
user.UpdatedTime = value
|
||||
case "DeletedTime":
|
||||
user.DeletedTime = value
|
||||
case "Id":
|
||||
user.Id = value
|
||||
case "Type":
|
||||
@ -266,6 +268,7 @@ func (syncer *Syncer) getMapFromOriginalUser(user *OriginalUser) map[string]stri
|
||||
m["Name"] = user.Name
|
||||
m["CreatedTime"] = user.CreatedTime
|
||||
m["UpdatedTime"] = user.UpdatedTime
|
||||
m["DeletedTime"] = user.DeletedTime
|
||||
m["Id"] = user.Id
|
||||
m["Type"] = user.Type
|
||||
m["Password"] = user.Password
|
||||
|
742
object/token.go
742
object/token.go
@ -16,33 +16,13 @@ package object
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
"github.com/casdoor/casdoor/idp"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
|
||||
const (
|
||||
hourSeconds = int(time.Hour / time.Second)
|
||||
InvalidRequest = "invalid_request"
|
||||
InvalidClient = "invalid_client"
|
||||
InvalidGrant = "invalid_grant"
|
||||
UnauthorizedClient = "unauthorized_client"
|
||||
UnsupportedGrantType = "unsupported_grant_type"
|
||||
InvalidScope = "invalid_scope"
|
||||
EndpointError = "endpoint_error"
|
||||
)
|
||||
|
||||
type Code struct {
|
||||
Message string `xorm:"varchar(100)" json:"message"`
|
||||
Code string `xorm:"varchar(100)" json:"code"`
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
@ -65,35 +45,6 @@ type Token struct {
|
||||
CodeExpireIn int64 `json:"codeExpireIn"`
|
||||
}
|
||||
|
||||
type TokenWrapper struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
IdToken string `json:"id_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
type TokenError struct {
|
||||
Error string `json:"error"`
|
||||
ErrorDescription string `json:"error_description,omitempty"`
|
||||
}
|
||||
|
||||
type IntrospectionResponse struct {
|
||||
Active bool `json:"active"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
ClientId string `json:"client_id,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
TokenType string `json:"token_type,omitempty"`
|
||||
Exp int64 `json:"exp,omitempty"`
|
||||
Iat int64 `json:"iat,omitempty"`
|
||||
Nbf int64 `json:"nbf,omitempty"`
|
||||
Sub string `json:"sub,omitempty"`
|
||||
Aud []string `json:"aud,omitempty"`
|
||||
Iss string `json:"iss,omitempty"`
|
||||
Jti string `json:"jti,omitempty"`
|
||||
}
|
||||
|
||||
func GetTokenCount(owner, organization, field, value string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
return session.Count(&Token{Organization: organization})
|
||||
@ -186,6 +137,29 @@ func GetTokenByRefreshToken(refreshToken string) (*Token, error) {
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
func GetTokenByTokenValue(tokenValue, tokenTypeHint string) (*Token, error) {
|
||||
switch tokenTypeHint {
|
||||
case "access_token":
|
||||
token, err := GetTokenByAccessToken(tokenValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token != nil {
|
||||
return token, nil
|
||||
}
|
||||
case "refresh_token":
|
||||
token, err := GetTokenByRefreshToken(tokenValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if token != nil {
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func updateUsedByCode(token *Token) bool {
|
||||
affected, err := ormer.Engine.Where("code=?", token.Code).Cols("code_is_used").Update(token)
|
||||
if err != nil {
|
||||
@ -259,673 +233,3 @@ func DeleteToken(token *Token) (bool, error) {
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token, error) {
|
||||
token, err := GetTokenByAccessToken(accessToken)
|
||||
if err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
if token == nil {
|
||||
return false, nil, nil, nil
|
||||
}
|
||||
|
||||
token.ExpiresIn = 0
|
||||
affected, err := ormer.Engine.ID(core.PK{token.Owner, token.Name}).Cols("expires_in").Update(token)
|
||||
if err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
|
||||
application, err := getApplication(token.Owner, token.Application)
|
||||
if err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
|
||||
return affected != 0, application, token, nil
|
||||
}
|
||||
|
||||
func GetTokenByTokenAndApplication(token string, application string) (*Token, error) {
|
||||
tokenResult := Token{}
|
||||
existed, err := ormer.Engine.Where("(refresh_token = ? or access_token = ? ) and application = ?", token, token, application).Get(&tokenResult)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !existed {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &tokenResult, nil
|
||||
}
|
||||
|
||||
func CheckOAuthLogin(clientId string, responseType string, redirectUri string, scope string, state string, lang string) (string, *Application, error) {
|
||||
if responseType != "code" && responseType != "token" && responseType != "id_token" {
|
||||
return fmt.Sprintf(i18n.Translate(lang, "token:Grant_type: %s is not supported in this application"), responseType), nil, nil
|
||||
}
|
||||
|
||||
application, err := GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
return i18n.Translate(lang, "token:Invalid client_id"), nil, nil
|
||||
}
|
||||
|
||||
if !application.IsRedirectUriValid(redirectUri) {
|
||||
return fmt.Sprintf(i18n.Translate(lang, "token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri), application, nil
|
||||
}
|
||||
|
||||
// Mask application for /api/get-app-login
|
||||
application.ClientSecret = ""
|
||||
return "", application, nil
|
||||
}
|
||||
|
||||
func GetOAuthCode(userId string, clientId string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string, lang string) (*Code, error) {
|
||||
user, err := GetUser(userId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return &Code{
|
||||
Message: fmt.Sprintf("general:The user: %s doesn't exist", userId),
|
||||
Code: "",
|
||||
}, nil
|
||||
}
|
||||
if user.IsForbidden {
|
||||
return &Code{
|
||||
Message: "error: the user is forbidden to sign in, please contact the administrator",
|
||||
Code: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
msg, application, err := CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, lang)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if msg != "" {
|
||||
return &Code{
|
||||
Message: msg,
|
||||
Code: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, nonce, scope, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if challenge == "null" {
|
||||
challenge = ""
|
||||
}
|
||||
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Code: util.GenerateClientId(),
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: scope,
|
||||
TokenType: "Bearer",
|
||||
CodeChallenge: challenge,
|
||||
CodeIsUsed: false,
|
||||
CodeExpireIn: time.Now().Add(time.Minute * 5).Unix(),
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Code{
|
||||
Message: "",
|
||||
Code: token.Code,
|
||||
}, nil
|
||||
}
|
||||
|
||||
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{}, error) {
|
||||
application, err := GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
return &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_id is invalid",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Check if grantType is allowed in the current application
|
||||
|
||||
if !IsGrantTypeValid(grantType, application.GrantTypes) && tag == "" {
|
||||
return &TokenError{
|
||||
Error: UnsupportedGrantType,
|
||||
ErrorDescription: fmt.Sprintf("grant_type: %s is not supported in this application", grantType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var token *Token
|
||||
var tokenError *TokenError
|
||||
switch grantType {
|
||||
case "authorization_code": // Authorization Code Grant
|
||||
token, tokenError, err = GetAuthorizationCodeToken(application, clientSecret, code, verifier)
|
||||
case "password": // Resource Owner Password Credentials Grant
|
||||
token, tokenError, err = GetPasswordToken(application, username, password, scope, host)
|
||||
case "client_credentials": // Client Credentials Grant
|
||||
token, tokenError, err = GetClientCredentialsToken(application, clientSecret, scope, host)
|
||||
case "refresh_token":
|
||||
refreshToken2, err := RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return refreshToken2, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tag == "wechat_miniprogram" {
|
||||
// Wechat Mini Program
|
||||
token, tokenError, err = GetWechatMiniProgramToken(application, code, host, username, avatar, lang)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tokenError != nil {
|
||||
return tokenError, nil
|
||||
}
|
||||
|
||||
token.CodeIsUsed = true
|
||||
|
||||
go updateUsedByCode(token)
|
||||
|
||||
tokenWrapper := &TokenWrapper{
|
||||
AccessToken: token.AccessToken,
|
||||
IdToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
TokenType: token.TokenType,
|
||||
ExpiresIn: token.ExpiresIn,
|
||||
Scope: token.Scope,
|
||||
}
|
||||
|
||||
return tokenWrapper, nil
|
||||
}
|
||||
|
||||
func RefreshToken(grantType string, refreshToken string, scope string, clientId string, clientSecret string, host string) (interface{}, error) {
|
||||
// check parameters
|
||||
if grantType != "refresh_token" {
|
||||
return &TokenError{
|
||||
Error: UnsupportedGrantType,
|
||||
ErrorDescription: "grant_type should be refresh_token",
|
||||
}, nil
|
||||
}
|
||||
application, err := GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
return &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_id is invalid",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if clientSecret != "" && application.ClientSecret != clientSecret {
|
||||
return &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// check whether the refresh token is valid, and has not expired.
|
||||
token, err := GetTokenByRefreshToken(refreshToken)
|
||||
if err != nil || token == nil {
|
||||
return &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "refresh token is invalid, expired or revoked",
|
||||
}, nil
|
||||
}
|
||||
|
||||
cert, err := getCertByApplication(application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cert == nil {
|
||||
return &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("cert: %s cannot be found", application.Cert),
|
||||
}, nil
|
||||
}
|
||||
|
||||
_, err = ParseJwtToken(refreshToken, cert)
|
||||
if err != nil {
|
||||
return &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generate a new token
|
||||
user, err := getUser(application.Organization, token.User)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsForbidden {
|
||||
return &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the user is forbidden to sign in, please contact the administrator",
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newAccessToken, newRefreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
||||
if err != nil {
|
||||
return &TokenError{
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
newToken := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Code: util.GenerateClientId(),
|
||||
AccessToken: newAccessToken,
|
||||
RefreshToken: newRefreshToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: scope,
|
||||
TokenType: "Bearer",
|
||||
}
|
||||
_, err = AddToken(newToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = DeleteToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenWrapper := &TokenWrapper{
|
||||
AccessToken: newToken.AccessToken,
|
||||
IdToken: newToken.AccessToken,
|
||||
RefreshToken: newToken.RefreshToken,
|
||||
TokenType: newToken.TokenType,
|
||||
ExpiresIn: newToken.ExpiresIn,
|
||||
Scope: newToken.Scope,
|
||||
}
|
||||
return tokenWrapper, nil
|
||||
}
|
||||
|
||||
// PkceChallenge: base64-URL-encoded SHA256 hash of verifier, per rfc 7636
|
||||
func pkceChallenge(verifier string) string {
|
||||
sum := sha256.Sum256([]byte(verifier))
|
||||
challenge := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(sum[:])
|
||||
return challenge
|
||||
}
|
||||
|
||||
// IsGrantTypeValid
|
||||
// Check if grantType is allowed in the current application
|
||||
// authorization_code is allowed by default
|
||||
func IsGrantTypeValid(method string, grantTypes []string) bool {
|
||||
if method == "authorization_code" {
|
||||
return true
|
||||
}
|
||||
for _, m := range grantTypes {
|
||||
if m == method {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetAuthorizationCodeToken
|
||||
// Authorization code flow
|
||||
func GetAuthorizationCodeToken(application *Application, clientSecret string, code string, verifier string) (*Token, *TokenError, error) {
|
||||
if code == "" {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidRequest,
|
||||
ErrorDescription: "authorization code should not be empty",
|
||||
}, nil
|
||||
}
|
||||
|
||||
token, err := getTokenByCode(code)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if token == nil {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "authorization code is invalid",
|
||||
}, nil
|
||||
}
|
||||
if token.CodeIsUsed {
|
||||
// anti replay attacks
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "authorization code has been used",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if token.CodeChallenge != "" && pkceChallenge(verifier) != token.CodeChallenge {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "verifier is invalid",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if application.ClientSecret != clientSecret {
|
||||
// when using PKCE, the Client Secret can be empty,
|
||||
// but if it is provided, it must be accurate.
|
||||
if token.CodeChallenge == "" {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}, nil
|
||||
} else {
|
||||
if clientSecret != "" {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if application.Name != token.Application {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the token is for wrong application (client_id)",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if time.Now().Unix() > token.CodeExpireIn {
|
||||
// code must be used within 5 minutes
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "authorization code has expired",
|
||||
}, nil
|
||||
}
|
||||
return token, nil, nil
|
||||
}
|
||||
|
||||
// GetPasswordToken
|
||||
// Resource Owner Password Credentials flow
|
||||
func GetPasswordToken(application *Application, username string, password string, scope string, host string) (*Token, *TokenError, error) {
|
||||
user, err := getUser(application.Organization, username)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if user == nil {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the user does not exist",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if user.Ldap != "" {
|
||||
err = checkLdapUserPassword(user, password, "en")
|
||||
} else {
|
||||
err = CheckPassword(user, password, "en")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("invalid username or password: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if user.IsForbidden {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the user is forbidden to sign in, please contact the administrator",
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Code: util.GenerateClientId(),
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: scope,
|
||||
TokenType: "Bearer",
|
||||
CodeIsUsed: true,
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return token, nil, nil
|
||||
}
|
||||
|
||||
// GetClientCredentialsToken
|
||||
// Client Credentials flow
|
||||
func GetClientCredentialsToken(application *Application, clientSecret string, scope string, host string) (*Token, *TokenError, error) {
|
||||
if application.ClientSecret != clientSecret {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}, nil
|
||||
}
|
||||
nullUser := &User{
|
||||
Owner: application.Owner,
|
||||
Id: application.GetId(),
|
||||
Name: application.Name,
|
||||
Type: "application",
|
||||
}
|
||||
|
||||
accessToken, _, tokenName, err := generateJwtToken(application, nullUser, "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: application.Organization,
|
||||
User: nullUser.Name,
|
||||
Code: util.GenerateClientId(),
|
||||
AccessToken: accessToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: scope,
|
||||
TokenType: "Bearer",
|
||||
CodeIsUsed: true,
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return token, nil, nil
|
||||
}
|
||||
|
||||
// GetTokenByUser
|
||||
// Implicit flow
|
||||
func GetTokenByUser(application *Application, user *User, scope string, nonce string, host string) (*Token, error) {
|
||||
err := ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, nonce, scope, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Code: util.GenerateClientId(),
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: scope,
|
||||
TokenType: "Bearer",
|
||||
CodeIsUsed: true,
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetWechatMiniProgramToken
|
||||
// Wechat Mini Program flow
|
||||
func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string, lang string) (*Token, *TokenError, error) {
|
||||
mpProvider := GetWechatMiniProgramProvider(application)
|
||||
if mpProvider == nil {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "the application does not support wechat mini program",
|
||||
}, nil
|
||||
}
|
||||
provider, err := GetProvider(util.GetId("admin", mpProvider.Name))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
mpIdp := idp.NewWeChatMiniProgramIdProvider(provider.ClientId, provider.ClientSecret)
|
||||
session, err := mpIdp.GetSessionByCode(code)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("get wechat mini program session error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
openId, unionId := session.Openid, session.Unionid
|
||||
if openId == "" && unionId == "" {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidRequest,
|
||||
ErrorDescription: "the wechat mini program session is invalid",
|
||||
}, nil
|
||||
}
|
||||
user, err := getUserByWechatId(application.Organization, openId, unionId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
if !application.EnableSignUp {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the application does not allow to sign up new account",
|
||||
}, nil
|
||||
}
|
||||
// Add new user
|
||||
var name string
|
||||
if CheckUsername(username, lang) == "" {
|
||||
name = username
|
||||
} else {
|
||||
name = fmt.Sprintf("wechat-%s", openId)
|
||||
}
|
||||
|
||||
user = &User{
|
||||
Owner: application.Organization,
|
||||
Id: util.GenerateId(),
|
||||
Name: name,
|
||||
Avatar: avatar,
|
||||
SignupApplication: application.Name,
|
||||
WeChat: openId,
|
||||
Type: "normal-user",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
IsAdmin: false,
|
||||
IsForbidden: false,
|
||||
IsDeleted: false,
|
||||
Properties: map[string]string{
|
||||
UserPropertiesWechatOpenId: openId,
|
||||
UserPropertiesWechatUnionId: unionId,
|
||||
},
|
||||
}
|
||||
_, err = AddUser(user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Code: session.SessionKey, // a trick, because miniprogram does not use the code, so use the code field to save the session_key
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: application.ExpireInHours * 60,
|
||||
Scope: "",
|
||||
TokenType: "Bearer",
|
||||
CodeIsUsed: true,
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return token, nil, nil
|
||||
}
|
||||
|
@ -256,12 +256,12 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
|
||||
|
||||
ticket := request.AssertionArtifact.InnerXML
|
||||
if ticket == "" {
|
||||
return "", "", fmt.Errorf("samlp:AssertionArtifact field not found")
|
||||
return "", "", fmt.Errorf("request.AssertionArtifact.InnerXML error, AssertionArtifact field not found")
|
||||
}
|
||||
|
||||
ok, _, service, userId := GetCasTokenByTicket(ticket)
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("ticket %s found", ticket)
|
||||
return "", "", fmt.Errorf("the CAS token for ticket %s is not found", ticket)
|
||||
}
|
||||
|
||||
user, err := GetUser(userId)
|
||||
@ -270,7 +270,7 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return "", "", fmt.Errorf("user %s found", userId)
|
||||
return "", "", fmt.Errorf("the user %s is not found", userId)
|
||||
}
|
||||
|
||||
application, err := GetApplicationByUser(user)
|
||||
@ -279,10 +279,13 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
return "", "", fmt.Errorf("application for user %s found", userId)
|
||||
return "", "", fmt.Errorf("the application for user %s is not found", userId)
|
||||
}
|
||||
|
||||
samlResponse := NewSamlResponse11(user, request.RequestID, host)
|
||||
samlResponse, err := NewSamlResponse11(user, request.RequestID, host)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
cert, err := getCertByApplication(application)
|
||||
if err != nil {
|
||||
|
@ -40,7 +40,7 @@ type UserShort struct {
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Avatar string `xorm:"varchar(500)" json:"avatar"`
|
||||
Email string `xorm:"varchar(100) index" json:"email"`
|
||||
Phone string `xorm:"varchar(20) index" json:"phone"`
|
||||
Phone string `xorm:"varchar(100) index" json:"phone"`
|
||||
}
|
||||
|
||||
type UserWithoutThirdIdp struct {
|
||||
@ -48,10 +48,11 @@ type UserWithoutThirdIdp struct {
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100) index" json:"createdTime"`
|
||||
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||
DeletedTime string `xorm:"varchar(100)" json:"deletedTime"`
|
||||
|
||||
Id string `xorm:"varchar(100) index" json:"id"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
Password string `xorm:"varchar(100)" json:"password"`
|
||||
Password string `xorm:"varchar(150)" json:"password"`
|
||||
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
@ -62,7 +63,7 @@ type UserWithoutThirdIdp struct {
|
||||
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
|
||||
Email string `xorm:"varchar(100) index" json:"email"`
|
||||
EmailVerified bool `json:"emailVerified"`
|
||||
Phone string `xorm:"varchar(20) index" json:"phone"`
|
||||
Phone string `xorm:"varchar(100) index" json:"phone"`
|
||||
CountryCode string `xorm:"varchar(6)" json:"countryCode"`
|
||||
Region string `xorm:"varchar(100)" json:"region"`
|
||||
Location string `xorm:"varchar(100)" json:"location"`
|
||||
@ -167,6 +168,7 @@ func getUserWithoutThirdIdp(user *User) *UserWithoutThirdIdp {
|
||||
Name: user.Name,
|
||||
CreatedTime: user.CreatedTime,
|
||||
UpdatedTime: user.UpdatedTime,
|
||||
DeletedTime: user.DeletedTime,
|
||||
|
||||
Id: user.Id,
|
||||
Type: user.Type,
|
||||
@ -357,6 +359,10 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
||||
var token *jwt.Token
|
||||
var refreshToken *jwt.Token
|
||||
|
||||
if application.TokenFormat == "" {
|
||||
application.TokenFormat = "JWT"
|
||||
}
|
||||
|
||||
// the JWT token length in "JWT-Empty" mode will be very short, as User object only has two properties: owner and name
|
||||
if application.TokenFormat == "JWT" {
|
||||
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
|
||||
|
@ -15,16 +15,19 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
func generateRsaKeys(bitSize int, expireInYears int, commonName string, organization string) (string, string, error) {
|
||||
func generateRsaKeys(bitSize int, shaSize int, expireInYears int, commonName string, organization string) (string, string, error) {
|
||||
// https://stackoverflow.com/questions/64104586/use-golang-to-get-rsa-key-the-same-way-openssl-genrsa
|
||||
// https://stackoverflow.com/questions/43822945/golang-can-i-create-x509keypair-using-rsa-key
|
||||
|
||||
@ -55,6 +58,132 @@ func generateRsaKeys(bitSize int, expireInYears int, commonName string, organiza
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
switch shaSize {
|
||||
case 256:
|
||||
tml.SignatureAlgorithm = x509.SHA256WithRSA
|
||||
case 384:
|
||||
tml.SignatureAlgorithm = x509.SHA384WithRSA
|
||||
case 512:
|
||||
tml.SignatureAlgorithm = x509.SHA512WithRSA
|
||||
default:
|
||||
return "", "", fmt.Errorf("generateRsaKeys() error, unsupported SHA size: %d", shaSize)
|
||||
}
|
||||
|
||||
cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Generate a pem block with the certificate
|
||||
certPem := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert,
|
||||
})
|
||||
|
||||
return string(certPem), string(privateKeyPem), nil
|
||||
}
|
||||
|
||||
func generateEsKeys(shaSize int, expireInYears int, commonName string, organization string) (string, string, error) {
|
||||
var curve elliptic.Curve
|
||||
switch shaSize {
|
||||
case 256:
|
||||
curve = elliptic.P256()
|
||||
case 384:
|
||||
curve = elliptic.P384()
|
||||
case 512:
|
||||
curve = elliptic.P521() // ES512(P521,SHA512)
|
||||
default:
|
||||
return "", "", fmt.Errorf("generateEsKeys() error, unsupported SHA size: %d", shaSize)
|
||||
}
|
||||
|
||||
// Generate ECDSA key pair.
|
||||
privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Encode private key to PEM format.
|
||||
privateKeyBytes, err := x509.MarshalECPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
privateKeyPem := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "EC PRIVATE KEY",
|
||||
Bytes: privateKeyBytes,
|
||||
})
|
||||
|
||||
// Generate certificate template.
|
||||
template := x509.Certificate{
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(expireInYears, 0, 0),
|
||||
SerialNumber: big.NewInt(time.Now().Unix()),
|
||||
Subject: pkix.Name{
|
||||
CommonName: commonName,
|
||||
Organization: []string{organization},
|
||||
},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
// Generate certificate.
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Encode certificate to PEM format.
|
||||
certPem := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: certBytes,
|
||||
})
|
||||
|
||||
return string(certPem), string(privateKeyPem), nil
|
||||
}
|
||||
|
||||
func generateRsaPssKeys(bitSize int, shaSize int, expireInYears int, commonName string, organization string) (string, string, error) {
|
||||
// Generate RSA key.
|
||||
key, err := rsa.GenerateKey(rand.Reader, bitSize)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Encode private key to PKCS#8 ASN.1 PEM.
|
||||
privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
privateKeyPem := pem.EncodeToMemory(
|
||||
&pem.Block{
|
||||
Type: "RSA PSS PRIVATE KEY",
|
||||
Bytes: privateKeyBytes,
|
||||
},
|
||||
)
|
||||
|
||||
tml := x509.Certificate{
|
||||
// you can add any attr that you need
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(expireInYears, 0, 0),
|
||||
// you have to generate a different serial number each execution
|
||||
SerialNumber: big.NewInt(123456),
|
||||
Subject: pkix.Name{
|
||||
CommonName: commonName,
|
||||
Organization: []string{organization},
|
||||
},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
// Set the signature algorithm based on the hash function
|
||||
switch shaSize {
|
||||
case 256:
|
||||
tml.SignatureAlgorithm = x509.SHA256WithRSAPSS
|
||||
case 384:
|
||||
tml.SignatureAlgorithm = x509.SHA384WithRSAPSS
|
||||
case 512:
|
||||
tml.SignatureAlgorithm = x509.SHA512WithRSAPSS
|
||||
default:
|
||||
return "", "", fmt.Errorf("generateRsaPssKeys() error, unsupported SHA size: %d", shaSize)
|
||||
}
|
||||
|
||||
cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
|
@ -23,7 +23,35 @@ import (
|
||||
|
||||
func TestGenerateRsaKeys(t *testing.T) {
|
||||
fileId := "token_jwt_key"
|
||||
certificate, privateKey, err := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization")
|
||||
certificate, privateKey, err := generateRsaKeys(4096, 512, 20, "Casdoor Cert", "Casdoor Organization")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Write certificate (aka certificate) to file.
|
||||
util.WriteStringToPath(certificate, fmt.Sprintf("%s.pem", fileId))
|
||||
|
||||
// Write private key to file.
|
||||
util.WriteStringToPath(privateKey, fmt.Sprintf("%s.key", fileId))
|
||||
}
|
||||
|
||||
func TestGenerateEsKeys(t *testing.T) {
|
||||
fileId := "token_jwt_key"
|
||||
certificate, privateKey, err := generateEsKeys(256, 20, "Casdoor Cert", "Casdoor Organization")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Write certificate (aka certificate) to file.
|
||||
util.WriteStringToPath(certificate, fmt.Sprintf("%s.pem", fileId))
|
||||
|
||||
// Write private key to file.
|
||||
util.WriteStringToPath(privateKey, fmt.Sprintf("%s.key", fileId))
|
||||
}
|
||||
|
||||
func TestGenerateRsaPssKeys(t *testing.T) {
|
||||
fileId := "token_jwt_key"
|
||||
certificate, privateKey, err := generateRsaPssKeys(4096, 256, 20, "Casdoor Cert", "Casdoor Organization")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
745
object/token_oauth.go
Normal file
745
object/token_oauth.go
Normal file
@ -0,0 +1,745 @@
|
||||
// Copyright 2024 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 (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
"github.com/casdoor/casdoor/idp"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
|
||||
const (
|
||||
hourSeconds = int(time.Hour / time.Second)
|
||||
InvalidRequest = "invalid_request"
|
||||
InvalidClient = "invalid_client"
|
||||
InvalidGrant = "invalid_grant"
|
||||
UnauthorizedClient = "unauthorized_client"
|
||||
UnsupportedGrantType = "unsupported_grant_type"
|
||||
InvalidScope = "invalid_scope"
|
||||
EndpointError = "endpoint_error"
|
||||
)
|
||||
|
||||
type Code struct {
|
||||
Message string `xorm:"varchar(100)" json:"message"`
|
||||
Code string `xorm:"varchar(100)" json:"code"`
|
||||
}
|
||||
|
||||
type TokenWrapper struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
IdToken string `json:"id_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
type TokenError struct {
|
||||
Error string `json:"error"`
|
||||
ErrorDescription string `json:"error_description,omitempty"`
|
||||
}
|
||||
|
||||
type IntrospectionResponse struct {
|
||||
Active bool `json:"active"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
ClientId string `json:"client_id,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
TokenType string `json:"token_type,omitempty"`
|
||||
Exp int64 `json:"exp,omitempty"`
|
||||
Iat int64 `json:"iat,omitempty"`
|
||||
Nbf int64 `json:"nbf,omitempty"`
|
||||
Sub string `json:"sub,omitempty"`
|
||||
Aud []string `json:"aud,omitempty"`
|
||||
Iss string `json:"iss,omitempty"`
|
||||
Jti string `json:"jti,omitempty"`
|
||||
}
|
||||
|
||||
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token, error) {
|
||||
token, err := GetTokenByAccessToken(accessToken)
|
||||
if err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
if token == nil {
|
||||
return false, nil, nil, nil
|
||||
}
|
||||
|
||||
token.ExpiresIn = 0
|
||||
affected, err := ormer.Engine.ID(core.PK{token.Owner, token.Name}).Cols("expires_in").Update(token)
|
||||
if err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
|
||||
application, err := getApplication(token.Owner, token.Application)
|
||||
if err != nil {
|
||||
return false, nil, nil, err
|
||||
}
|
||||
|
||||
return affected != 0, application, token, nil
|
||||
}
|
||||
|
||||
func CheckOAuthLogin(clientId string, responseType string, redirectUri string, scope string, state string, lang string) (string, *Application, error) {
|
||||
if responseType != "code" && responseType != "token" && responseType != "id_token" {
|
||||
return fmt.Sprintf(i18n.Translate(lang, "token:Grant_type: %s is not supported in this application"), responseType), nil, nil
|
||||
}
|
||||
|
||||
application, err := GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
return i18n.Translate(lang, "token:Invalid client_id"), nil, nil
|
||||
}
|
||||
|
||||
if !application.IsRedirectUriValid(redirectUri) {
|
||||
return fmt.Sprintf(i18n.Translate(lang, "token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri), application, nil
|
||||
}
|
||||
|
||||
// Mask application for /api/get-app-login
|
||||
application.ClientSecret = ""
|
||||
return "", application, nil
|
||||
}
|
||||
|
||||
func GetOAuthCode(userId string, clientId string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string, lang string) (*Code, error) {
|
||||
user, err := GetUser(userId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return &Code{
|
||||
Message: fmt.Sprintf("general:The user: %s doesn't exist", userId),
|
||||
Code: "",
|
||||
}, nil
|
||||
}
|
||||
if user.IsForbidden {
|
||||
return &Code{
|
||||
Message: "error: the user is forbidden to sign in, please contact the administrator",
|
||||
Code: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
msg, application, err := CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, lang)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if msg != "" {
|
||||
return &Code{
|
||||
Message: msg,
|
||||
Code: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, nonce, scope, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if challenge == "null" {
|
||||
challenge = ""
|
||||
}
|
||||
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Code: util.GenerateClientId(),
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: scope,
|
||||
TokenType: "Bearer",
|
||||
CodeChallenge: challenge,
|
||||
CodeIsUsed: false,
|
||||
CodeExpireIn: time.Now().Add(time.Minute * 5).Unix(),
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Code{
|
||||
Message: "",
|
||||
Code: token.Code,
|
||||
}, nil
|
||||
}
|
||||
|
||||
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{}, error) {
|
||||
application, err := GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
return &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_id is invalid",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Check if grantType is allowed in the current application
|
||||
|
||||
if !IsGrantTypeValid(grantType, application.GrantTypes) && tag == "" {
|
||||
return &TokenError{
|
||||
Error: UnsupportedGrantType,
|
||||
ErrorDescription: fmt.Sprintf("grant_type: %s is not supported in this application", grantType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var token *Token
|
||||
var tokenError *TokenError
|
||||
switch grantType {
|
||||
case "authorization_code": // Authorization Code Grant
|
||||
token, tokenError, err = GetAuthorizationCodeToken(application, clientSecret, code, verifier)
|
||||
case "password": // Resource Owner Password Credentials Grant
|
||||
token, tokenError, err = GetPasswordToken(application, username, password, scope, host)
|
||||
case "client_credentials": // Client Credentials Grant
|
||||
token, tokenError, err = GetClientCredentialsToken(application, clientSecret, scope, host)
|
||||
case "refresh_token":
|
||||
refreshToken2, err := RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return refreshToken2, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tag == "wechat_miniprogram" {
|
||||
// Wechat Mini Program
|
||||
token, tokenError, err = GetWechatMiniProgramToken(application, code, host, username, avatar, lang)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if tokenError != nil {
|
||||
return tokenError, nil
|
||||
}
|
||||
|
||||
token.CodeIsUsed = true
|
||||
|
||||
go updateUsedByCode(token)
|
||||
|
||||
tokenWrapper := &TokenWrapper{
|
||||
AccessToken: token.AccessToken,
|
||||
IdToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
TokenType: token.TokenType,
|
||||
ExpiresIn: token.ExpiresIn,
|
||||
Scope: token.Scope,
|
||||
}
|
||||
|
||||
return tokenWrapper, nil
|
||||
}
|
||||
|
||||
func RefreshToken(grantType string, refreshToken string, scope string, clientId string, clientSecret string, host string) (interface{}, error) {
|
||||
// check parameters
|
||||
if grantType != "refresh_token" {
|
||||
return &TokenError{
|
||||
Error: UnsupportedGrantType,
|
||||
ErrorDescription: "grant_type should be refresh_token",
|
||||
}, nil
|
||||
}
|
||||
application, err := GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
return &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_id is invalid",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if clientSecret != "" && application.ClientSecret != clientSecret {
|
||||
return &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// check whether the refresh token is valid, and has not expired.
|
||||
token, err := GetTokenByRefreshToken(refreshToken)
|
||||
if err != nil || token == nil {
|
||||
return &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "refresh token is invalid, expired or revoked",
|
||||
}, nil
|
||||
}
|
||||
|
||||
cert, err := getCertByApplication(application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cert == nil {
|
||||
return &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("cert: %s cannot be found", application.Cert),
|
||||
}, nil
|
||||
}
|
||||
|
||||
_, err = ParseJwtToken(refreshToken, cert)
|
||||
if err != nil {
|
||||
return &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// generate a new token
|
||||
user, err := getUser(application.Organization, token.User)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user.IsForbidden {
|
||||
return &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the user is forbidden to sign in, please contact the administrator",
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newAccessToken, newRefreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
||||
if err != nil {
|
||||
return &TokenError{
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
newToken := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Code: util.GenerateClientId(),
|
||||
AccessToken: newAccessToken,
|
||||
RefreshToken: newRefreshToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: scope,
|
||||
TokenType: "Bearer",
|
||||
}
|
||||
_, err = AddToken(newToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = DeleteToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenWrapper := &TokenWrapper{
|
||||
AccessToken: newToken.AccessToken,
|
||||
IdToken: newToken.AccessToken,
|
||||
RefreshToken: newToken.RefreshToken,
|
||||
TokenType: newToken.TokenType,
|
||||
ExpiresIn: newToken.ExpiresIn,
|
||||
Scope: newToken.Scope,
|
||||
}
|
||||
return tokenWrapper, nil
|
||||
}
|
||||
|
||||
// PkceChallenge: base64-URL-encoded SHA256 hash of verifier, per rfc 7636
|
||||
func pkceChallenge(verifier string) string {
|
||||
sum := sha256.Sum256([]byte(verifier))
|
||||
challenge := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(sum[:])
|
||||
return challenge
|
||||
}
|
||||
|
||||
// IsGrantTypeValid
|
||||
// Check if grantType is allowed in the current application
|
||||
// authorization_code is allowed by default
|
||||
func IsGrantTypeValid(method string, grantTypes []string) bool {
|
||||
if method == "authorization_code" {
|
||||
return true
|
||||
}
|
||||
for _, m := range grantTypes {
|
||||
if m == method {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetAuthorizationCodeToken
|
||||
// Authorization code flow
|
||||
func GetAuthorizationCodeToken(application *Application, clientSecret string, code string, verifier string) (*Token, *TokenError, error) {
|
||||
if code == "" {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidRequest,
|
||||
ErrorDescription: "authorization code should not be empty",
|
||||
}, nil
|
||||
}
|
||||
|
||||
token, err := getTokenByCode(code)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if token == nil {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "authorization code is invalid",
|
||||
}, nil
|
||||
}
|
||||
if token.CodeIsUsed {
|
||||
// anti replay attacks
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "authorization code has been used",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if token.CodeChallenge != "" && pkceChallenge(verifier) != token.CodeChallenge {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "verifier is invalid",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if application.ClientSecret != clientSecret {
|
||||
// when using PKCE, the Client Secret can be empty,
|
||||
// but if it is provided, it must be accurate.
|
||||
if token.CodeChallenge == "" {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}, nil
|
||||
} else {
|
||||
if clientSecret != "" {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if application.Name != token.Application {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the token is for wrong application (client_id)",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if time.Now().Unix() > token.CodeExpireIn {
|
||||
// code must be used within 5 minutes
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "authorization code has expired",
|
||||
}, nil
|
||||
}
|
||||
return token, nil, nil
|
||||
}
|
||||
|
||||
// GetPasswordToken
|
||||
// Resource Owner Password Credentials flow
|
||||
func GetPasswordToken(application *Application, username string, password string, scope string, host string) (*Token, *TokenError, error) {
|
||||
user, err := GetUserByFields(application.Organization, username)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if user == nil {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the user does not exist",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if user.Ldap != "" {
|
||||
err = checkLdapUserPassword(user, password, "en")
|
||||
} else {
|
||||
err = CheckPassword(user, password, "en")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("invalid username or password: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if user.IsForbidden {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the user is forbidden to sign in, please contact the administrator",
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Code: util.GenerateClientId(),
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: scope,
|
||||
TokenType: "Bearer",
|
||||
CodeIsUsed: true,
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return token, nil, nil
|
||||
}
|
||||
|
||||
// GetClientCredentialsToken
|
||||
// Client Credentials flow
|
||||
func GetClientCredentialsToken(application *Application, clientSecret string, scope string, host string) (*Token, *TokenError, error) {
|
||||
if application.ClientSecret != clientSecret {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}, nil
|
||||
}
|
||||
nullUser := &User{
|
||||
Owner: application.Owner,
|
||||
Id: application.GetId(),
|
||||
Name: application.Name,
|
||||
Type: "application",
|
||||
}
|
||||
|
||||
accessToken, _, tokenName, err := generateJwtToken(application, nullUser, "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: application.Organization,
|
||||
User: nullUser.Name,
|
||||
Code: util.GenerateClientId(),
|
||||
AccessToken: accessToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: scope,
|
||||
TokenType: "Bearer",
|
||||
CodeIsUsed: true,
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return token, nil, nil
|
||||
}
|
||||
|
||||
// GetTokenByUser
|
||||
// Implicit flow
|
||||
func GetTokenByUser(application *Application, user *User, scope string, nonce string, host string) (*Token, error) {
|
||||
err := ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, nonce, scope, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Code: util.GenerateClientId(),
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: scope,
|
||||
TokenType: "Bearer",
|
||||
CodeIsUsed: true,
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetWechatMiniProgramToken
|
||||
// Wechat Mini Program flow
|
||||
func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string, lang string) (*Token, *TokenError, error) {
|
||||
mpProvider := GetWechatMiniProgramProvider(application)
|
||||
if mpProvider == nil {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "the application does not support wechat mini program",
|
||||
}, nil
|
||||
}
|
||||
provider, err := GetProvider(util.GetId("admin", mpProvider.Name))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
mpIdp := idp.NewWeChatMiniProgramIdProvider(provider.ClientId, provider.ClientSecret)
|
||||
session, err := mpIdp.GetSessionByCode(code)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("get wechat mini program session error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
openId, unionId := session.Openid, session.Unionid
|
||||
if openId == "" && unionId == "" {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidRequest,
|
||||
ErrorDescription: "the wechat mini program session is invalid",
|
||||
}, nil
|
||||
}
|
||||
user, err := getUserByWechatId(application.Organization, openId, unionId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
if !application.EnableSignUp {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the application does not allow to sign up new account",
|
||||
}, nil
|
||||
}
|
||||
// Add new user
|
||||
var name string
|
||||
if CheckUsername(username, lang) == "" {
|
||||
name = username
|
||||
} else {
|
||||
name = fmt.Sprintf("wechat-%s", openId)
|
||||
}
|
||||
|
||||
user = &User{
|
||||
Owner: application.Organization,
|
||||
Id: util.GenerateId(),
|
||||
Name: name,
|
||||
Avatar: avatar,
|
||||
SignupApplication: application.Name,
|
||||
WeChat: openId,
|
||||
Type: "normal-user",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
IsAdmin: false,
|
||||
IsForbidden: false,
|
||||
IsDeleted: false,
|
||||
Properties: map[string]string{
|
||||
UserPropertiesWechatOpenId: openId,
|
||||
UserPropertiesWechatUnionId: unionId,
|
||||
},
|
||||
}
|
||||
_, err = AddUser(user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Code: session.SessionKey, // a trick, because miniprogram does not use the code, so use the code field to save the session_key
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: refreshToken,
|
||||
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||
Scope: "",
|
||||
TokenType: "Bearer",
|
||||
CodeIsUsed: true,
|
||||
}
|
||||
_, err = AddToken(token)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return token, nil, nil
|
||||
}
|
||||
|
||||
func GetAccessTokenByUser(user *User, host string) (string, error) {
|
||||
application, err := GetApplicationByUser(user)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if application == nil {
|
||||
return "", fmt.Errorf("the application for user %s is not found", user.Id)
|
||||
}
|
||||
|
||||
token, err := GetTokenByUser(application, user, "profile", "", host)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return token.AccessToken, nil
|
||||
}
|
144
object/transaction.go
Normal file
144
object/transaction.go
Normal file
@ -0,0 +1,144 @@
|
||||
// Copyright 2024 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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
|
||||
type Transaction 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"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
// Transaction Provider Info
|
||||
Provider string `xorm:"varchar(100)" json:"provider"`
|
||||
Category string `xorm:"varchar(100)" json:"category"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
// Product Info
|
||||
ProductName string `xorm:"varchar(100)" json:"productName"`
|
||||
ProductDisplayName string `xorm:"varchar(100)" json:"productDisplayName"`
|
||||
Detail string `xorm:"varchar(255)" json:"detail"`
|
||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
Amount float64 `json:"amount"`
|
||||
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
|
||||
// User Info
|
||||
User string `xorm:"varchar(100)" json:"user"`
|
||||
Application string `xorm:"varchar(100)" json:"application"`
|
||||
Payment string `xorm:"varchar(100)" json:"payment"`
|
||||
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
}
|
||||
|
||||
func GetTransactionCount(owner, field, value string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
return session.Count(&Transaction{Owner: owner})
|
||||
}
|
||||
|
||||
func GetTransactions(owner string) ([]*Transaction, error) {
|
||||
transactions := []*Transaction{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&transactions, &Transaction{Owner: owner})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
func GetUserTransactions(owner, user string) ([]*Transaction, error) {
|
||||
transactions := []*Transaction{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&transactions, &Transaction{Owner: owner, User: user})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
func GetPaginationTransactions(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Transaction, error) {
|
||||
transactions := []*Transaction{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&transactions, &Transaction{Owner: owner})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return transactions, nil
|
||||
}
|
||||
|
||||
func getTransaction(owner string, name string) (*Transaction, error) {
|
||||
if owner == "" || name == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
transaction := Transaction{Owner: owner, Name: name}
|
||||
existed, err := ormer.Engine.Get(&transaction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &transaction, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetTransaction(id string) (*Transaction, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getTransaction(owner, name)
|
||||
}
|
||||
|
||||
func UpdateTransaction(id string, transaction *Transaction) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if p, err := getTransaction(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if p == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(transaction)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func AddTransaction(transaction *Transaction) (bool, error) {
|
||||
affected, err := ormer.Engine.Insert(transaction)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func DeleteTransaction(transaction *Transaction) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{transaction.Owner, transaction.Name}).Delete(&Transaction{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func (transaction *Transaction) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", transaction.Owner, transaction.Name)
|
||||
}
|
@ -15,7 +15,9 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -49,11 +51,12 @@ type User struct {
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100) index" json:"createdTime"`
|
||||
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||
DeletedTime string `xorm:"varchar(100)" json:"deletedTime"`
|
||||
|
||||
Id string `xorm:"varchar(100) index" json:"id"`
|
||||
ExternalId string `xorm:"varchar(100) index" json:"externalId"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
Password string `xorm:"varchar(100)" json:"password"`
|
||||
Password string `xorm:"varchar(150)" json:"password"`
|
||||
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
@ -64,7 +67,7 @@ type User struct {
|
||||
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
|
||||
Email string `xorm:"varchar(100) index" json:"email"`
|
||||
EmailVerified bool `json:"emailVerified"`
|
||||
Phone string `xorm:"varchar(20) index" json:"phone"`
|
||||
Phone string `xorm:"varchar(100) index" json:"phone"`
|
||||
CountryCode string `xorm:"varchar(6)" json:"countryCode"`
|
||||
Region string `xorm:"varchar(100)" json:"region"`
|
||||
Location string `xorm:"varchar(100)" json:"location"`
|
||||
@ -83,6 +86,8 @@ type User struct {
|
||||
Score int `json:"score"`
|
||||
Karma int `json:"karma"`
|
||||
Ranking int `json:"ranking"`
|
||||
Balance float64 `json:"balance"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
IsDefaultAvatar bool `json:"isDefaultAvatar"`
|
||||
IsOnline bool `json:"isOnline"`
|
||||
IsAdmin bool `json:"isAdmin"`
|
||||
@ -93,6 +98,7 @@ type User struct {
|
||||
PreHash string `xorm:"varchar(100)" json:"preHash"`
|
||||
AccessKey string `xorm:"varchar(100)" json:"accessKey"`
|
||||
AccessSecret string `xorm:"varchar(100)" json:"accessSecret"`
|
||||
AccessToken string `xorm:"mediumtext" json:"accessToken"`
|
||||
|
||||
CreatedIp string `xorm:"varchar(100)" json:"createdIp"`
|
||||
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
|
||||
@ -185,6 +191,7 @@ type User struct {
|
||||
MultiFactorAuths []*MfaProps `xorm:"-" json:"multiFactorAuths,omitempty"`
|
||||
Invitation string `xorm:"varchar(100) index" json:"invitation"`
|
||||
InvitationCode string `xorm:"varchar(100) index" json:"invitationCode"`
|
||||
FaceIds []*FaceId `json:"faceIds"`
|
||||
|
||||
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
|
||||
Properties map[string]string `json:"properties"`
|
||||
@ -211,6 +218,8 @@ type Userinfo struct {
|
||||
Address string `json:"address,omitempty"`
|
||||
Phone string `json:"phone,omitempty"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
Roles []string `json:"roles,omitempty"`
|
||||
Permissions []string `json:"permissions,omitempty"`
|
||||
}
|
||||
|
||||
type ManagedAccount struct {
|
||||
@ -220,6 +229,31 @@ type ManagedAccount struct {
|
||||
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
|
||||
}
|
||||
|
||||
type FaceId struct {
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
FaceIdData []float64 `json:"faceIdData"`
|
||||
}
|
||||
|
||||
func GetUserFieldStringValue(user *User, fieldName string) (bool, string, error) {
|
||||
val := reflect.ValueOf(*user)
|
||||
fieldValue := val.FieldByName(fieldName)
|
||||
|
||||
if !fieldValue.IsValid() {
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
if fieldValue.Kind() == reflect.String {
|
||||
return true, fieldValue.String(), nil
|
||||
}
|
||||
|
||||
marshalValue, err := json.Marshal(fieldValue.Interface())
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
return true, string(marshalValue), nil
|
||||
}
|
||||
|
||||
func GetGlobalUserCount(field, value string) (int64, error) {
|
||||
session := GetSession("", -1, -1, field, value, "", "")
|
||||
return session.Count(&User{})
|
||||
@ -639,8 +673,8 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
if len(columns) == 0 {
|
||||
columns = []string{
|
||||
"owner", "display_name", "avatar", "first_name", "last_name",
|
||||
"location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
||||
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
||||
"location", "address", "country_code", "region", "language", "affiliation", "title", "id_card_type", "id_card", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
||||
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "face_ids",
|
||||
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret",
|
||||
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
||||
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
||||
@ -652,12 +686,16 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
}
|
||||
}
|
||||
if isAdmin {
|
||||
columns = append(columns, "name", "email", "phone", "country_code", "type")
|
||||
columns = append(columns, "name", "id", "email", "phone", "country_code", "type")
|
||||
}
|
||||
|
||||
columns = append(columns, "updated_time")
|
||||
user.UpdatedTime = util.GetCurrentTime()
|
||||
|
||||
if len(user.DeletedTime) > 0 {
|
||||
columns = append(columns, "deleted_time")
|
||||
}
|
||||
|
||||
if util.ContainsString(columns, "groups") {
|
||||
_, err := userEnforcer.UpdateGroupsForUser(user.GetId(), user.Groups)
|
||||
if err != nil {
|
||||
@ -788,6 +826,13 @@ func AddUser(user *User) (bool, error) {
|
||||
}
|
||||
user.Ranking = int(count + 1)
|
||||
|
||||
if user.Groups != nil && len(user.Groups) > 0 {
|
||||
_, err = userEnforcer.UpdateGroupsForUser(user.GetId(), user.Groups)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.Insert(user)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@ -817,6 +862,13 @@ func AddUsers(users []*User) (bool, error) {
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if user.Groups != nil && len(user.Groups) > 0 {
|
||||
_, err = userEnforcer.UpdateGroupsForUser(user.GetId(), user.Groups)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.Insert(users)
|
||||
@ -871,7 +923,7 @@ func DeleteUser(user *User) (bool, error) {
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func GetUserInfo(user *User, scope string, aud string, host string) *Userinfo {
|
||||
func GetUserInfo(user *User, scope string, aud string, host string) (*Userinfo, error) {
|
||||
_, originBackend := getOriginFromHost(host)
|
||||
|
||||
resp := Userinfo{
|
||||
@ -879,24 +931,44 @@ func GetUserInfo(user *User, scope string, aud string, host string) *Userinfo {
|
||||
Iss: originBackend,
|
||||
Aud: aud,
|
||||
}
|
||||
|
||||
if strings.Contains(scope, "profile") {
|
||||
resp.Name = user.Name
|
||||
resp.DisplayName = user.DisplayName
|
||||
resp.Avatar = user.Avatar
|
||||
resp.Groups = user.Groups
|
||||
|
||||
err := ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Roles = []string{}
|
||||
for _, role := range user.Roles {
|
||||
resp.Roles = append(resp.Roles, role.Name)
|
||||
}
|
||||
|
||||
resp.Permissions = []string{}
|
||||
for _, permission := range user.Permissions {
|
||||
resp.Permissions = append(resp.Permissions, permission.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(scope, "email") {
|
||||
resp.Email = user.Email
|
||||
// resp.EmailVerified = user.EmailVerified
|
||||
resp.EmailVerified = true
|
||||
}
|
||||
|
||||
if strings.Contains(scope, "address") {
|
||||
resp.Address = user.Location
|
||||
}
|
||||
|
||||
if strings.Contains(scope, "phone") {
|
||||
resp.Phone = user.Phone
|
||||
}
|
||||
return &resp
|
||||
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func LinkUserAccount(user *User, field string, value string) (bool, error) {
|
||||
@ -980,6 +1052,10 @@ func userChangeTrigger(oldName string, newName string) error {
|
||||
}
|
||||
for _, permission := range permissions {
|
||||
for j, u := range permission.Users {
|
||||
if u == "*" {
|
||||
continue
|
||||
}
|
||||
|
||||
// u = organization/username
|
||||
owner, name := util.GetOwnerAndNameFromId(u)
|
||||
if name == oldName {
|
||||
|
@ -41,11 +41,7 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
fmt.Printf("downloadImage() error for url [%s]: %s\n", url, resp.Status)
|
||||
if resp.StatusCode == 404 {
|
||||
return nil, "", nil
|
||||
} else {
|
||||
return nil, "", fmt.Errorf("failed to download gravatar image: %s", resp.Status)
|
||||
}
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
// Get the content type and determine the file extension
|
||||
|
@ -1,10 +1,12 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
errors2 "errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/errors"
|
||||
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
@ -87,7 +89,7 @@ func (e *UserGroupEnforcer) GetAllUsersByGroup(group string) ([]string, error) {
|
||||
|
||||
users, err := e.enforcer.GetUsersForRole(GetGroupWithPrefix(group))
|
||||
if err != nil {
|
||||
if err == errors.ErrNameNotFound {
|
||||
if errors2.Is(err, errors.ErrNameNotFound) {
|
||||
return []string{}, nil
|
||||
}
|
||||
return nil, err
|
||||
|
@ -134,6 +134,7 @@ func UploadUsers(owner string, path string) (bool, error) {
|
||||
LastSigninIp: parseLineItem(&line, 38),
|
||||
Ldap: "",
|
||||
Properties: map[string]string{},
|
||||
DeletedTime: parseLineItem(&line, 39),
|
||||
}
|
||||
|
||||
if _, ok := oldUserMap[user.GetId()]; !ok {
|
||||
|
@ -18,8 +18,11 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
|
||||
"github.com/casdoor/casdoor/idp"
|
||||
@ -74,6 +77,12 @@ func GetUserByFields(organization string, field string) (*User, error) {
|
||||
return user, err
|
||||
}
|
||||
|
||||
// check user ID
|
||||
user, err = GetUserByField(organization, "id", field)
|
||||
if user != nil || err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
// check ID card
|
||||
user, err = GetUserByField(organization, "id_card", field)
|
||||
if user != nil || err != nil {
|
||||
@ -328,6 +337,31 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
if oldUser.Gender != newUser.Gender {
|
||||
item := GetAccountItemByName("Gender", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
if oldUser.Birthday != newUser.Birthday {
|
||||
item := GetAccountItemByName("Birthday", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
if oldUser.Education != newUser.Education {
|
||||
item := GetAccountItemByName("Education", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
if oldUser.IdCard != newUser.IdCard {
|
||||
item := GetAccountItemByName("ID card", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
if oldUser.IdCardType != newUser.IdCardType {
|
||||
item := GetAccountItemByName("ID card type", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties)
|
||||
newUserPropertiesJson, _ := json.Marshal(newUser.Properties)
|
||||
if string(oldUserPropertiesJson) != string(newUserPropertiesJson) {
|
||||
@ -353,6 +387,11 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
if newUser.FaceIds != nil {
|
||||
item := GetAccountItemByName("Face ID", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
if oldUser.IsAdmin != newUser.IsAdmin {
|
||||
item := GetAccountItemByName("Is admin", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
@ -372,10 +411,33 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
for i := range itemsChanged {
|
||||
if pass, err := CheckAccountItemModifyRule(itemsChanged[i], isAdmin, lang); !pass {
|
||||
for _, accountItem := range itemsChanged {
|
||||
|
||||
if pass, err := CheckAccountItemModifyRule(accountItem, isAdmin, lang); !pass {
|
||||
return pass, err
|
||||
}
|
||||
|
||||
exist, userValue, err := GetUserFieldStringValue(newUser, util.SpaceToCamel(accountItem.Name))
|
||||
if err != nil {
|
||||
return false, err.Error()
|
||||
}
|
||||
|
||||
if !exist {
|
||||
continue
|
||||
}
|
||||
|
||||
if accountItem.Regex == "" {
|
||||
continue
|
||||
}
|
||||
regexSignupItem, err := regexp.Compile(accountItem.Regex)
|
||||
if err != nil {
|
||||
return false, err.Error()
|
||||
}
|
||||
|
||||
matched := regexSignupItem.MatchString(userValue)
|
||||
if !matched {
|
||||
return false, fmt.Sprintf(i18n.Translate(lang, "check:The value \"%s\" for account field \"%s\" doesn't match the account item regex"), userValue, accountItem.Name)
|
||||
}
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package object
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
@ -53,7 +54,7 @@ type VerificationRecord struct {
|
||||
Type string `xorm:"varchar(10)"`
|
||||
User string `xorm:"varchar(100) notnull"`
|
||||
Provider string `xorm:"varchar(100) notnull"`
|
||||
Receiver string `xorm:"varchar(100) notnull"`
|
||||
Receiver string `xorm:"varchar(100) index notnull"`
|
||||
Code string `xorm:"varchar(10) notnull"`
|
||||
Time int64 `xorm:"notnull"`
|
||||
IsUsed bool
|
||||
@ -66,6 +67,7 @@ func IsAllowSend(user *User, remoteAddr, recordType string) error {
|
||||
if user != nil {
|
||||
record.User = user.GetId()
|
||||
}
|
||||
|
||||
has, err := ormer.Engine.Desc("created_time").Get(&record)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -94,15 +96,18 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
|
||||
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
|
||||
}
|
||||
|
||||
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil {
|
||||
err := IsAllowSend(user, remoteAddr, provider.Category)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := SendEmail(provider, title, content, dest, sender); err != nil {
|
||||
err = SendEmail(provider, title, content, dest, sender)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code); err != nil {
|
||||
err = AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -110,7 +115,8 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
|
||||
}
|
||||
|
||||
func SendVerificationCodeToPhone(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
|
||||
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil {
|
||||
err := IsAllowSend(user, remoteAddr, provider.Category)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -119,11 +125,13 @@ func SendVerificationCodeToPhone(organization *Organization, user *User, provide
|
||||
code = organization.MasterVerificationCode
|
||||
}
|
||||
|
||||
if err := SendSms(provider, code, dest); err != nil {
|
||||
err = SendSms(provider, code, dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code); err != nil {
|
||||
err = AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -158,6 +166,7 @@ func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordT
|
||||
func getVerificationRecord(dest string) (*VerificationRecord, error) {
|
||||
var record VerificationRecord
|
||||
record.Receiver = dest
|
||||
|
||||
has, err := ormer.Engine.Desc("time").Where("is_used = false").Get(&record)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -165,34 +174,34 @@ func getVerificationRecord(dest string) (*VerificationRecord, error) {
|
||||
if !has {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
func CheckVerificationCode(dest string, code string, lang string) *VerifyResult {
|
||||
func CheckVerificationCode(dest string, code string, lang string) (*VerifyResult, error) {
|
||||
record, err := getVerificationRecord(dest)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if record == nil {
|
||||
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:Code has not been sent yet!")}
|
||||
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:The verification code has not been sent yet, or has already been used!")}, nil
|
||||
}
|
||||
|
||||
timeout, err := conf.GetConfigInt64("verificationCodeTimeout")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
if now-record.Time > timeout*60 {
|
||||
return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeout)}
|
||||
return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeout)}, nil
|
||||
}
|
||||
|
||||
if record.Code != code {
|
||||
return &VerifyResult{wrongCodeError, i18n.Translate(lang, "verification:Wrong verification code!")}
|
||||
return &VerifyResult{wrongCodeError, i18n.Translate(lang, "verification:Wrong verification code!")}, nil
|
||||
}
|
||||
|
||||
return &VerifyResult{VerificationSuccess, ""}
|
||||
return &VerifyResult{VerificationSuccess, ""}, nil
|
||||
}
|
||||
|
||||
func DisableVerificationCode(dest string) error {
|
||||
@ -213,7 +222,11 @@ func CheckSigninCode(user *User, dest, code, lang string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
result := CheckVerificationCode(dest, code, lang)
|
||||
result, err := CheckVerificationCode(dest, code, lang)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch result.Code {
|
||||
case VerificationSuccess:
|
||||
return resetUserSigninErrorTimes(user)
|
||||
@ -224,6 +237,28 @@ func CheckSigninCode(user *User, dest, code, lang string) error {
|
||||
}
|
||||
}
|
||||
|
||||
func CheckFaceId(user *User, faceId []float64, lang string) error {
|
||||
if len(user.FaceIds) == 0 {
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Face data does not exist, cannot log in"))
|
||||
}
|
||||
|
||||
for _, userFaceId := range user.FaceIds {
|
||||
if faceId == nil || len(userFaceId.FaceIdData) != len(faceId) {
|
||||
continue
|
||||
}
|
||||
var sumOfSquares float64
|
||||
for i := 0; i < len(userFaceId.FaceIdData); i++ {
|
||||
diff := userFaceId.FaceIdData[i] - faceId[i]
|
||||
sumOfSquares += diff * diff
|
||||
}
|
||||
if math.Sqrt(sumOfSquares) < 0.25 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Face data mismatch"))
|
||||
}
|
||||
|
||||
func GetVerifyType(username string) (verificationCodeType string) {
|
||||
if strings.Contains(username, "@") {
|
||||
return VerifyTypeEmail
|
||||
|
116
object/viaSSHDialer.go
Normal file
116
object/viaSSHDialer.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2024 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 (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
mssql "github.com/denisenkom/go-mssqldb"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type ViaSSHDialer struct {
|
||||
Client *ssh.Client
|
||||
Context *context.Context
|
||||
DatabaseType string
|
||||
}
|
||||
|
||||
func (v *ViaSSHDialer) MysqlDial(ctx context.Context, addr string) (net.Conn, error) {
|
||||
return v.Client.Dial("tcp", addr)
|
||||
}
|
||||
|
||||
func (v *ViaSSHDialer) Open(s string) (_ driver.Conn, err error) {
|
||||
if v.DatabaseType == "mssql" {
|
||||
c, err := mssql.NewConnector(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Dialer = v
|
||||
return c.Connect(context.Background())
|
||||
} else if v.DatabaseType == "postgres" {
|
||||
return pq.DialOpen(v, s)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (v *ViaSSHDialer) Dial(network, address string) (net.Conn, error) {
|
||||
return v.Client.Dial(network, address)
|
||||
}
|
||||
|
||||
func (v *ViaSSHDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
|
||||
return v.Client.DialContext(ctx, network, addr)
|
||||
}
|
||||
|
||||
func (v *ViaSSHDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
|
||||
return v.Client.Dial(network, address)
|
||||
}
|
||||
|
||||
func DialWithPassword(SshUser string, SshPassword string, SshHost string, SshPort int) (*ssh.Client, error) {
|
||||
address := fmt.Sprintf("%s:%d", SshHost, SshPort)
|
||||
config := &ssh.ClientConfig{
|
||||
User: SshUser,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(SshPassword),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
return ssh.Dial("tcp", address, config)
|
||||
}
|
||||
|
||||
func DialWithCert(SshUser string, CertId string, SshHost string, SshPort int) (*ssh.Client, error) {
|
||||
address := fmt.Sprintf("%s:%d", SshHost, SshPort)
|
||||
config := &ssh.ClientConfig{
|
||||
User: SshUser,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
cert, err := GetCert(CertId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signer, err := ssh.ParsePrivateKey([]byte(cert.PrivateKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Auth = []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
}
|
||||
return ssh.Dial("tcp", address, config)
|
||||
}
|
||||
|
||||
func DialWithPrivateKey(SshUser string, PrivateKey []byte, SshHost string, SshPort int) (*ssh.Client, error) {
|
||||
address := fmt.Sprintf("%s:%d", SshHost, SshPort)
|
||||
config := &ssh.ClientConfig{
|
||||
User: SshUser,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
signer, err := ssh.ParsePrivateKey(PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Auth = []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
}
|
||||
return ssh.Dial("tcp", address, config)
|
||||
}
|
@ -33,12 +33,13 @@ type Webhook struct {
|
||||
|
||||
Organization string `xorm:"varchar(100) index" json:"organization"`
|
||||
|
||||
Url string `xorm:"varchar(100)" json:"url"`
|
||||
Url string `xorm:"varchar(200)" json:"url"`
|
||||
Method string `xorm:"varchar(100)" json:"method"`
|
||||
ContentType string `xorm:"varchar(100)" json:"contentType"`
|
||||
Headers []*Header `xorm:"mediumtext" json:"headers"`
|
||||
Events []string `xorm:"varchar(1000)" json:"events"`
|
||||
IsUserExtended bool `json:"isUserExtended"`
|
||||
SingleOrgOnly bool `json:"singleOrgOnly"`
|
||||
IsEnabled bool `json:"isEnabled"`
|
||||
}
|
||||
|
||||
|
@ -66,17 +66,27 @@ func getObject(ctx *context.Context) (string, string) {
|
||||
path := ctx.Request.URL.Path
|
||||
|
||||
if method == http.MethodGet {
|
||||
if ctx.Request.URL.Path == "/api/get-policies" && ctx.Input.Query("id") == "/" {
|
||||
adapterId := ctx.Input.Query("adapterId")
|
||||
if adapterId != "" {
|
||||
return util.GetOwnerAndNameFromIdNoCheck(adapterId)
|
||||
if ctx.Request.URL.Path == "/api/get-policies" {
|
||||
if ctx.Input.Query("id") == "/" {
|
||||
adapterId := ctx.Input.Query("adapterId")
|
||||
if adapterId != "" {
|
||||
return util.GetOwnerAndNameFromIdNoCheck(adapterId)
|
||||
}
|
||||
} else {
|
||||
// query == "?id=built-in/admin"
|
||||
id := ctx.Input.Query("id")
|
||||
if id != "" {
|
||||
return util.GetOwnerAndNameFromIdNoCheck(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// query == "?id=built-in/admin"
|
||||
id := ctx.Input.Query("id")
|
||||
if id != "" {
|
||||
return util.GetOwnerAndNameFromIdNoCheck(id)
|
||||
if !(strings.HasPrefix(ctx.Request.URL.Path, "/api/get-") && strings.HasSuffix(ctx.Request.URL.Path, "s")) {
|
||||
// query == "?id=built-in/admin"
|
||||
id := ctx.Input.Query("id")
|
||||
if id != "" {
|
||||
return util.GetOwnerAndNameFromIdNoCheck(id)
|
||||
}
|
||||
}
|
||||
|
||||
owner := ctx.Input.Query("owner")
|
||||
@ -164,6 +174,10 @@ func getUrlPath(urlPath string) string {
|
||||
return "/api/webauthn"
|
||||
}
|
||||
|
||||
if strings.HasPrefix(urlPath, "/api/saml/redirect") {
|
||||
return "/api/saml/redirect"
|
||||
}
|
||||
|
||||
return urlPath
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user