forked from casdoor/casdoor
Compare commits
140 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49c417c70e | ||
|
|
8b30e12915 | ||
|
|
2e18c65429 | ||
|
|
27c98bb056 | ||
|
|
4400b66862 | ||
|
|
e7e7d18ee7 | ||
|
|
66d1e28300 | ||
|
|
53782a6706 | ||
|
|
30bb0ce92f | ||
|
|
29f7dda858 | ||
|
|
68b82ed524 | ||
|
|
c4ce88198f | ||
|
|
a11fa23add | ||
|
|
add6ba32db | ||
|
|
37379dee13 | ||
|
|
2066670b76 | ||
|
|
e751148be2 | ||
|
|
c541d0bcdd | ||
|
|
f0db95d006 | ||
|
|
e4db367eaa | ||
|
|
9df81e3ffc | ||
|
|
048d6acc83 | ||
|
|
e440199977 | ||
|
|
cb4e559d51 | ||
|
|
4d1d0b95d6 | ||
|
|
9cc1133a96 | ||
|
|
897c28e8ad | ||
|
|
9d37a7e38e | ||
|
|
ea597296b4 | ||
|
|
427ddd215e | ||
|
|
24de79b100 | ||
|
|
9ab9c7c8e0 | ||
|
|
0728a9716b | ||
|
|
471570f24a | ||
|
|
2fa520844b | ||
|
|
2306acb416 | ||
|
|
d3f3f76290 | ||
|
|
fe93128495 | ||
|
|
7fd890ff14 | ||
|
|
83b56d7ceb | ||
|
|
503e5a75d2 | ||
|
|
5a607b4991 | ||
|
|
ca2dc2825d | ||
|
|
446d0b9047 | ||
|
|
ee708dbf48 | ||
|
|
221ca28488 | ||
|
|
e93d3f6c13 | ||
|
|
e285396d4e | ||
|
|
10320bb49f | ||
|
|
4d27ebd82a | ||
|
|
6d5e6dab0a | ||
|
|
e600ea7efd | ||
|
|
8002613398 | ||
|
|
a48b1d0c73 | ||
|
|
d8b5ecba36 | ||
|
|
e3a8a464d5 | ||
|
|
a575ba02d6 | ||
|
|
a9fcfceb8f | ||
|
|
712482ffb9 | ||
|
|
84e2c760d9 | ||
|
|
4ab85d6781 | ||
|
|
2ede56ac46 | ||
|
|
6a819a9a20 | ||
|
|
ddaeac46e8 | ||
|
|
f9d061d905 | ||
|
|
5e550e4364 | ||
|
|
146d54d6f6 | ||
|
|
1df15a2706 | ||
|
|
f7d73bbfdd | ||
|
|
a8b7217348 | ||
|
|
40a3b19cee | ||
|
|
98b45399a7 | ||
|
|
90edb7ab6b | ||
|
|
e21b995eca | ||
|
|
81221f07f0 | ||
|
|
5fc2cdf637 | ||
|
|
5e852e0121 | ||
|
|
513ac6ffe9 | ||
|
|
821ba5673d | ||
|
|
d3ee73e48c | ||
|
|
1d719e3759 | ||
|
|
b3355a9fa6 | ||
|
|
ccc88cdafb | ||
|
|
abf328bbe5 | ||
|
|
5530253d38 | ||
|
|
4cef6c5f3f | ||
|
|
7e6929b900 | ||
|
|
46ae1a9580 | ||
|
|
37e22f3e2c | ||
|
|
68cde65d84 | ||
|
|
1c7f5fdfe4 | ||
|
|
1a5be46325 | ||
|
|
f7bafb28d6 | ||
|
|
6f815aefdf | ||
|
|
eb49f29529 | ||
|
|
5ad4e6aac0 | ||
|
|
3c28a2202d | ||
|
|
0a9a9117e5 | ||
|
|
f3ee1f83fe | ||
|
|
171af2901c | ||
|
|
2ded293e10 | ||
|
|
a1c6d6c6cf | ||
|
|
bf42176708 | ||
|
|
23a45c1d33 | ||
|
|
6894ca407e | ||
|
|
d288ecf6ed | ||
|
|
0a04174ec8 | ||
|
|
3feb723abf | ||
|
|
ff8b8fb631 | ||
|
|
df38c0dd62 | ||
|
|
93e87e009e | ||
|
|
f0a4ccbc3c | ||
|
|
f17c8622f7 | ||
|
|
09698b0714 | ||
|
|
1d913677a0 | ||
|
|
f3b00fb431 | ||
|
|
c95a427635 | ||
|
|
778be62bae | ||
|
|
5574c6ad0d | ||
|
|
36db852a32 | ||
|
|
8ee8767882 | ||
|
|
af5a9c805d | ||
|
|
f8e5fedf8b | ||
|
|
962a4970f4 | ||
|
|
d239b3f0cb | ||
|
|
0df467ce5e | ||
|
|
3d5356a1f0 | ||
|
|
1824762e00 | ||
|
|
a533212d8a | ||
|
|
53e1813dc8 | ||
|
|
ba95c7ffb0 | ||
|
|
10105de418 | ||
|
|
9582163bdd | ||
|
|
cc7408e976 | ||
|
|
d67d714105 | ||
|
|
0aab27f154 | ||
|
|
212090325b | ||
|
|
b24e43c736 | ||
|
|
1728bf01ac | ||
|
|
86a7a87c57 |
27
.github/workflows/build.yml
vendored
27
.github/workflows/build.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '^1.16.5'
|
||||
go-version: '1.23'
|
||||
cache-dependency-path: ./go.mod
|
||||
- name: Tests
|
||||
run: |
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '^1.16.5'
|
||||
go-version: '1.23'
|
||||
cache-dependency-path: ./go.mod
|
||||
- run: go version
|
||||
- name: Build
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '^1.16.5'
|
||||
go-version: '1.23'
|
||||
cache: false
|
||||
|
||||
# gen a dummy config file
|
||||
@@ -98,11 +98,28 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '^1.16.5'
|
||||
go-version: '1.23'
|
||||
cache-dependency-path: ./go.mod
|
||||
- name: start backend
|
||||
run: nohup go run ./main.go &
|
||||
run: nohup go run ./main.go > /tmp/backend.log 2>&1 &
|
||||
working-directory: ./
|
||||
- name: Wait for backend to be ready
|
||||
run: |
|
||||
echo "Waiting for backend server to start on port 8000..."
|
||||
for i in {1..60}; do
|
||||
if curl -s http://localhost:8000 > /dev/null 2>&1; then
|
||||
echo "Backend is ready!"
|
||||
break
|
||||
fi
|
||||
if [ $i -eq 60 ]; then
|
||||
echo "Backend failed to start within 60 seconds"
|
||||
echo "Backend logs:"
|
||||
cat /tmp/backend.log || echo "No backend logs available"
|
||||
exit 1
|
||||
fi
|
||||
echo "Waiting... ($i/60)"
|
||||
sleep 1
|
||||
done
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
@@ -4,7 +4,7 @@ COPY ./web .
|
||||
RUN yarn install --frozen-lockfile --network-timeout 1000000 && NODE_OPTIONS="--max-old-space-size=4096" yarn run build
|
||||
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:1.21.13 AS BACK
|
||||
FROM --platform=$BUILDPLATFORM golang:1.23.12 AS BACK
|
||||
WORKDIR /go/src/casdoor
|
||||
COPY . .
|
||||
RUN ./build.sh
|
||||
|
||||
@@ -46,6 +46,8 @@ p, *, *, POST, /api/login, *, *
|
||||
p, *, *, GET, /api/get-app-login, *, *
|
||||
p, *, *, POST, /api/logout, *, *
|
||||
p, *, *, GET, /api/logout, *, *
|
||||
p, *, *, POST, /api/sso-logout, *, *
|
||||
p, *, *, GET, /api/sso-logout, *, *
|
||||
p, *, *, POST, /api/callback, *, *
|
||||
p, *, *, POST, /api/device-auth, *, *
|
||||
p, *, *, GET, /api/get-account, *, *
|
||||
@@ -65,7 +67,6 @@ p, *, *, POST, /api/upload-users, *, *
|
||||
p, *, *, GET, /api/get-resources, *, *
|
||||
p, *, *, GET, /api/get-records, *, *
|
||||
p, *, *, GET, /api/get-product, *, *
|
||||
p, *, *, POST, /api/buy-product, *, *
|
||||
p, *, *, GET, /api/get-payment, *, *
|
||||
p, *, *, POST, /api/update-payment, *, *
|
||||
p, *, *, POST, /api/invoice-payment, *, *
|
||||
@@ -98,6 +99,8 @@ p, *, *, *, /api/metrics, *, *
|
||||
p, *, *, GET, /api/get-pricing, *, *
|
||||
p, *, *, GET, /api/get-plan, *, *
|
||||
p, *, *, GET, /api/get-subscription, *, *
|
||||
p, *, *, GET, /api/get-transactions, *, *
|
||||
p, *, *, GET, /api/get-transaction, *, *
|
||||
p, *, *, GET, /api/get-provider, *, *
|
||||
p, *, *, GET, /api/get-organization-names, *, *
|
||||
p, *, *, GET, /api/get-all-objects, *, *
|
||||
@@ -173,7 +176,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/") || urlPath == "/api/webhook" || urlPath == "/api/get-qrcode" || urlPath == "/api/refresh-engines" {
|
||||
if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/sso-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" || urlPath == "/api/refresh-engines" {
|
||||
return true
|
||||
} else if urlPath == "/api/update-user" {
|
||||
// Allow ordinary users to update their own information
|
||||
@@ -181,7 +184,7 @@ func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else if urlPath == "/api/upload-resource" {
|
||||
} else if urlPath == "/api/upload-resource" || urlPath == "/api/add-transaction" {
|
||||
if subOwner == "app" && subName == "app-casibase" {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ func (c *ApiController) Signup() {
|
||||
|
||||
userType := "normal-user"
|
||||
if authForm.Plan != "" && authForm.Pricing != "" {
|
||||
err = object.CheckPricingAndPlan(authForm.Organization, authForm.Pricing, authForm.Plan)
|
||||
err = object.CheckPricingAndPlan(authForm.Organization, authForm.Pricing, authForm.Plan, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -218,7 +218,7 @@ func (c *ApiController) Signup() {
|
||||
Tag: authForm.Tag,
|
||||
Education: authForm.Education,
|
||||
Avatar: organization.DefaultAvatar,
|
||||
Email: authForm.Email,
|
||||
Email: strings.ToLower(authForm.Email),
|
||||
Phone: authForm.Phone,
|
||||
CountryCode: authForm.CountryCode,
|
||||
Address: []string{},
|
||||
@@ -290,6 +290,8 @@ func (c *ApiController) Signup() {
|
||||
|
||||
if user.Type == "normal-user" {
|
||||
c.SetSessionUsername(user.GetId())
|
||||
} else if user.Type == "paid-user" {
|
||||
c.SetSession("paidUsername", user.GetId())
|
||||
}
|
||||
|
||||
if authForm.Email != "" {
|
||||
@@ -343,8 +345,12 @@ 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())
|
||||
owner, username, err := util.GetOwnerAndNameFromIdWithError(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -391,7 +397,11 @@ func (c *ApiController) Logout() {
|
||||
c.ClearUserSession()
|
||||
c.ClearTokenSession()
|
||||
// TODO https://github.com/casdoor/casdoor/pull/1494#discussion_r1095675265
|
||||
owner, username := util.GetOwnerAndNameFromId(user)
|
||||
owner, username, err := util.GetOwnerAndNameFromIdWithError(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID())
|
||||
if err != nil {
|
||||
@@ -423,6 +433,77 @@ func (c *ApiController) Logout() {
|
||||
}
|
||||
}
|
||||
|
||||
// SsoLogout
|
||||
// @Title SsoLogout
|
||||
// @Tag Login API
|
||||
// @Description logout the current user from all applications
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /sso-logout [get,post]
|
||||
func (c *ApiController) SsoLogout() {
|
||||
user := c.GetSessionUsername()
|
||||
|
||||
if user == "" {
|
||||
c.ResponseOk()
|
||||
return
|
||||
}
|
||||
|
||||
c.ClearUserSession()
|
||||
c.ClearTokenSession()
|
||||
owner, username, err := util.GetOwnerAndNameFromIdWithError(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = object.ExpireTokenByUser(owner, username)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
sessions, err := object.GetUserSessions(owner, username)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var sessionIds []string
|
||||
for _, session := range sessions {
|
||||
sessionIds = append(sessionIds, session.SessionId...)
|
||||
}
|
||||
object.DeleteBeegoSession(sessionIds)
|
||||
|
||||
_, err = object.DeleteAllUserSessions(owner, username)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Send SSO logout notifications to all notification providers in the user's signup application
|
||||
userObj, err := object.GetUser(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if userObj != nil {
|
||||
err = object.SendSsoLogoutNotifications(userObj)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
util.LogInfo(c.Ctx, "API: [%s] logged out from all applications", user)
|
||||
|
||||
c.ResponseOk()
|
||||
}
|
||||
|
||||
// GetAccount
|
||||
// @Title GetAccount
|
||||
// @Tag Account API
|
||||
|
||||
@@ -237,7 +237,7 @@ func (c *ApiController) UpdateApplication() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application, c.IsGlobalAdmin()))
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application, c.IsGlobalAdmin(), c.GetAcceptLanguage()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"), user.Name, application.Name))
|
||||
return
|
||||
} else {
|
||||
c.SetSession("paidUsername", user.GetId())
|
||||
// let the paid-user select plan
|
||||
c.ResponseOk("SelectPlan", pricing)
|
||||
return
|
||||
@@ -723,6 +724,7 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
userInfo := &idp.UserInfo{}
|
||||
var token *oauth2.Token
|
||||
if provider.Category == "SAML" {
|
||||
// SAML
|
||||
userInfo, err = object.ParseSamlResponse(authForm.SamlResponse, provider, c.Ctx.Request.Host)
|
||||
@@ -753,7 +755,6 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
// https://github.com/golang/oauth2/issues/123#issuecomment-103715338
|
||||
var token *oauth2.Token
|
||||
token, err = idProvider.GetToken(authForm.Code)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -803,7 +804,7 @@ func (c *ApiController) Login() {
|
||||
if user != nil && !user.IsDeleted {
|
||||
// Sign in via OAuth (want to sign up but already have account)
|
||||
// sync info from 3rd-party if possible
|
||||
_, err = object.SetUserOAuthProperties(organization, user, provider.Type, userInfo, provider.UserMapping)
|
||||
_, err = object.SetUserOAuthProperties(organization, user, provider.Type, userInfo, token, provider.UserMapping)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -866,6 +867,11 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle UseEmailAsUsername for OAuth and Web3
|
||||
if organization.UseEmailAsUsername && userInfo.Email != "" {
|
||||
userInfo.Username = userInfo.Email
|
||||
}
|
||||
|
||||
// Handle username conflicts
|
||||
var tmpUser *object.User
|
||||
tmpUser, err = object.GetUser(util.GetId(application.Organization, userInfo.Username))
|
||||
@@ -948,7 +954,7 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
// sync info from 3rd-party if possible
|
||||
_, err = object.SetUserOAuthProperties(organization, user, provider.Type, userInfo, provider.UserMapping)
|
||||
_, err = object.SetUserOAuthProperties(organization, user, provider.Type, userInfo, token, provider.UserMapping)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -996,7 +1002,7 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
// sync info from 3rd-party if possible
|
||||
_, err = object.SetUserOAuthProperties(organization, user, provider.Type, userInfo, provider.UserMapping)
|
||||
_, err = object.SetUserOAuthProperties(organization, user, provider.Type, userInfo, token, provider.UserMapping)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -1212,7 +1218,7 @@ func (c *ApiController) HandleOfficialAccountEvent() {
|
||||
return
|
||||
}
|
||||
if data.Ticket == "" {
|
||||
c.ResponseError(err.Error())
|
||||
c.ResponseError("empty ticket")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1222,10 +1228,11 @@ func (c *ApiController) HandleOfficialAccountEvent() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if data.Ticket == "" {
|
||||
c.ResponseError("empty ticket")
|
||||
if provider == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The provider: %s does not exist"), providerId))
|
||||
return
|
||||
}
|
||||
|
||||
if !idp.VerifyWechatSignature(provider.Content, nonce, timestamp, signature) {
|
||||
c.ResponseError("invalid signature")
|
||||
return
|
||||
@@ -1277,6 +1284,11 @@ func (c *ApiController) GetQRCode() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if provider == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The provider: %s does not exist"), providerId))
|
||||
return
|
||||
}
|
||||
|
||||
code, ticket, err := idp.GetWechatOfficialAccountQRCode(provider.ClientId2, provider.ClientSecret2, providerId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
|
||||
@@ -122,6 +122,26 @@ func (c *ApiController) GetSessionUsername() string {
|
||||
return user.(string)
|
||||
}
|
||||
|
||||
// GetPaidUsername ...
|
||||
func (c *ApiController) GetPaidUsername() string {
|
||||
// check if user session expired
|
||||
sessionData := c.GetSessionData()
|
||||
|
||||
if sessionData != nil &&
|
||||
sessionData.ExpireTime != 0 &&
|
||||
sessionData.ExpireTime < time.Now().Unix() {
|
||||
c.ClearUserSession()
|
||||
return ""
|
||||
}
|
||||
|
||||
user := c.GetSession("paidUsername")
|
||||
if user == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return user.(string)
|
||||
}
|
||||
|
||||
func (c *ApiController) GetSessionToken() string {
|
||||
accessToken := c.GetSession("accessToken")
|
||||
if accessToken == nil {
|
||||
|
||||
@@ -119,7 +119,11 @@ func (c *ApiController) Enforce() {
|
||||
|
||||
permissions := []*object.Permission{}
|
||||
if modelId != "" {
|
||||
owner, modelName := util.GetOwnerAndNameFromId(modelId)
|
||||
owner, modelName, err := util.GetOwnerAndNameFromIdWithError(modelId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
permissions, err = object.GetPermissionsByModel(owner, modelName)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -255,7 +259,11 @@ func (c *ApiController) BatchEnforce() {
|
||||
|
||||
permissions := []*object.Permission{}
|
||||
if modelId != "" {
|
||||
owner, modelName := util.GetOwnerAndNameFromId(modelId)
|
||||
owner, modelName, err := util.GetOwnerAndNameFromIdWithError(modelId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
permissions, err = object.GetPermissionsByModel(owner, modelName)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -38,6 +39,46 @@ var (
|
||||
cliVersionMutex sync.RWMutex
|
||||
)
|
||||
|
||||
// cleanOldMEIFolders cleans up old _MEIXXX folders from the Casdoor temp directory
|
||||
// that are older than 24 hours. These folders are created by PyInstaller when
|
||||
// executing casbin-python-cli and can accumulate over time.
|
||||
func cleanOldMEIFolders() {
|
||||
tempDir := "temp"
|
||||
cutoffTime := time.Now().Add(-24 * time.Hour)
|
||||
|
||||
entries, err := os.ReadDir(tempDir)
|
||||
if err != nil {
|
||||
// Log error but don't fail - cleanup is best-effort
|
||||
// This is expected if temp directory doesn't exist yet
|
||||
return
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
// Check if the entry is a directory and matches the _MEI pattern
|
||||
if !entry.IsDir() || !strings.HasPrefix(entry.Name(), "_MEI") {
|
||||
continue
|
||||
}
|
||||
|
||||
dirPath := filepath.Join(tempDir, entry.Name())
|
||||
info, err := entry.Info()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if the folder is older than 24 hours
|
||||
if info.ModTime().Before(cutoffTime) {
|
||||
// Try to remove the directory
|
||||
err = os.RemoveAll(dirPath)
|
||||
if err != nil {
|
||||
// Log but continue with other folders
|
||||
fmt.Printf("failed to remove old MEI folder %s: %v\n", dirPath, err)
|
||||
} else {
|
||||
fmt.Printf("removed old MEI folder: %s\n", dirPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getCLIVersion
|
||||
// @Title getCLIVersion
|
||||
// @Description Get CLI version with cache mechanism
|
||||
@@ -66,6 +107,9 @@ func getCLIVersion(language string) (string, error) {
|
||||
}
|
||||
cliVersionMutex.RUnlock()
|
||||
|
||||
// Clean up old _MEI folders before running the command
|
||||
cleanOldMEIFolders()
|
||||
|
||||
cmd := exec.Command(binaryName, "--version")
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@@ -186,6 +230,10 @@ func (c *ApiController) RunCasbinCommand() {
|
||||
return
|
||||
}
|
||||
|
||||
// Clean up old _MEI folders before running the command
|
||||
// This is especially important for Python CLI which creates these folders
|
||||
cleanOldMEIFolders()
|
||||
|
||||
command := exec.Command(binaryName, processedArgs...)
|
||||
outputBytes, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
|
||||
@@ -84,10 +84,12 @@ func (c *ApiController) GetEnforcer() {
|
||||
return
|
||||
}
|
||||
|
||||
if loadModelCfg == "true" && enforcer.Model != "" {
|
||||
err := enforcer.LoadModelCfg()
|
||||
if err != nil {
|
||||
return
|
||||
if enforcer != nil {
|
||||
if loadModelCfg == "true" && enforcer.Model != "" {
|
||||
err = enforcer.LoadModelCfg()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,11 @@ import (
|
||||
|
||||
func (c *ApiController) UploadGroups() {
|
||||
userId := c.GetSessionUsername()
|
||||
owner, user := util.GetOwnerAndNameFromId(userId)
|
||||
owner, user, err := util.GetOwnerAndNameFromIdWithError(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
file, header, err := c.Ctx.Request.FormFile("file")
|
||||
if err != nil {
|
||||
|
||||
@@ -102,6 +102,10 @@ func (c *ApiController) GetInvitationCodeInfo() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The application: %s does not exist"), applicationId))
|
||||
return
|
||||
}
|
||||
|
||||
invitation, msg := object.GetInvitationByCode(code, application.Organization, c.GetAcceptLanguage())
|
||||
if msg != "" {
|
||||
@@ -225,6 +229,10 @@ func (c *ApiController) SendInvitation() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if organization == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The organization: %s does not exist"), invitation.Owner))
|
||||
return
|
||||
}
|
||||
|
||||
application, err := object.GetApplicationByOrganizationName(invitation.Owner)
|
||||
if err != nil {
|
||||
|
||||
@@ -16,6 +16,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
@@ -47,12 +48,20 @@ type LdapSyncResp struct {
|
||||
func (c *ApiController) GetLdapUsers() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
_, ldapId := util.GetOwnerAndNameFromId(id)
|
||||
_, ldapId, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
ldapServer, err := object.GetLdap(ldapId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if ldapServer == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The LDAP: %s does not exist"), ldapId))
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := ldapServer.GetLdapConn()
|
||||
if err != nil {
|
||||
@@ -125,7 +134,11 @@ func (c *ApiController) GetLdap() {
|
||||
return
|
||||
}
|
||||
|
||||
_, name := util.GetOwnerAndNameFromId(id)
|
||||
_, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
ldap, err := object.GetLdap(name)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -255,9 +268,13 @@ func (c *ApiController) DeleteLdap() {
|
||||
func (c *ApiController) SyncLdapUsers() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
owner, ldapId := util.GetOwnerAndNameFromId(id)
|
||||
owner, ldapId, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
var users []object.LdapUser
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &users)
|
||||
err = json.Unmarshal(c.Ctx.Input.RequestBody, &users)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
||||
@@ -124,6 +124,28 @@ func (c *ApiController) MfaSetupVerify() {
|
||||
return
|
||||
}
|
||||
config.Secret = dest
|
||||
} else if mfaType == object.RadiusType {
|
||||
if dest == "" {
|
||||
c.ResponseError("RADIUS username is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = dest
|
||||
if secret == "" {
|
||||
c.ResponseError("RADIUS provider is missing")
|
||||
return
|
||||
}
|
||||
config.URL = secret
|
||||
} else if mfaType == object.PushType {
|
||||
if dest == "" {
|
||||
c.ResponseError("push notification receiver is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = dest
|
||||
if secret == "" {
|
||||
c.ResponseError("push notification provider is missing")
|
||||
return
|
||||
}
|
||||
config.URL = secret
|
||||
}
|
||||
|
||||
mfaUtil := object.GetMfaUtil(mfaType, config)
|
||||
@@ -200,6 +222,28 @@ func (c *ApiController) MfaSetupEnable() {
|
||||
}
|
||||
user.CountryCode = countryCode
|
||||
}
|
||||
} else if mfaType == object.RadiusType {
|
||||
if dest == "" {
|
||||
c.ResponseError("RADIUS username is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = dest
|
||||
if secret == "" {
|
||||
c.ResponseError("RADIUS provider is missing")
|
||||
return
|
||||
}
|
||||
config.URL = secret
|
||||
} else if mfaType == object.PushType {
|
||||
if dest == "" {
|
||||
c.ResponseError("push notification receiver is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = dest
|
||||
if secret == "" {
|
||||
c.ResponseError("push notification provider is missing")
|
||||
return
|
||||
}
|
||||
config.URL = secret
|
||||
}
|
||||
|
||||
if recoveryCodes == "" {
|
||||
|
||||
166
controllers/order.go
Normal file
166
controllers/order.go
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright 2025 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"
|
||||
)
|
||||
|
||||
// GetOrders
|
||||
// @Title GetOrders
|
||||
// @Tag Order API
|
||||
// @Description get orders
|
||||
// @Param owner query string true "The owner of orders"
|
||||
// @Success 200 {array} object.Order The Response object
|
||||
// @router /get-orders [get]
|
||||
func (c *ApiController) GetOrders() {
|
||||
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 == "" {
|
||||
orders, err := object.GetOrders(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(orders)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetOrderCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
orders, err := object.GetPaginationOrders(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(orders, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserOrders
|
||||
// @Title GetUserOrders
|
||||
// @Tag Order API
|
||||
// @Description get orders for a user
|
||||
// @Param owner query string true "The owner of orders"
|
||||
// @Param user query string true "The username of the user"
|
||||
// @Success 200 {array} object.Order The Response object
|
||||
// @router /get-user-orders [get]
|
||||
func (c *ApiController) GetUserOrders() {
|
||||
owner := c.Input().Get("owner")
|
||||
user := c.Input().Get("user")
|
||||
|
||||
orders, err := object.GetUserOrders(owner, user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(orders)
|
||||
}
|
||||
|
||||
// GetOrder
|
||||
// @Title GetOrder
|
||||
// @Tag Order API
|
||||
// @Description get order
|
||||
// @Param id query string true "The id ( owner/name ) of the order"
|
||||
// @Success 200 {object} object.Order The Response object
|
||||
// @router /get-order [get]
|
||||
func (c *ApiController) GetOrder() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
order, err := object.GetOrder(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(order)
|
||||
}
|
||||
|
||||
// UpdateOrder
|
||||
// @Title UpdateOrder
|
||||
// @Tag Order API
|
||||
// @Description update order
|
||||
// @Param id query string true "The id ( owner/name ) of the order"
|
||||
// @Param body body object.Order true "The details of the order"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-order [post]
|
||||
func (c *ApiController) UpdateOrder() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var order object.Order
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &order)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateOrder(id, &order))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddOrder
|
||||
// @Title AddOrder
|
||||
// @Tag Order API
|
||||
// @Description add order
|
||||
// @Param body body object.Order true "The details of the order"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-order [post]
|
||||
func (c *ApiController) AddOrder() {
|
||||
var order object.Order
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &order)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddOrder(&order))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteOrder
|
||||
// @Title DeleteOrder
|
||||
// @Tag Order API
|
||||
// @Description delete order
|
||||
// @Param body body object.Order true "The details of the order"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-order [post]
|
||||
func (c *ApiController) DeleteOrder() {
|
||||
var order object.Order
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &order)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteOrder(&order))
|
||||
c.ServeJSON()
|
||||
}
|
||||
169
controllers/order_pay.go
Normal file
169
controllers/order_pay.go
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright 2025 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 (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// PlaceOrder
|
||||
// @Title PlaceOrder
|
||||
// @Tag Order API
|
||||
// @Description place an order for a product
|
||||
// @Param productId query string true "The id ( owner/name ) of the product"
|
||||
// @Param pricingName query string false "The name of the pricing (for subscription)"
|
||||
// @Param planName query string false "The name of the plan (for subscription)"
|
||||
// @Param customPrice query number false "Custom price for recharge products"
|
||||
// @Param userName query string false "The username to place order for (admin only)"
|
||||
// @Success 200 {object} object.Order The Response object
|
||||
// @router /place-order [post]
|
||||
func (c *ApiController) PlaceOrder() {
|
||||
productId := c.Input().Get("productId")
|
||||
pricingName := c.Input().Get("pricingName")
|
||||
planName := c.Input().Get("planName")
|
||||
customPriceStr := c.Input().Get("customPrice")
|
||||
paidUserName := c.Input().Get("userName")
|
||||
|
||||
if productId == "" {
|
||||
c.ResponseError(c.T("general:ProductId is required"))
|
||||
return
|
||||
}
|
||||
|
||||
var customPrice float64
|
||||
if customPriceStr != "" {
|
||||
var err error
|
||||
customPrice, err = strconv.ParseFloat(customPriceStr, 64)
|
||||
if err != nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:Invalid customPrice: %s"), customPriceStr))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
owner, _, err := util.GetOwnerAndNameFromIdWithError(productId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var userId string
|
||||
if paidUserName != "" {
|
||||
userId = util.GetId(owner, paidUserName)
|
||||
if userId != c.GetSessionUsername() && !c.IsAdmin() && userId != c.GetPaidUsername() {
|
||||
c.ResponseError(c.T("general:Only admin user can specify user"))
|
||||
return
|
||||
}
|
||||
|
||||
c.SetSession("paidUsername", "")
|
||||
} else {
|
||||
userId = c.GetSessionUsername()
|
||||
}
|
||||
|
||||
if userId == "" {
|
||||
c.ResponseError(c.T("general:Please login first"))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
|
||||
return
|
||||
}
|
||||
|
||||
order, err := object.PlaceOrder(productId, user, pricingName, planName, customPrice)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(order)
|
||||
}
|
||||
|
||||
// PayOrder
|
||||
// @Title PayOrder
|
||||
// @Tag Order API
|
||||
// @Description pay an existing order
|
||||
// @Param id query string true "The id ( owner/name ) of the order"
|
||||
// @Param providerName query string true "The name of the provider"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /pay-order [post]
|
||||
func (c *ApiController) PayOrder() {
|
||||
id := c.Input().Get("id")
|
||||
host := c.Ctx.Request.Host
|
||||
providerName := c.Input().Get("providerName")
|
||||
paymentEnv := c.Input().Get("paymentEnv")
|
||||
|
||||
order, err := object.GetOrder(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if order == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The order: %s does not exist"), id))
|
||||
return
|
||||
}
|
||||
|
||||
userId := c.GetSessionUsername()
|
||||
orderUserId := util.GetId(order.Owner, order.User)
|
||||
if userId != orderUserId && !c.IsAdmin() {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return
|
||||
}
|
||||
|
||||
payment, attachInfo, err := object.PayOrder(providerName, host, paymentEnv, order)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(payment, attachInfo)
|
||||
}
|
||||
|
||||
// CancelOrder
|
||||
// @Title CancelOrder
|
||||
// @Tag Order API
|
||||
// @Description cancel an order
|
||||
// @Param id query string true "The id ( owner/name ) of the order"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /cancel-order [post]
|
||||
func (c *ApiController) CancelOrder() {
|
||||
id := c.Input().Get("id")
|
||||
order, err := object.GetOrder(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if order == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The order: %s does not exist"), id))
|
||||
return
|
||||
}
|
||||
|
||||
userId := c.GetSessionUsername()
|
||||
orderUserId := util.GetId(order.Owner, order.User)
|
||||
if userId != orderUserId && !c.IsAdmin() {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.CancelOrder(order))
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -130,6 +130,10 @@ func (c *ApiController) UpdateOrganization() {
|
||||
|
||||
isGlobalAdmin, _ := c.isGlobalAdmin()
|
||||
|
||||
if organization.BalanceCurrency == "" {
|
||||
organization.BalanceCurrency = "USD"
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateOrganization(id, &organization, isGlobalAdmin))
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -165,6 +169,10 @@ func (c *ApiController) AddOrganization() {
|
||||
return
|
||||
}
|
||||
|
||||
if organization.BalanceCurrency == "" {
|
||||
organization.BalanceCurrency = "USD"
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddOrganization(&organization))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@@ -24,7 +24,11 @@ import (
|
||||
|
||||
func (c *ApiController) UploadPermissions() {
|
||||
userId := c.GetSessionUsername()
|
||||
owner, user := util.GetOwnerAndNameFromId(userId)
|
||||
owner, user, err := util.GetOwnerAndNameFromIdWithError(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
file, header, err := c.Ctx.Request.FormFile("file")
|
||||
if err != nil {
|
||||
|
||||
@@ -16,8 +16,6 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/beego/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
@@ -151,64 +149,3 @@ func (c *ApiController) DeleteProduct() {
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteProduct(&product))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// BuyProduct
|
||||
// @Title BuyProduct
|
||||
// @Tag Product API
|
||||
// @Description buy product
|
||||
// @Param id query string true "The id ( owner/name ) of the product"
|
||||
// @Param providerName query string true "The name of the provider"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /buy-product [post]
|
||||
func (c *ApiController) BuyProduct() {
|
||||
id := c.Input().Get("id")
|
||||
host := c.Ctx.Request.Host
|
||||
providerName := c.Input().Get("providerName")
|
||||
paymentEnv := c.Input().Get("paymentEnv")
|
||||
customPriceStr := c.Input().Get("customPrice")
|
||||
if customPriceStr == "" {
|
||||
customPriceStr = "0"
|
||||
}
|
||||
|
||||
customPrice, err := strconv.ParseFloat(customPriceStr, 64)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// buy `pricingName/planName` for `paidUserName`
|
||||
pricingName := c.Input().Get("pricingName")
|
||||
planName := c.Input().Get("planName")
|
||||
paidUserName := c.Input().Get("userName")
|
||||
owner, _ := util.GetOwnerAndNameFromId(id)
|
||||
userId := util.GetId(owner, paidUserName)
|
||||
if paidUserName != "" && paidUserName != c.GetSessionUsername() && !c.IsAdmin() {
|
||||
c.ResponseError(c.T("general:Only admin user can specify user"))
|
||||
return
|
||||
}
|
||||
if paidUserName == "" {
|
||||
userId = c.GetSessionUsername()
|
||||
}
|
||||
if userId == "" {
|
||||
c.ResponseError(c.T("general:Please login first"))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
|
||||
return
|
||||
}
|
||||
|
||||
payment, attachInfo, err := object.BuyProduct(id, user, providerName, pricingName, planName, host, paymentEnv, customPrice)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(payment, attachInfo)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// GetPrometheusInfo
|
||||
@@ -37,3 +38,17 @@ func (c *ApiController) GetPrometheusInfo() {
|
||||
|
||||
c.ResponseOk(prometheusInfo)
|
||||
}
|
||||
|
||||
// GetMetrics
|
||||
// @Title GetMetrics
|
||||
// @Tag System API
|
||||
// @Description get Prometheus metrics
|
||||
// @Success 200 {string} Prometheus metrics in text format
|
||||
// @router /metrics [get]
|
||||
func (c *ApiController) GetMetrics() {
|
||||
_, ok := c.RequireAdmin()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
promhttp.Handler().ServeHTTP(c.Ctx.ResponseWriter, c.Ctx.Request)
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ func (c *ApiController) UploadResource() {
|
||||
}
|
||||
|
||||
applicationObj.TermsOfUse = fileUrl
|
||||
_, err = object.UpdateApplication(applicationId, applicationObj, true)
|
||||
_, err = object.UpdateApplication(applicationId, applicationObj, true, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
||||
@@ -24,7 +24,11 @@ import (
|
||||
|
||||
func (c *ApiController) UploadRoles() {
|
||||
userId := c.GetSessionUsername()
|
||||
owner, user := util.GetOwnerAndNameFromId(userId)
|
||||
owner, user, err := util.GetOwnerAndNameFromIdWithError(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
file, header, err := c.Ctx.Request.FormFile("file")
|
||||
if err != nil {
|
||||
|
||||
@@ -16,6 +16,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/beego/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
@@ -103,7 +104,7 @@ func (c *ApiController) UpdateSyncer() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateSyncer(id, &syncer, c.IsGlobalAdmin()))
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateSyncer(id, &syncer, c.IsGlobalAdmin(), c.GetAcceptLanguage()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@@ -159,6 +160,10 @@ func (c *ApiController) RunSyncer() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if syncer == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The syncer: %s does not exist"), id))
|
||||
return
|
||||
}
|
||||
|
||||
err = object.RunSyncer(syncer)
|
||||
if err != nil {
|
||||
@@ -177,7 +182,7 @@ func (c *ApiController) TestSyncerDb() {
|
||||
return
|
||||
}
|
||||
|
||||
err = object.TestSyncerDb(syncer)
|
||||
err = object.TestSyncer(syncer)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
||||
@@ -39,7 +39,26 @@ func (c *ApiController) GetTransactions() {
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
transactions, err := object.GetTransactions(owner)
|
||||
var transactions []*object.Transaction
|
||||
var err error
|
||||
|
||||
if c.IsAdmin() {
|
||||
// If field is "user", filter by that user even for admins
|
||||
if field == "user" && value != "" {
|
||||
transactions, err = object.GetUserTransactions(owner, value)
|
||||
} else {
|
||||
transactions, err = object.GetTransactions(owner)
|
||||
}
|
||||
} else {
|
||||
user := c.GetSessionUsername()
|
||||
_, userName, userErr := util.GetOwnerAndNameFromIdWithError(user)
|
||||
if userErr != nil {
|
||||
c.ResponseError(userErr.Error())
|
||||
return
|
||||
}
|
||||
transactions, err = object.GetUserTransactions(owner, userName)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -48,6 +67,19 @@ func (c *ApiController) GetTransactions() {
|
||||
c.ResponseOk(transactions)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
|
||||
// Apply user filter for non-admin users
|
||||
if !c.IsAdmin() {
|
||||
user := c.GetSessionUsername()
|
||||
_, userName, userErr := util.GetOwnerAndNameFromIdWithError(user)
|
||||
if userErr != nil {
|
||||
c.ResponseError(userErr.Error())
|
||||
return
|
||||
}
|
||||
field = "user"
|
||||
value = userName
|
||||
}
|
||||
|
||||
count, err := object.GetTransactionCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -65,28 +97,6 @@ func (c *ApiController) GetTransactions() {
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -103,6 +113,27 @@ func (c *ApiController) GetTransaction() {
|
||||
return
|
||||
}
|
||||
|
||||
if transaction == nil {
|
||||
c.ResponseOk(nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if non-admin user is trying to access someone else's transaction
|
||||
if !c.IsAdmin() {
|
||||
user := c.GetSessionUsername()
|
||||
_, userName, userErr := util.GetOwnerAndNameFromIdWithError(user)
|
||||
if userErr != nil {
|
||||
c.ResponseError(userErr.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Only allow users to view their own transactions
|
||||
if transaction.User != userName {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.ResponseOk(transaction)
|
||||
}
|
||||
|
||||
@@ -124,7 +155,7 @@ func (c *ApiController) UpdateTransaction() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateTransaction(id, &transaction))
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateTransaction(id, &transaction, c.GetAcceptLanguage()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@@ -133,6 +164,7 @@ func (c *ApiController) UpdateTransaction() {
|
||||
// @Tag Transaction API
|
||||
// @Description add transaction
|
||||
// @Param body body object.Transaction true "The details of the transaction"
|
||||
// @Param dryRun query string false "Dry run mode: set to 'true' or '1' to validate without committing"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-transaction [post]
|
||||
func (c *ApiController) AddTransaction() {
|
||||
@@ -143,8 +175,22 @@ func (c *ApiController) AddTransaction() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddTransaction(&transaction))
|
||||
c.ServeJSON()
|
||||
dryRunParam := c.Input().Get("dryRun")
|
||||
dryRun := dryRunParam != ""
|
||||
|
||||
affected, transactionId, err := object.AddTransaction(&transaction, c.GetAcceptLanguage(), dryRun)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !affected {
|
||||
c.Data["json"] = wrapActionResponse(false)
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(transactionId)
|
||||
}
|
||||
|
||||
// DeleteTransaction
|
||||
@@ -162,6 +208,6 @@ func (c *ApiController) DeleteTransaction() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteTransaction(&transaction))
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteTransaction(&transaction, c.GetAcceptLanguage()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@@ -500,11 +500,6 @@ func (c *ApiController) SetPassword() {
|
||||
// return
|
||||
// }
|
||||
|
||||
if strings.Contains(newPassword, " ") {
|
||||
c.ResponseError(c.T("user:New password cannot contain blank space."))
|
||||
return
|
||||
}
|
||||
|
||||
userId := util.GetId(userOwner, userName)
|
||||
|
||||
user, err := object.GetUser(userId)
|
||||
@@ -517,6 +512,41 @@ func (c *ApiController) SetPassword() {
|
||||
return
|
||||
}
|
||||
|
||||
// Get organization to check for password obfuscation settings
|
||||
organization, err := object.GetOrganizationByUser(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if organization == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:the organization: %s is not found"), user.Owner))
|
||||
return
|
||||
}
|
||||
|
||||
// Deobfuscate passwords if organization has password obfuscator configured
|
||||
// Note: Deobfuscation is optional - if it fails, we treat the password as plain text
|
||||
// This allows SDKs and raw HTTP API calls to work without obfuscation support
|
||||
if organization.PasswordObfuscatorType != "" && organization.PasswordObfuscatorType != "Plain" {
|
||||
if oldPassword != "" {
|
||||
deobfuscatedOldPassword, deobfuscateErr := util.GetUnobfuscatedPassword(organization.PasswordObfuscatorType, organization.PasswordObfuscatorKey, oldPassword)
|
||||
if deobfuscateErr == nil {
|
||||
oldPassword = deobfuscatedOldPassword
|
||||
}
|
||||
}
|
||||
|
||||
if newPassword != "" {
|
||||
deobfuscatedNewPassword, deobfuscateErr := util.GetUnobfuscatedPassword(organization.PasswordObfuscatorType, organization.PasswordObfuscatorKey, newPassword)
|
||||
if deobfuscateErr == nil {
|
||||
newPassword = deobfuscatedNewPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(newPassword, " ") {
|
||||
c.ResponseError(c.T("user:New password cannot contain blank space."))
|
||||
return
|
||||
}
|
||||
|
||||
requestUserId := c.GetSessionUsername()
|
||||
if requestUserId == "" && code == "" {
|
||||
c.ResponseError(c.T("general:Please login first"), "Please login first")
|
||||
@@ -573,22 +603,12 @@ func (c *ApiController) SetPassword() {
|
||||
}
|
||||
}
|
||||
|
||||
msg := object.CheckPasswordComplexity(targetUser, newPassword)
|
||||
msg := object.CheckPasswordComplexity(targetUser, newPassword, c.GetAcceptLanguage())
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
}
|
||||
|
||||
organization, err := object.GetOrganizationByUser(targetUser)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if organization == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:the organization: %s is not found"), targetUser.Owner))
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the new password is the same as the current password
|
||||
if !object.CheckPasswordNotSameAsCurrent(targetUser, newPassword, organization) {
|
||||
c.ResponseError(c.T("user:The new password must be different from your current password"))
|
||||
|
||||
@@ -52,7 +52,11 @@ func (c *ApiController) UploadUsers() {
|
||||
}
|
||||
|
||||
userId := c.GetSessionUsername()
|
||||
owner, user := util.GetOwnerAndNameFromId(userId)
|
||||
owner, user, err := util.GetOwnerAndNameFromIdWithError(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
file, header, err := c.Ctx.Request.FormFile("file")
|
||||
if err != nil {
|
||||
|
||||
@@ -44,7 +44,11 @@ const (
|
||||
// @Success 200 {array} object.Verification The Response object
|
||||
// @router /get-payments [get]
|
||||
func (c *ApiController) GetVerifications() {
|
||||
owner := c.Input().Get("owner")
|
||||
organization, ok := c.RequireAdmin()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
@@ -52,8 +56,15 @@ func (c *ApiController) GetVerifications() {
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
owner := c.Input().Get("owner")
|
||||
// For global admin with organizationName parameter, use it to filter
|
||||
// For org admin, use their organization
|
||||
if c.IsGlobalAdmin() && owner != "" {
|
||||
organization = owner
|
||||
}
|
||||
|
||||
if limit == "" || page == "" {
|
||||
payments, err := object.GetVerifications(owner)
|
||||
payments, err := object.GetVerifications(organization)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -62,14 +73,14 @@ func (c *ApiController) GetVerifications() {
|
||||
c.ResponseOk(payments)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetVerificationCount(owner, field, value)
|
||||
count, err := object.GetVerificationCount(organization, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
payments, err := object.GetPaginationVerifications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
payments, err := object.GetPaginationVerifications(organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
||||
@@ -105,7 +105,7 @@ func (c *ApiController) UpdateWebhook() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateWebhook(id, &webhook, c.IsGlobalAdmin()))
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateWebhook(id, &webhook, c.IsGlobalAdmin(), c.GetAcceptLanguage()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package deployment
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
|
||||
var req *http.Request
|
||||
var err error
|
||||
|
||||
fromAddressField := "fromAddress"
|
||||
fromNameField := "fromName"
|
||||
toAddressField := "toAddress"
|
||||
toAddressesField := "toAddresses"
|
||||
@@ -59,6 +60,8 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
|
||||
contentField := "content"
|
||||
for k, v := range c.bodyMapping {
|
||||
switch k {
|
||||
case "fromAddress":
|
||||
fromAddressField = v
|
||||
case "fromName":
|
||||
fromNameField = v
|
||||
case "toAddress":
|
||||
@@ -74,6 +77,7 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
|
||||
|
||||
if c.method == "POST" || c.method == "PUT" || c.method == "DELETE" {
|
||||
bodyMap := make(map[string]string)
|
||||
bodyMap[fromAddressField] = fromAddress
|
||||
bodyMap[fromNameField] = fromName
|
||||
bodyMap[subjectField] = subject
|
||||
bodyMap[contentField] = content
|
||||
@@ -112,6 +116,7 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Add(fromAddressField, fromAddress)
|
||||
q.Add(fromNameField, fromName)
|
||||
if len(toAddress) == 1 {
|
||||
q.Add(toAddressField, toAddress[0])
|
||||
|
||||
49
go.mod
49
go.mod
@@ -1,9 +1,11 @@
|
||||
module github.com/casdoor/casdoor
|
||||
|
||||
go 1.21
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/Masterminds/squirrel v1.5.3
|
||||
github.com/NdoleStudio/lemonsqueezy-go v1.2.4
|
||||
github.com/PaddleHQ/paddle-go-sdk v1.0.0
|
||||
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4
|
||||
github.com/alibabacloud-go/facebody-20191230/v5 v5.1.2
|
||||
@@ -15,7 +17,7 @@ require (
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/casbin/casbin/v2 v2.77.2
|
||||
github.com/casdoor/go-sms-sender v0.25.0
|
||||
github.com/casdoor/gomail/v2 v2.1.0
|
||||
github.com/casdoor/gomail/v2 v2.2.0
|
||||
github.com/casdoor/ldapserver v1.2.0
|
||||
github.com/casdoor/notify v1.0.1
|
||||
github.com/casdoor/oss v1.8.0
|
||||
@@ -29,7 +31,8 @@ require (
|
||||
github.com/go-git/go-git/v5 v5.13.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/go-mysql-org/go-mysql v1.7.0
|
||||
github.com/go-pay/gopay v1.5.72
|
||||
github.com/go-pay/gopay v1.5.115
|
||||
github.com/go-pay/util v0.0.4
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||
github.com/go-webauthn/webauthn v0.10.2
|
||||
@@ -41,7 +44,8 @@ require (
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
||||
github.com/markbates/goth v1.79.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nyaruka/phonenumbers v1.1.5
|
||||
github.com/nyaruka/phonenumbers v1.2.2
|
||||
github.com/polarsource/polar-go v0.12.0
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.11.1
|
||||
github.com/prometheus/client_model v0.4.0
|
||||
@@ -52,20 +56,20 @@ require (
|
||||
github.com/sendgrid/sendgrid-go v3.14.0+incompatible
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/stripe/stripe-go/v74 v74.29.0
|
||||
github.com/tealeg/xlsx v1.0.5
|
||||
github.com/thanhpk/randstr v1.0.4
|
||||
github.com/xorm-io/builder v0.3.13
|
||||
github.com/xorm-io/core v0.7.4
|
||||
github.com/xorm-io/xorm v1.1.6
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/net v0.34.0
|
||||
golang.org/x/crypto v0.39.0
|
||||
golang.org/x/net v0.40.0
|
||||
golang.org/x/oauth2 v0.17.0
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/text v0.26.0
|
||||
google.golang.org/api v0.150.0
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
layeh.com/radius v0.0.0-20221205141417-e7fbddd11d68
|
||||
layeh.com/radius v0.0.0-20231213012653-1006025d24f8
|
||||
maunium.net/go/mautrix v0.16.0
|
||||
modernc.org/sqlite v1.18.2
|
||||
)
|
||||
@@ -124,14 +128,21 @@ require (
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
||||
github.com/ggicci/httpin v0.19.0 // indirect
|
||||
github.com/ggicci/owl v0.8.2 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.0 // indirect
|
||||
github.com/go-lark/lark v1.9.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-pay/crypto v0.0.1 // indirect
|
||||
github.com/go-pay/errgroup v0.0.3 // indirect
|
||||
github.com/go-pay/smap v0.0.2 // indirect
|
||||
github.com/go-pay/xlog v0.0.3 // indirect
|
||||
github.com/go-pay/xtime v0.0.2 // indirect
|
||||
github.com/go-webauthn/x v0.1.9 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
@@ -146,6 +157,7 @@ require (
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/gregdel/pushover v1.2.1 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
@@ -162,9 +174,9 @@ require (
|
||||
github.com/line/line-bot-sdk-go v7.8.0+incompatible // indirect
|
||||
github.com/markbates/going v1.0.0 // indirect
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-ieproxy v0.0.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mileusna/viber v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@@ -188,9 +200,10 @@ require (
|
||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
github.com/slack-go/slack v0.12.3 // indirect
|
||||
github.com/spyzhov/ajson v0.8.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
@@ -217,18 +230,18 @@ require (
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
|
||||
google.golang.org/grpc v1.59.0 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
|
||||
99
go.sum
99
go.sum
@@ -78,7 +78,11 @@ github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/NdoleStudio/lemonsqueezy-go v1.2.4 h1:BhWlCUH+DIPfSn4g/V7f2nFkMCQuzno9DXKZ7YDrXXA=
|
||||
github.com/NdoleStudio/lemonsqueezy-go v1.2.4/go.mod h1:2uZlWgn9sbNxOx3JQWLlPrDOC6NT/wmSTOgL3U/fMMw=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PaddleHQ/paddle-go-sdk v1.0.0 h1:+EXitsPFbRcc0CpQE/MIeudxiVOR8pFe/aOWTEUHDKU=
|
||||
github.com/PaddleHQ/paddle-go-sdk v1.0.0/go.mod h1:kbBBzf0BHEj38QvhtoELqlGip3alKgA/I+vl7RQzB58=
|
||||
github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
|
||||
github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20221121042443-a3fd332d56d9 h1:vuu1KBsr6l7XU3CHsWESP/4B1SNd+VZkrgeFZsUXrsY=
|
||||
@@ -233,8 +237,8 @@ github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXy
|
||||
github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc=
|
||||
github.com/casdoor/go-sms-sender v0.25.0 h1:eF4cOCSbjVg7+0uLlJQnna/FQ0BWW+Fp/x4cXhzQu1Y=
|
||||
github.com/casdoor/go-sms-sender v0.25.0/go.mod h1:bOm4H8/YfJmEHjBatEVQFOnAf0OOn1B0Wi5B7zDhws0=
|
||||
github.com/casdoor/gomail/v2 v2.1.0 h1:ua97E3CARnF1Ik8ga/Drz9uGZfaElXJumFexiErWUxM=
|
||||
github.com/casdoor/gomail/v2 v2.1.0/go.mod h1:GFzOD9RhY0nODiiPaQiOa6DfoKtmO9aTesu5qrp26OI=
|
||||
github.com/casdoor/gomail/v2 v2.2.0 h1:gVMk43qvqq4XYkAJ+CDY5WWKF9yYRipuyXfp7P0HWIg=
|
||||
github.com/casdoor/gomail/v2 v2.2.0/go.mod h1:GFzOD9RhY0nODiiPaQiOa6DfoKtmO9aTesu5qrp26OI=
|
||||
github.com/casdoor/ldapserver v1.2.0 h1:HdSYe+ULU6z9K+2BqgTrJKQRR4//ERAXB64ttOun6Ow=
|
||||
github.com/casdoor/ldapserver v1.2.0/go.mod h1:VwYU2vqQ2pA8sa00PRekH71R2XmgfzMKhmp1XrrDu2s=
|
||||
github.com/casdoor/notify v1.0.1 h1:p0kzI7OBlvLbL7zWeKIu31LRcEAygNZGKr5gcFfSIoE=
|
||||
@@ -350,6 +354,10 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
|
||||
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/ggicci/httpin v0.19.0 h1:p0B3SWLVgg770VirYiHB14M5wdRx3zR8mCTzM/TkTQ8=
|
||||
github.com/ggicci/httpin v0.19.0/go.mod h1:hzsQHcbqLabmGOycf7WNw6AAzcVbsMeoOp46bWAbIWc=
|
||||
github.com/ggicci/owl v0.8.2 h1:og+lhqpzSMPDdEB+NJfzoAJARP7qCG3f8uUC3xvGukA=
|
||||
github.com/ggicci/owl v0.8.2/go.mod h1:PHRD57u41vFN5UtFz2SF79yTVoM3HlWpjMiE+ZU2dj4=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
@@ -384,8 +392,20 @@ github.com/go-mysql-org/go-mysql v1.7.0 h1:qE5FTRb3ZeTQmlk3pjE+/m2ravGxxRDrVDTyD
|
||||
github.com/go-mysql-org/go-mysql v1.7.0/go.mod h1:9cRWLtuXNKhamUPMkrDVzBhaomGvqLRLtBiyjvjc4pk=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-pay/gopay v1.5.72 h1:3zm64xMBhJBa8rXbm//q5UiGgOa4WO5XYEnU394N2Zw=
|
||||
github.com/go-pay/gopay v1.5.72/go.mod h1:0qOGIJuFW7PKDOjmecwKyW0mgsVImgwB9yPJj0ilpn8=
|
||||
github.com/go-pay/crypto v0.0.1 h1:B6InT8CLfSLc6nGRVx9VMJRBBazFMjr293+jl0lLXUY=
|
||||
github.com/go-pay/crypto v0.0.1/go.mod h1:41oEIvHMKbNcYlWUlRWtsnC6+ASgh7u29z0gJXe5bes=
|
||||
github.com/go-pay/errgroup v0.0.3 h1:DB4s8e8oWYDyETKQ1y1riMJ7y29zE1uIsMCSjEOFSbU=
|
||||
github.com/go-pay/errgroup v0.0.3/go.mod h1:0+4b8mvFMS71MIzsaC+gVvB4x37I93lRb2dqrwuU8x8=
|
||||
github.com/go-pay/gopay v1.5.115 h1:8WjWftPChKCiVt5Qz2xLqXeUdidsR+y9/R2S/7Q9szc=
|
||||
github.com/go-pay/gopay v1.5.115/go.mod h1:p48xvWeepPolZuakAjCeucWynWwW7msoXsqahcoJpKE=
|
||||
github.com/go-pay/smap v0.0.2 h1:kKflYor5T5FgZltPFBMTFfjJvqYMHr5VnIFSEyhVTcA=
|
||||
github.com/go-pay/smap v0.0.2/go.mod h1:HW9oAo0okuyDYsbpbj5fJFxnNj/BZorRGFw26SxrNWw=
|
||||
github.com/go-pay/util v0.0.4 h1:TuwSU9o3Qd7m9v1PbzFuIA/8uO9FJnA6P7neG/NwPyk=
|
||||
github.com/go-pay/util v0.0.4/go.mod h1:Tsdhs8Ib9J9b4+NKNO1PHh5hWHhlg98PthsX0ckq6PM=
|
||||
github.com/go-pay/xlog v0.0.3 h1:avyMhCL/JgBHreoGx/am/kHxfs1udDOAeVqbmzP/Yes=
|
||||
github.com/go-pay/xlog v0.0.3/go.mod h1:mH47xbobrdsSHWsmFtSF5agWbMHFP+tK0ZbVCk5OAEw=
|
||||
github.com/go-pay/xtime v0.0.2 h1:7YR4/iuELsEHpJ6LUO0SVK80hQxDO9MLCfuVYIiTCRM=
|
||||
github.com/go-pay/xtime v0.0.2/go.mod h1:W1yRbJaSt4CSBcdAtLBQ8xajiN/Pl5hquGczUcUE9xE=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
@@ -416,8 +436,8 @@ github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptG
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
@@ -522,8 +542,9 @@ github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORR
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
|
||||
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
@@ -540,6 +561,7 @@ github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOj
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
@@ -585,8 +607,9 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
||||
@@ -605,6 +628,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
|
||||
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
@@ -671,8 +696,9 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
|
||||
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
@@ -681,8 +707,9 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
@@ -728,8 +755,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/nyaruka/phonenumbers v1.1.5 h1:vYy2DI+z5hdaemqVzXYJ4CVyK92IG484CirEY+40GTo=
|
||||
github.com/nyaruka/phonenumbers v1.1.5/go.mod h1:yShPJHDSH3aTKzCbXyVxNpbl2kA+F+Ne5Pun/MvFRos=
|
||||
github.com/nyaruka/phonenumbers v1.2.2 h1:OwVjf7Y4uHoK9VJUrA8ebR0ha2yc6sEYbfrwkq0asCY=
|
||||
github.com/nyaruka/phonenumbers v1.2.2/go.mod h1:wzk2qq7qwsaBKrfbkWKdgHYOOH+QFTesSpIq53ELw8M=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||
@@ -776,6 +803,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/polarsource/polar-go v0.12.0 h1:um+6ftOPUMg2TQq9Kv/6fKGBOAl7dOc2YiDdx4Bb0y8=
|
||||
github.com/polarsource/polar-go v0.12.0/go.mod h1:FB11Q4m2n3wIk6l/POOkz0MVOUx1o0Yt4Y97MnQfe0c=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||
@@ -868,8 +897,9 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
|
||||
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
|
||||
github.com/slack-go/slack v0.12.3 h1:92/dfFU8Q5XP6Wp5rr5/T5JHLM5c5Smtn53fhToAP88=
|
||||
@@ -881,6 +911,8 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ
|
||||
github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
|
||||
github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spyzhov/ajson v0.8.0 h1:sFXyMbi4Y/BKjrsfkUZHSjA2JM1184enheSjjoT/zCc=
|
||||
github.com/spyzhov/ajson v0.8.0/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
|
||||
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
@@ -903,8 +935,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/stripe/stripe-go/v74 v74.29.0 h1:ffJ+1Ta1Ccg7yDDz+SfjixX0KizEEJ/wNVRoFYkdwFY=
|
||||
github.com/stripe/stripe-go/v74 v74.29.0/go.mod h1:f9L6LvaXa35ja7eyvP6GQswoaIPaBRvGAimAO+udbBw=
|
||||
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
@@ -1023,7 +1055,6 @@ golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -1037,7 +1068,6 @@ golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220208233918-bba287dce954/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
@@ -1049,8 +1079,9 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
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=
|
||||
@@ -1096,8 +1127,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20171115151908-9dfe39835686/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1159,8 +1190,9 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1186,8 +1218,9 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1262,6 +1295,7 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1272,8 +1306,9 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
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=
|
||||
@@ -1290,8 +1325,9 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
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=
|
||||
@@ -1310,8 +1346,9 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
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=
|
||||
@@ -1378,8 +1415,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1487,8 +1524,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
@@ -1536,8 +1573,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
layeh.com/radius v0.0.0-20221205141417-e7fbddd11d68 h1:2NDro2Jzkrqfngy/sA5GVnChs7fx8EzcQKFi/lI2cfg=
|
||||
layeh.com/radius v0.0.0-20221205141417-e7fbddd11d68/go.mod h1:pFWM9De99EY9TPVyHIyA56QmoRViVck/x41WFkUlc9A=
|
||||
layeh.com/radius v0.0.0-20231213012653-1006025d24f8 h1:orYXpi6BJZdvgytfHH4ybOe4wHnLbbS71Cmd8mWdZjs=
|
||||
layeh.com/radius v0.0.0-20231213012653-1006025d24f8/go.mod h1:QRf+8aRqXc019kHkpcs/CTgyWXFzf+bxlsyuo2nAl1o=
|
||||
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package i18n
|
||||
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "الحساب الخاص بالمزود: %s واسم المستخدم: %s (%s) غير موجود ولا يُسمح بالتسجيل كحساب جديد، يرجى الاتصال بدعم تكنولوجيا المعلومات",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "الحساب الخاص بالمزود: %s واسم المستخدم: %s (%s) مرتبط بالفعل بحساب آخر: %s (%s)",
|
||||
"The application: %s does not exist": "التطبيق: %s غير موجود",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "التطبيق: %s قد عطّل تسجيل دخول المستخدمين",
|
||||
"The group: %s does not exist": "المجموعة: %s غير موجودة",
|
||||
"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 organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "المنظمة: %s قد عطّلت تسجيل دخول المستخدمين",
|
||||
"The plan: %s does not exist": "الخطة: %s غير موجودة",
|
||||
"The pricing: %s does not exist": "التسعير: %s غير موجود",
|
||||
"The pricing: %s does not have plan: %s": "التسعير: %s لا يحتوي على الخطة: %s",
|
||||
"The provider: %s does not exist": "المزود: %s غير موجود",
|
||||
"The provider: %s is not enabled for the application": "المزود: %s غير مفعّل لهذا التطبيق",
|
||||
"Unauthorized operation": "عملية غير مصرح بها",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "يرجى التسجيل باستخدام اسم المستخدم المطابق لرمز الدعوة",
|
||||
"Session outdated, please login again": "الجلسة منتهية الصلاحية، يرجى تسجيل الدخول مرة أخرى",
|
||||
"The invitation code has already been used": "رمز الدعوة تم استخدامه بالفعل",
|
||||
"The password must contain at least one special character": "يجب أن تحتوي كلمة المرور على حرف خاص واحد على الأقل",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "يجب أن تحتوي كلمة المرور على حرف كبير واحد على الأقل وحرف صغير ورقم",
|
||||
"The password must have at least 6 characters": "يجب أن تحتوي كلمة المرور على 6 أحرف على الأقل",
|
||||
"The password must have at least 8 characters": "يجب أن تحتوي كلمة المرور على 8 أحرف على الأقل",
|
||||
"The password must not contain any repeated characters": "يجب ألا تحتوي كلمة المرور على أي أحرف متكررة",
|
||||
"The user has been deleted and cannot be used 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": "المستخدم: %s غير موجود في خادم LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "هذه العملية تتطلب مسؤولاً لتنفيذها"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "الدعوة %s غير موجودة"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "خادم LDAP موجود"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "تم تمكين MFA للبريد الإلكتروني لكن البريد الإلكتروني فارغ",
|
||||
"MFA phone is enabled but phone number is empty": "تم تمكين MFA للهاتف لكن رقم الهاتف فارغ",
|
||||
"New password cannot contain blank space.": "كلمة المرور الجديدة لا يمكن أن تحتوي على مسافات.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "يجب أن تكون كلمة المرور الجديدة مختلفة عن كلمة المرور الحالية",
|
||||
"the user's owner and name should not be empty": "مالك المستخدم واسمه لا يجب أن يكونا فارغين"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "المستخدم غير موجود، يرجى التسجيل أولاً"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "لم يتم العثور على بيانات اعتماد لهذا المستخدم",
|
||||
"Please call WebAuthnSigninBegin first": "يرجى استدعاء WebAuthnSigninBegin أولاً"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Provayder üçün hesab: %s və istifadəçi adı: %s (%s) artıq başqa hesabla əlaqələndirilmişdir: %s (%s)",
|
||||
"The application: %s does not exist": "Tətbiq: %s mövcud deyil",
|
||||
"The application: %s has disabled users to signin": "Tətbiq: %s istifadəçilərin girişini söndürmüşdür",
|
||||
"The group: %s does not exist": "Qrup: %s mövcud deyil",
|
||||
"The login method: login with LDAP is not enabled for the application": "Giriş metodu: LDAP ilə giriş bu tətbiq üçün aktiv deyil",
|
||||
"The login method: login with SMS is not enabled for the application": "Giriş metodu: SMS ilə giriş bu tətbiq üçün aktiv deyil",
|
||||
"The login method: login with email is not enabled for the application": "Giriş metodu: email ilə giriş bu tətbiq üçün aktiv deyil",
|
||||
@@ -24,6 +25,9 @@
|
||||
"The login method: login with password is not enabled for the application": "Giriş metodu: şifrə ilə giriş bu tətbiq üçün aktiv deyil",
|
||||
"The organization: %s does not exist": "Təşkilat: %s mövcud deyil",
|
||||
"The organization: %s has disabled users to signin": "Təşkilat: %s istifadəçilərin girişini söndürmüşdür",
|
||||
"The plan: %s does not exist": "Plan: %s mövcud deyil",
|
||||
"The pricing: %s does not exist": "Qiymətləndirmə: %s mövcud deyil",
|
||||
"The pricing: %s does not have plan: %s": "Qiymətləndirmə: %s planı yoxdur: %s",
|
||||
"The provider: %s does not exist": "Provayder: %s mövcud deyil",
|
||||
"The provider: %s is not enabled for the application": "Provayder: %s bu tətbiq üçün aktiv deyil",
|
||||
"Unauthorized operation": "İcazəsiz əməliyyat",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Xahiş edirik dəvət koduna uyğun istifadəçi adı istifadə edərək qeydiyyatdan keçin",
|
||||
"Session outdated, please login again": "Sessiyanın vaxtı keçib, xahiş edirik yenidən daxil olun",
|
||||
"The invitation code has already been used": "Dəvət kodu artıq istifadə edilib",
|
||||
"The password must contain at least one special character": "Parol ən azı bir xüsusi simvol ehtiva etməlidir",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Parol ən azı bir böyük hərf, bir kiçik hərf və bir rəqəm ehtiva etməlidir",
|
||||
"The password must have at least 6 characters": "Parol ən azı 6 simvoldan ibarət olmalıdır",
|
||||
"The password must have at least 8 characters": "Parol ən azı 8 simvoldan ibarət olmalıdır",
|
||||
"The password must not contain any repeated characters": "Parol təkrarlanan simvollar ehtiva etməməlidir",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "İstifadəçi silinib və daxil olmaq üçün istifadə edilə bilməz, zəhmət olmasa administratorla əlaqə saxlayın",
|
||||
"The user is forbidden to sign in, please contact the administrator": "İstifadəçinin girişi qadağandır, xahiş edirik administratorla əlaqə saxlayın",
|
||||
"The user: %s doesn't exist in LDAP server": "İstifadəçi: %s LDAP serverində mövcud deyil",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "bu əməliyyat administrator tərəfindən yerinə yetirilməsini tələb edir"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Dəvət %s mövcud deyil"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP serveri mövcuddur"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA email aktiv edilib, lakin email boşdur",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefon aktiv edilib, lakin telefon nömrəsi boşdur",
|
||||
"New password cannot contain blank space.": "Yeni şifrə boş yer ehtiva edə bilməz.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Yeni şifrə cari şifrənizdən fərqli olmalıdır",
|
||||
"the user's owner and name should not be empty": "istifadəçinin sahibi və adı boş olmamalıdır"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) neexistuje a není povoleno se registrovat jako nový účet, prosím kontaktujte svou IT podporu",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) je již propojen s jiným účtem: %s (%s)",
|
||||
"The application: %s does not exist": "Aplikace: %s neexistuje",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Aplikace: %s zakázala přihlašování uživatelů",
|
||||
"The group: %s does not exist": "Skupina: %s neexistuje",
|
||||
"The login method: login with LDAP is not enabled for the application": "Přihlašovací metoda: přihlášení pomocí LDAP není pro aplikaci povolena",
|
||||
"The login method: login with SMS is not enabled for the application": "Přihlašovací metoda: přihlášení pomocí SMS není pro aplikaci povolena",
|
||||
"The login method: login with email is not enabled for the application": "Přihlašovací metoda: přihlášení pomocí e-mailu není pro aplikaci povolena",
|
||||
"The login method: login with face is not enabled for the application": "Přihlašovací metoda: přihlášení pomocí obličeje není pro aplikaci povolena",
|
||||
"The login method: login with password is not enabled for the application": "Metoda přihlášení: přihlášení pomocí hesla není pro aplikaci povolena",
|
||||
"The organization: %s does not exist": "Organizace: %s neexistuje",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Organizace: %s zakázala přihlašování uživatelů",
|
||||
"The plan: %s does not exist": "Plán: %s neexistuje",
|
||||
"The pricing: %s does not exist": "Ceník: %s neexistuje",
|
||||
"The pricing: %s does not have plan: %s": "Ceník: %s nemá plán: %s",
|
||||
"The provider: %s does not exist": "Poskytovatel: %s neexistuje",
|
||||
"The provider: %s is not enabled for the application": "Poskytovatel: %s není pro aplikaci povolen",
|
||||
"Unauthorized operation": "Neoprávněná operace",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Prosím registrujte se pomocí uživatelského jména odpovídajícího pozvánkovému kódu",
|
||||
"Session outdated, please login again": "Relace je zastaralá, prosím přihlaste se znovu",
|
||||
"The invitation code has already been used": "Pozvánkový kód již byl použit",
|
||||
"The password must contain at least one special character": "Heslo musí obsahovat alespoň jeden speciální znak",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Heslo musí obsahovat alespoň jedno velké písmeno, jedno malé písmeno a jednu číslici",
|
||||
"The password must have at least 6 characters": "Heslo musí mít alespoň 6 znaků",
|
||||
"The password must have at least 8 characters": "Heslo musí mít alespoň 8 znaků",
|
||||
"The password must not contain any repeated characters": "Heslo nesmí obsahovat opakující se znaky",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Uživatel byl odstraněn a nelze jej použít k přihlášení, kontaktujte prosím správce",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Uživatel má zakázáno se přihlásit, prosím kontaktujte administrátora",
|
||||
"The user: %s doesn't exist in LDAP server": "Uživatel: %s neexistuje na LDAP serveru",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "tato operace vyžaduje administrátora k provedení"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Pozvánka %s neexistuje"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Ldap server existuje"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA e-mail je povolen, ale e-mail je prázdný",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefon je povolen, ale telefonní číslo je prázdné",
|
||||
"New password cannot contain blank space.": "Nové heslo nemůže obsahovat prázdné místo.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Nové heslo musí být odlišné od vašeho současného hesla",
|
||||
"the user's owner and name should not be empty": "vlastník a jméno uživatele by neměly být prázdné"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "uživatel neexistuje, prosím nejprve se zaregistrujte"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Pro tohoto uživatele nebyly nalezeny žádné přihlašovací údaje",
|
||||
"Please call WebAuthnSigninBegin first": "Prosím, nejprve zavolejte WebAuthnSigninBegin"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Das Konto für den Anbieter %s und Benutzernamen %s (%s) existiert nicht und es ist nicht erlaubt, ein neues Konto anzumelden. Bitte wenden Sie sich an Ihren IT-Support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Das Konto für den Anbieter %s und Benutzernamen %s (%s) ist bereits mit einem anderen Konto verknüpft: %s (%s)",
|
||||
"The application: %s does not exist": "Die Anwendung: %s existiert nicht",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Die Anwendung: %s hat die Anmeldung von Benutzern deaktiviert",
|
||||
"The group: %s does not exist": "Die Gruppe: %s existiert nicht",
|
||||
"The login method: login with LDAP is not enabled for the application": "Die Anmeldemethode: Anmeldung mit LDAP ist für die Anwendung nicht aktiviert",
|
||||
"The login method: login with SMS is not enabled for the application": "Die Anmeldemethode: Anmeldung per SMS ist für die Anwendung nicht aktiviert",
|
||||
"The login method: login with email is not enabled for the application": "Die Anmeldemethode: Anmeldung per E-Mail ist für die Anwendung nicht aktiviert",
|
||||
"The login method: login with face is not enabled for the application": "Die Anmeldemethode: Anmeldung per Gesicht ist für die Anwendung nicht aktiviert",
|
||||
"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": "Die Organisation: %s existiert nicht",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Die Organisation: %s hat die Anmeldung von Benutzern deaktiviert",
|
||||
"The plan: %s does not exist": "Der Plan: %s existiert nicht",
|
||||
"The pricing: %s does not exist": "Die Preisgestaltung: %s existiert nicht",
|
||||
"The pricing: %s does not have plan: %s": "Die Preisgestaltung: %s hat keinen Plan: %s",
|
||||
"The provider: %s does not exist": "Der Anbieter: %s existiert nicht",
|
||||
"The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert",
|
||||
"Unauthorized operation": "Nicht autorisierte Operation",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Bitte registrieren Sie sich mit dem Benutzernamen, der zum Einladungscode gehört",
|
||||
"Session outdated, please login again": "Sitzung abgelaufen, bitte erneut anmelden",
|
||||
"The invitation code has already been used": "Der Einladungscode wurde bereits verwendet",
|
||||
"The password must contain at least one special character": "Das Passwort muss mindestens ein Sonderzeichen enthalten",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Das Passwort muss mindestens einen Großbuchstaben, einen Kleinbuchstaben und eine Ziffer enthalten",
|
||||
"The password must have at least 6 characters": "Das Passwort muss mindestens 6 Zeichen haben",
|
||||
"The password must have at least 8 characters": "Das Passwort muss mindestens 8 Zeichen haben",
|
||||
"The password must not contain any repeated characters": "Das Passwort darf keine wiederholten Zeichen enthalten",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Der Benutzer wurde gelöscht und kann nicht zur Anmeldung verwendet werden. Bitte wenden Sie sich an den Administrator",
|
||||
"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": "Der Benutzer: %s existiert nicht im LDAP-Server",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "Dieser Vorgang erfordert einen Administrator zur Ausführung"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Einladung %s existiert nicht"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Es gibt einen LDAP-Server"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA-E-Mail ist aktiviert, aber E-Mail ist leer",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-Telefon ist aktiviert, aber Telefonnummer ist leer",
|
||||
"New password cannot contain blank space.": "Das neue Passwort darf keine Leerzeichen enthalten.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Das neue Passwort muss sich von Ihrem aktuellen Passwort unterscheiden",
|
||||
"the user's owner and name should not be empty": "Eigentümer und Name des Benutzers dürfen nicht leer sein"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "Der Benutzer existiert nicht, bitte zuerst anmelden"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Für diesen Benutzer wurden keine Anmeldeinformationen gefunden",
|
||||
"Please call WebAuthnSigninBegin first": "Bitte rufen Sie zuerst WebAuthnSigninBegin auf"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The group: %s does not exist": "The group: %s does not exist",
|
||||
"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",
|
||||
@@ -24,6 +25,9 @@
|
||||
"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 organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The plan: %s does not exist": "The plan: %s does not exist",
|
||||
"The pricing: %s does not exist": "The pricing: %s does not exist",
|
||||
"The pricing: %s does not have plan: %s": "The pricing: %s does not have plan: %s",
|
||||
"The provider: %s does not exist": "The provider: %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",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Please register using the username corresponding to the invitation code",
|
||||
"Session outdated, please login again": "Session outdated, please login again",
|
||||
"The invitation code has already been used": "The invitation code has already been used",
|
||||
"The password must contain at least one special character": "The password must contain at least one special character",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "The password must contain at least one uppercase letter, one lowercase letter and one digit",
|
||||
"The password must have at least 6 characters": "The password must have at least 6 characters",
|
||||
"The password must have at least 8 characters": "The password must have at least 8 characters",
|
||||
"The password must not contain any repeated characters": "The password must not contain any repeated characters",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "The user has been deleted and cannot be used to sign in, please contact the administrator",
|
||||
"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",
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "La cuenta para el proveedor: %s y el nombre de usuario: %s (%s) no existe y no se permite registrarse como una nueva cuenta, por favor contacte a su soporte de TI",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "La cuenta para proveedor: %s y nombre de usuario: %s (%s) ya está vinculada a otra cuenta: %s (%s)",
|
||||
"The application: %s does not exist": "La aplicación: %s no existe",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "La aplicación: %s ha desactivado el inicio de sesión de usuarios",
|
||||
"The group: %s does not exist": "El grupo: %s no existe",
|
||||
"The login method: login with LDAP is not enabled for the application": "El método de inicio de sesión: inicio de sesión con LDAP no está habilitado para la aplicación",
|
||||
"The login method: login with SMS is not enabled for the application": "El método de inicio de sesión: inicio de sesión con SMS no está habilitado para la aplicación",
|
||||
"The login method: login with email is not enabled for the application": "El método de inicio de sesión: inicio de sesión con correo electrónico no está habilitado para la aplicación",
|
||||
"The login method: login with face is not enabled for the application": "El método de inicio de sesión: inicio de sesión con reconocimiento facial no está habilitado para la aplicación",
|
||||
"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": "La organización: %s no existe",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "La organización: %s ha desactivado el inicio de sesión de usuarios",
|
||||
"The plan: %s does not exist": "El plan: %s no existe",
|
||||
"The pricing: %s does not exist": "El precio: %s no existe",
|
||||
"The pricing: %s does not have plan: %s": "El precio: %s no tiene el plan: %s",
|
||||
"The provider: %s does not exist": "El proveedor: %s no existe",
|
||||
"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",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Regístrese usando el nombre de usuario correspondiente al código de invitación",
|
||||
"Session outdated, please login again": "Sesión expirada, por favor vuelva a iniciar sesión",
|
||||
"The invitation code has already been used": "El código de invitación ya ha sido utilizado",
|
||||
"The password must contain at least one special character": "La contraseña debe contener al menos un carácter especial",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "La contraseña debe contener al menos una letra mayúscula, una letra minúscula y un dígito",
|
||||
"The password must have at least 6 characters": "La contraseña debe tener al menos 6 caracteres",
|
||||
"The password must have at least 8 characters": "La contraseña debe tener al menos 8 caracteres",
|
||||
"The password must not contain any repeated characters": "La contraseña no debe contener caracteres repetidos",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "El usuario ha sido eliminado y no se puede usar para iniciar sesión, póngase en contacto con el administrador",
|
||||
"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": "El usuario: %s no existe en el servidor LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "esta operación requiere que el administrador la realice"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "La invitación %s no existe"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "El servidor LDAP existe"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "El correo electrónico MFA está habilitado pero el correo está vacío",
|
||||
"MFA phone is enabled but phone number is empty": "El teléfono MFA está habilitado pero el número de teléfono está vacío",
|
||||
"New password cannot contain blank space.": "La nueva contraseña no puede contener espacios en blanco.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "La nueva contraseña debe ser diferente de su contraseña actual",
|
||||
"the user's owner and name should not be empty": "el propietario y el nombre del usuario no deben estar vacíos"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "El usuario no existe, por favor regístrese primero"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "No se encontraron credenciales para este usuario",
|
||||
"Please call WebAuthnSigninBegin first": "Por favor, llama primero a WebAuthnSigninBegin"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "حساب برای ارائهدهنده: %s و نام کاربری: %s (%s) وجود ندارد و مجاز به ثبتنام بهعنوان حساب جدید نیست، لطفاً با پشتیبانی IT خود تماس بگیرید",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "حساب برای ارائهدهنده: %s و نام کاربری: %s (%s) در حال حاضر به حساب دیگری مرتبط است: %s (%s)",
|
||||
"The application: %s does not exist": "برنامه: %s وجود ندارد",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "برنامه: %s ورود کاربران را غیرفعال کرده است",
|
||||
"The group: %s does not exist": "گروه: %s وجود ندارد",
|
||||
"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 organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "سازمان: %s ورود کاربران را غیرفعال کرده است",
|
||||
"The plan: %s does not exist": "طرح: %s وجود ندارد",
|
||||
"The pricing: %s does not exist": "قیمتگذاری: %s وجود ندارد",
|
||||
"The pricing: %s does not have plan: %s": "قیمتگذاری: %s طرح ندارد: %s",
|
||||
"The provider: %s does not exist": "ارائهکننده: %s وجود ندارد",
|
||||
"The provider: %s is not enabled for the application": "ارائهدهنده: %s برای برنامه فعال نیست",
|
||||
"Unauthorized operation": "عملیات غیرمجاز",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "لطفاً با استفاده از نام کاربری مربوط به کد دعوت ثبتنام کنید",
|
||||
"Session outdated, please login again": "جلسه منقضی شده است، لطفاً دوباره وارد شوید",
|
||||
"The invitation code has already been used": "کد دعوت قبلاً استفاده شده است",
|
||||
"The password must contain at least one special character": "رمز عبور باید حداقل یک کاراکتر خاص داشته باشد",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "رمز عبور باید حداقل یک حرف بزرگ، یک حرف کوچک و یک رقم داشته باشد",
|
||||
"The password must have at least 6 characters": "رمز عبور باید حداقل 6 کاراکتر داشته باشد",
|
||||
"The password must have at least 8 characters": "رمز عبور باید حداقل 8 کاراکتر داشته باشد",
|
||||
"The password must not contain any repeated characters": "رمز عبور نباید شامل کاراکترهای تکراری باشد",
|
||||
"The user has been deleted and cannot be used 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": "کاربر: %s در سرور LDAP وجود ندارد",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "این عملیات نیاز به مدیر برای انجام دارد"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "دعوتنامه %s وجود ندارد"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "سرور LDAP وجود دارد"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "ایمیل MFA فعال است اما ایمیل خالی است",
|
||||
"MFA phone is enabled but phone number is empty": "تلفن MFA فعال است اما شماره تلفن خالی است",
|
||||
"New password cannot contain blank space.": "رمز عبور جدید نمیتواند حاوی فاصله خالی باشد.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "رمز عبور جدید باید با رمز عبور فعلی شما متفاوت باشد",
|
||||
"the user's owner and name should not be empty": "مالک و نام کاربر نباید خالی باشند"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "کاربر وجود ندارد، لطفاً ابتدا ثبتنام کنید"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "اطلاعات ورود برای این کاربر یافت نشد",
|
||||
"Please call WebAuthnSigninBegin first": "لطفاً ابتدا WebAuthnSigninBegin را فراخوانی کنید"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Tiliä providerille: %s ja käyttäjälle: %s (%s) ei ole olemassa, eikä sitä voi rekisteröidä uutena tilinä, ota yhteyttä IT-tukeen",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Tili providerille: %s ja käyttäjälle: %s (%s) on jo linkitetty toiseen tiliin: %s (%s)",
|
||||
"The application: %s does not exist": "Sovellus: %s ei ole olemassa",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Sovellus: %s on estänyt käyttäjien kirjautumisen",
|
||||
"The group: %s does not exist": "Ryhmä: %s ei ole olemassa",
|
||||
"The login method: login with LDAP is not enabled for the application": "Kirjautumistapa: kirjautuminen LDAP:n kautta ei ole käytössä sovelluksessa",
|
||||
"The login method: login with SMS is not enabled for the application": "Kirjautumistapa: kirjautuminen SMS:n kautta ei ole käytössä sovelluksessa",
|
||||
"The login method: login with email is not enabled for the application": "Kirjautumistapa: kirjautuminen sähköpostin kautta ei ole käytössä sovelluksessa",
|
||||
"The login method: login with face is not enabled for the application": "Kirjautumistapa: kirjautuminen kasvotunnistuksella ei ole käytössä sovelluksessa",
|
||||
"The login method: login with password is not enabled for the application": "Kirjautumistapa: kirjautuminen salasanalla ei ole käytössä sovelluksessa",
|
||||
"The organization: %s does not exist": "Organisaatio: %s ei ole olemassa",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Organisaatio: %s on estänyt käyttäjien kirjautumisen",
|
||||
"The plan: %s does not exist": "Suunnitelma: %s ei ole olemassa",
|
||||
"The pricing: %s does not exist": "Hinnoittelu: %s ei ole olemassa",
|
||||
"The pricing: %s does not have plan: %s": "Hinnoittelu: %s ei sisällä suunnitelmaa: %s",
|
||||
"The provider: %s does not exist": "Provider: %s ei ole olemassa",
|
||||
"The provider: %s is not enabled for the application": "Provider: %s ei ole käytössä sovelluksessa",
|
||||
"Unauthorized operation": "Luvaton toiminto",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Rekisteröidy käyttämällä kutsukoodiin vastaavaa käyttäjänimeä",
|
||||
"Session outdated, please login again": "Istunto vanhentunut, kirjaudu uudelleen",
|
||||
"The invitation code has already been used": "Kutsukoodi on jo käytetty",
|
||||
"The password must contain at least one special character": "Salasanan on sisällettävä vähintään yksi erikoismerkki",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Salasanan on sisällettävä vähintään yksi iso kirjain, yksi pieni kirjain ja yksi numero",
|
||||
"The password must have at least 6 characters": "Salasanassa on oltava vähintään 6 merkkiä",
|
||||
"The password must have at least 8 characters": "Salasanassa on oltava vähintään 8 merkkiä",
|
||||
"The password must not contain any repeated characters": "Salasana ei saa sisältää toistuvia merkkejä",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Käyttäjä on poistettu eikä sitä voi käyttää kirjautumiseen, ota yhteyttä järjestelmänvalvojaan",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Käyttäjän kirjautuminen on estetty, ota yhteyttä ylläpitäjään",
|
||||
"The user: %s doesn't exist in LDAP server": "Käyttäjä: %s ei ole olemassa LDAP-palvelimessa",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "tämä toiminto vaatii ylläpitäjän suorittamista"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Kutsua %s ei ole olemassa"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-palvelin on olemassa"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA-sähköposti on käytössä, mutta sähköposti on tyhjä",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-puhelin on käytössä, mutta puhelinnumero on tyhjä",
|
||||
"New password cannot contain blank space.": "Uusi salasana ei voi sisältää välilyöntejä.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Uuden salasanan on oltava erilainen kuin nykyinen salasana",
|
||||
"the user's owner and name should not be empty": "käyttäjän omistaja ja nimi eivät saa olla tyhjiä"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "käyttäjää ei ole olemassa, rekisteröidy ensin"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Tälle käyttäjälle ei löytynyt kirjautumistietoja",
|
||||
"Please call WebAuthnSigninBegin first": "Kutsu ensin WebAuthnSigninBegin"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Le compte pour le fournisseur : %s et le nom d'utilisateur : %s (%s) n'existe pas et n'est pas autorisé à s'inscrire comme nouveau compte, veuillez contacter votre support informatique",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Le compte du fournisseur : %s et le nom d'utilisateur : %s (%s) sont déjà liés à un autre compte : %s (%s)",
|
||||
"The application: %s does not exist": "L'application : %s n'existe pas",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "L'application: %s a désactivé la connexion des utilisateurs",
|
||||
"The group: %s does not exist": "Le groupe : %s n'existe pas",
|
||||
"The login method: login with LDAP is not enabled for the application": "La méthode de connexion : connexion LDAP n'est pas activée pour l'application",
|
||||
"The login method: login with SMS is not enabled for the application": "La méthode de connexion : connexion par SMS n'est pas activée pour l'application",
|
||||
"The login method: login with email is not enabled for the application": "La méthode de connexion : connexion par e-mail n'est pas activée pour l'application",
|
||||
"The login method: login with face is not enabled for the application": "La méthode de connexion : connexion par visage n'est pas activée pour l'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": "L'organisation : %s n'existe pas",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "L'organisation: %s a désactivé la connexion des utilisateurs",
|
||||
"The plan: %s does not exist": "Le plan : %s n'existe pas",
|
||||
"The pricing: %s does not exist": "Le tarif : %s n'existe pas",
|
||||
"The pricing: %s does not have plan: %s": "Le tarif : %s n'a pas de plan : %s",
|
||||
"The provider: %s does not exist": "Le fournisseur : %s n'existe pas",
|
||||
"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",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Veuillez vous inscrire avec le nom d'utilisateur correspondant au code d'invitation",
|
||||
"Session outdated, please login again": "Session expirée, veuillez vous connecter à nouveau",
|
||||
"The invitation code has already been used": "Le code d'invitation a déjà été utilisé",
|
||||
"The password must contain at least one special character": "Le mot de passe doit contenir au moins un caractère spécial",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Le mot de passe doit contenir au moins une lettre majuscule, une lettre minuscule et un chiffre",
|
||||
"The password must have at least 6 characters": "Le mot de passe doit contenir au moins 6 caractères",
|
||||
"The password must have at least 8 characters": "Le mot de passe doit contenir au moins 8 caractères",
|
||||
"The password must not contain any repeated characters": "Le mot de passe ne doit pas contenir de caractères répétés",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "L'utilisateur a été supprimé et ne peut pas être utilisé pour se connecter, veuillez contacter l'administrateur",
|
||||
"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",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "cette opération nécessite un administrateur pour être effectuée"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "L'invitation %s n'existe pas"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Le serveur LDAP existe"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "L'authentification MFA par e-mail est activée mais l'e-mail est vide",
|
||||
"MFA phone is enabled but phone number is empty": "L'authentification MFA par téléphone est activée mais le numéro de téléphone est vide",
|
||||
"New password cannot contain blank space.": "Le nouveau mot de passe ne peut pas contenir d'espace.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Le nouveau mot de passe doit être différent de votre mot de passe actuel",
|
||||
"the user's owner and name should not be empty": "le propriétaire et le nom de l'utilisateur ne doivent pas être vides"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "L'utilisateur n'existe pas, veuillez vous inscrire d'abord"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Aucune information d'identification trouvée pour cet utilisateur",
|
||||
"Please call WebAuthnSigninBegin first": "Veuillez d'abord appeler WebAuthnSigninBegin"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "החשבון עבור ספק: %s ושם משתמש: %s (%s) אינו קיים ואינו מאופשר להרשמה כחשבון חדש, אנא צור קשר עם התמיכה הטכנית",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "החשבון עבור ספק: %s ושם משתמש: %s (%s) כבר מקושר לחשבון אחר: %s (%s)",
|
||||
"The application: %s does not exist": "האפליקציה: %s אינה קיימת",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "האפליקציה: %s השביתה כניסת משתמשים",
|
||||
"The group: %s does not exist": "הקבוצה: %s לא קיימת",
|
||||
"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": "שיטת הכניסה: כניסה עם SMS אינה מאופשרת לאפליקציה",
|
||||
"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 organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "הארגון: %s השבית כניסת משתמשים",
|
||||
"The plan: %s does not exist": "התוכנית: %s לא קיימת",
|
||||
"The pricing: %s does not exist": "התמחור: %s לא קיים",
|
||||
"The pricing: %s does not have plan: %s": "התמחור: %s אין לו תוכנית: %s",
|
||||
"The provider: %s does not exist": "הספק: %s אינו קיים",
|
||||
"The provider: %s is not enabled for the application": "הספק: %s אינו מאופשר לאפליקציה",
|
||||
"Unauthorized operation": "פעולה לא מורשית",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "אנא הרשם באמצעות שם המשתמש התואם לקוד ההזמנה",
|
||||
"Session outdated, please login again": "הסשן פג תוקף, אנא התחבר שוב",
|
||||
"The invitation code has already been used": "קוד ההזמנה כבר נוצל",
|
||||
"The password must contain at least one special character": "הסיסמה חייבת להכיל לפחות תו מיוחד אחד",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "הסיסמה חייבת להכיל לפחות אות גדולה אחת, אות קטנה אחת וספרה אחת",
|
||||
"The password must have at least 6 characters": "הסיסמה חייבת להכיל לפחות 6 תווים",
|
||||
"The password must have at least 8 characters": "הסיסמה חייבת להכיל לפחות 8 תווים",
|
||||
"The password must not contain any repeated characters": "הסיסמה אינה יכולה להכיל תווים חוזרים",
|
||||
"The user has been deleted and cannot be used 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": "המשתמש: %s אינו קיים בשרת ה-LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "פעולה זו דורשת מנהל לביצוע"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "ההזמנה %s אינה קיימת"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "שרת LDAP קיים"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA דוא\"ל מופעל אך הדוא\"ל ריק",
|
||||
"MFA phone is enabled but phone number is empty": "MFA טלפון מופעל אך מספר הטלפון ריק",
|
||||
"New password cannot contain blank space.": "הסיסמה החדשה אינה יכולה להכיל רווחים.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "הסיסמה החדשה חייבת להיות שונה מהסיסמה הנוכחית שלך",
|
||||
"the user's owner and name should not be empty": "הבעלים והשם של המשתמש אינם יכולים להיות ריקים"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "המשתמש אינו קיים, אנא הרשם תחילה"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "לא נמצאו אישורים עבור משתמש זה",
|
||||
"Please call WebAuthnSigninBegin first": "אנא קרא ל-WebAuthnSigninBegin תחילה"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Akun untuk penyedia: %s dan nama pengguna: %s (%s) tidak ada dan tidak diizinkan untuk mendaftar sebagai akun baru, silakan hubungi dukungan IT Anda",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Akun untuk penyedia: %s dan username: %s (%s) sudah terhubung dengan akun lain: %s (%s)",
|
||||
"The application: %s does not exist": "Aplikasi: %s tidak ada",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Aplikasi: %s telah menonaktifkan pengguna untuk masuk",
|
||||
"The group: %s does not exist": "Grup: %s tidak ada",
|
||||
"The login method: login with LDAP is not enabled for the application": "Metode masuk: masuk dengan LDAP tidak diaktifkan untuk aplikasi ini",
|
||||
"The login method: login with SMS is not enabled for the application": "Metode masuk: masuk dengan SMS tidak diaktifkan untuk aplikasi ini",
|
||||
"The login method: login with email is not enabled for the application": "Metode masuk: masuk dengan email tidak diaktifkan untuk aplikasi ini",
|
||||
"The login method: login with face is not enabled for the application": "Metode masuk: masuk dengan wajah tidak diaktifkan untuk aplikasi ini",
|
||||
"The login method: login with password is not enabled for the application": "Metode login: login dengan sandi tidak diaktifkan untuk aplikasi tersebut",
|
||||
"The organization: %s does not exist": "Organisasi: %s tidak ada",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Organisasi: %s telah menonaktifkan pengguna untuk masuk",
|
||||
"The plan: %s does not exist": "Rencana: %s tidak ada",
|
||||
"The pricing: %s does not exist": "Harga: %s tidak ada",
|
||||
"The pricing: %s does not have plan: %s": "Harga: %s tidak memiliki rencana: %s",
|
||||
"The provider: %s does not exist": "Penyedia: %s tidak ada",
|
||||
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
|
||||
"Unauthorized operation": "Operasi tidak sah",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Silakan daftar menggunakan nama pengguna yang sesuai dengan kode undangan",
|
||||
"Session outdated, please login again": "Sesi kadaluwarsa, silakan masuk lagi",
|
||||
"The invitation code has already been used": "Kode undangan sudah digunakan",
|
||||
"The password must contain at least one special character": "Kata sandi harus berisi setidaknya satu karakter khusus",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Kata sandi harus berisi setidaknya satu huruf besar, satu huruf kecil dan satu angka",
|
||||
"The password must have at least 6 characters": "Kata sandi harus memiliki setidaknya 6 karakter",
|
||||
"The password must have at least 8 characters": "Kata sandi harus memiliki setidaknya 8 karakter",
|
||||
"The password must not contain any repeated characters": "Kata sandi tidak boleh berisi karakter yang berulang",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Pengguna telah dihapus dan tidak dapat digunakan untuk masuk, silakan hubungi administrator",
|
||||
"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": "Pengguna: %s tidak ada di server LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "operasi ini memerlukan administrator untuk melakukannya"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Undangan %s tidak ada"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Server ldap ada"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "Email MFA diaktifkan tetapi email kosong",
|
||||
"MFA phone is enabled but phone number is empty": "Telepon MFA diaktifkan tetapi nomor telepon kosong",
|
||||
"New password cannot contain blank space.": "Sandi baru tidak boleh mengandung spasi kosong.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Kata sandi baru harus berbeda dari kata sandi Anda saat ini",
|
||||
"the user's owner and name should not be empty": "pemilik dan nama pengguna tidak boleh kosong"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "Pengguna tidak ada, silakan daftar terlebih dahulu"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Tidak ditemukan kredensial untuk pengguna ini",
|
||||
"Please call WebAuthnSigninBegin first": "Harap panggil WebAuthnSigninBegin terlebih dahulu"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Account per provider: %s e utente: %s (%s) non esiste, registrazione non consentita: contatta l'assistenza IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Account per provider: %s e utente: %s (%s) già collegato a un altro account: %s (%s)",
|
||||
"The application: %s does not exist": "L'app: %s non esiste",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "L'applicazione: %s ha disabilitato l'accesso degli utenti",
|
||||
"The group: %s does not exist": "Il gruppo: %s non esiste",
|
||||
"The login method: login with LDAP is not enabled for the application": "Metodo di accesso: login con LDAP non abilitato per l'applicazione",
|
||||
"The login method: login with SMS is not enabled for the application": "Metodo di accesso: login con SMS non abilitato per l'applicazione",
|
||||
"The login method: login with email is not enabled for the application": "Metodo di accesso: login con email non abilitato per l'applicazione",
|
||||
"The login method: login with face is not enabled for the application": "Metodo di accesso: login con riconoscimento facciale non abilitato per l'applicazione",
|
||||
"The login method: login with password is not enabled for the application": "Login con password non abilitato per questa app",
|
||||
"The organization: %s does not exist": "L'organizzazione: %s non esiste",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "L'organizzazione: %s ha disabilitato l'accesso degli utenti",
|
||||
"The plan: %s does not exist": "Il piano: %s non esiste",
|
||||
"The pricing: %s does not exist": "Il prezzo: %s non esiste",
|
||||
"The pricing: %s does not have plan: %s": "Il prezzo: %s non ha il piano: %s",
|
||||
"The provider: %s does not exist": "Il provider: %s non esiste",
|
||||
"The provider: %s is not enabled for the application": "Il provider: %s non è abilitato per l'app",
|
||||
"Unauthorized operation": "Operazione non autorizzata",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Registrati con il nome utente corrispondente al codice di invito",
|
||||
"Session outdated, please login again": "Sessione scaduta, rieffettua il login",
|
||||
"The invitation code has already been used": "Il codice di invito è già stato utilizzato",
|
||||
"The password must contain at least one special character": "La password deve contenere almeno un carattere speciale",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "La password deve contenere almeno una lettera maiuscola, una lettera minuscola e una cifra",
|
||||
"The password must have at least 6 characters": "La password deve avere almeno 6 caratteri",
|
||||
"The password must have at least 8 characters": "La password deve avere almeno 8 caratteri",
|
||||
"The password must not contain any repeated characters": "La password non deve contenere caratteri ripetuti",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "L'utente è stato eliminato e non può essere utilizzato per accedere, contattare l'amministratore",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Utente bloccato, contatta l'amministratore",
|
||||
"The user: %s doesn't exist in LDAP server": "L'utente: %s non esiste nel server LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "questa operazione richiede un amministratore"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "L'invito %s non esiste"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Server LDAP esistente"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "L'email MFA è abilitata ma l'email è vuota",
|
||||
"MFA phone is enabled but phone number is empty": "Il telefono MFA è abilitato ma il numero di telefono è vuoto",
|
||||
"New password cannot contain blank space.": "Nuova password non può contenere spazi",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "La nuova password deve essere diversa dalla password attuale",
|
||||
"the user's owner and name should not be empty": "il proprietario e il nome dell'utente non devono essere vuoti"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "Utente inesistente, registrati prima"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Nessuna credenziale trovata per questo utente",
|
||||
"Please call WebAuthnSigninBegin first": "Chiamare prima WebAuthnSigninBegin"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "プロバイダー名:%sとユーザー名:%s(%s)のアカウントは存在しません。新しいアカウントとしてサインアップすることはできません。 ITサポートに連絡してください",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "プロバイダのアカウント:%s とユーザー名:%s (%s) は既に別のアカウント:%s (%s) にリンクされています",
|
||||
"The application: %s does not exist": "アプリケーション: %sは存在しません",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "アプリケーション: %s はユーザーのサインインを無効にしました",
|
||||
"The group: %s does not exist": "グループ: %sは存在しません",
|
||||
"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": "このアプリケーションでは SMS ログインは有効になっていません",
|
||||
"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 organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "組織: %s はユーザーのサインインを無効にしました",
|
||||
"The plan: %s does not exist": "プラン: %sは存在しません",
|
||||
"The pricing: %s does not exist": "料金: %sは存在しません",
|
||||
"The pricing: %s does not have plan: %s": "料金: %sにはプラン%sがありません",
|
||||
"The provider: %s does not exist": "プロバイダ「%s」は存在しません",
|
||||
"The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません",
|
||||
"Unauthorized operation": "不正操作",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "招待コードに対応するユーザー名で登録してください",
|
||||
"Session outdated, please login again": "セッションが期限切れになりました。再度ログインしてください",
|
||||
"The invitation code has already been used": "この招待コードは既に使用されています",
|
||||
"The password must contain at least one special character": "パスワードには少なくとも1つの特殊文字が必要です",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "パスワードには少なくとも1つの大文字、1つの小文字、1つの数字が必要です",
|
||||
"The password must have at least 6 characters": "パスワードは少なくとも6文字必要です",
|
||||
"The password must have at least 8 characters": "パスワードは少なくとも8文字必要です",
|
||||
"The password must not contain any repeated characters": "パスワードに繰り返し文字を含めることはできません",
|
||||
"The user has been deleted and cannot be used 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": "ユーザー「%s」は LDAP サーバーに存在しません",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "この操作は管理者権限が必要です"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "招待 %s は存在しません"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAPサーバーは存在します"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA メールが有効になっていますが、メールアドレスが空です",
|
||||
"MFA phone is enabled but phone number is empty": "MFA 電話番号が有効になっていますが、電話番号が空です",
|
||||
"New password cannot contain blank space.": "新しいパスワードにはスペースを含めることはできません。",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "新しいパスワードは現在のパスワードと異なる必要があります",
|
||||
"the user's owner and name should not be empty": "ユーザーのオーナーと名前は空にできません"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "ユーザーは存在しません。まず登録してください"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "このユーザーの認証情報が見つかりませんでした",
|
||||
"Please call WebAuthnSigninBegin first": "最初にWebAuthnSigninBeginを呼び出してください"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Account voor provider: %s en gebruikersnaam: %s (%s) bestaat niet en mag niet registreren, contacteer IT-support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Account voor provider: %s en gebruikersnaam: %s (%s) is al gelinkt aan ander account: %s (%s)",
|
||||
"The application: %s does not exist": "Applicatie: %s bestaat niet",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Қолданба: %s пайдаланушылардың кіруін өшірді",
|
||||
"The group: %s does not exist": "Топ: %s жоқ",
|
||||
"The login method: login with LDAP is not enabled for the application": "Inloggen via LDAP is niet ingeschakeld voor deze applicatie",
|
||||
"The login method: login with SMS is not enabled for the application": "Inloggen via SMS is niet ingeschakeld voor deze applicatie",
|
||||
"The login method: login with email is not enabled for the application": "Inloggen via e-mail is niet ingeschakeld voor deze applicatie",
|
||||
"The login method: login with face is not enabled for the application": "Inloggen via gezichtsherkenning is niet ingeschakeld voor deze applicatie",
|
||||
"The login method: login with password is not enabled for the application": "Inloggen via wachtwoord is niet ingeschakeld voor deze applicatie",
|
||||
"The organization: %s does not exist": "Organisatie: %s bestaat niet",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Ұйым: %s пайдаланушылардың кіруін өшірді",
|
||||
"The plan: %s does not exist": "Жоспар: %s жоқ",
|
||||
"The pricing: %s does not exist": "Баға: %s жоқ",
|
||||
"The pricing: %s does not have plan: %s": "Баға: %s жоспары жоқ: %s",
|
||||
"The provider: %s does not exist": "Provider: %s bestaat niet",
|
||||
"The provider: %s is not enabled for the application": "Provider: %s is niet ingeschakeld voor de applicatie",
|
||||
"Unauthorized operation": "Ongeautoriseerde handeling",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Registreer met de gebruikersnaam die hoort bij de uitnodigingscode",
|
||||
"Session outdated, please login again": "Sessie verlopen, gelieve opnieuw in te loggen",
|
||||
"The invitation code has already been used": "Uitnodigingscode is al gebruikt",
|
||||
"The password must contain at least one special character": "Құпия сөз кемінде бір арнайы таңбаны қамтуы керек",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Құпия сөз кемінде бір бас әріпті, бір кіші әріпті және бір санды қамтуы керек",
|
||||
"The password must have at least 6 characters": "Құпия сөз кемінде 6 таңбадан тұруы керек",
|
||||
"The password must have at least 8 characters": "Құпия сөз кемінде 8 таңбадан тұруы керек",
|
||||
"The password must not contain any repeated characters": "Құпия сөз қайталанатын таңбаларды қамтымауы керек",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Пайдаланушы жойылған және кіру үшін пайдалануға болмайды, әкімшіге хабарласыңыз",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Gebruiker mag niet inloggen, contacteer beheerder",
|
||||
"The user: %s doesn't exist in LDAP server": "Gebruiker: %s bestaat niet in LDAP-server",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "deze handeling vereist beheerder"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Шақыру %s жоқ"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-server bestaat al"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA-e-mail is ingeschakeld maar e-mailadres is leeg",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-telefoon is ingeschakeld maar telefoonnummer is leeg",
|
||||
"New password cannot contain blank space.": "Nieuw wachtwoord mag geen spaties bevatten.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Жаңа құпия сөз ағымдағы құпия сөзіңізден өзгеше болуы керек",
|
||||
"the user's owner and name should not be empty": "eigenaar en naam van gebruiker mogen niet leeg zijn"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "gebruiker bestaat niet, registreer eerst"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Бұл пайдаланушы үшін тіркелгі деректері табылмады",
|
||||
"Please call WebAuthnSigninBegin first": "Roep eerst WebAuthnSigninBegin aan"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "공급자 계정 %s과 사용자 이름 %s (%s)는 존재하지 않으며 새 계정으로 등록할 수 없습니다. IT 지원팀에 문의하십시오",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "공급자 계정 %s과 사용자 이름 %s(%s)는 이미 다른 계정 %s(%s)에 연결되어 있습니다",
|
||||
"The application: %s does not exist": "해당 애플리케이션(%s)이 존재하지 않습니다",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "애플리케이션: %s이(가) 사용자 로그인을 비활성화했습니다",
|
||||
"The group: %s does not exist": "그룹: %s이(가) 존재하지 않습니다",
|
||||
"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": "SMS를 이용한 로그인 방식이 이 애플리케이션에 활성화되어 있지 않습니다",
|
||||
"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 organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "조직: %s이(가) 사용자 로그인을 비활성화했습니다",
|
||||
"The plan: %s does not exist": "플랜: %s이(가) 존재하지 않습니다",
|
||||
"The pricing: %s does not exist": "가격: %s이(가) 존재하지 않습니다",
|
||||
"The pricing: %s does not have plan: %s": "가격: %s에는 플랜 %s이(가) 없습니다",
|
||||
"The provider: %s does not exist": "제공자 %s이(가) 존재하지 않습니다",
|
||||
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
|
||||
"Unauthorized operation": "무단 조작",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "초대 코드에 해당하는 사용자 이름으로 가입해 주세요",
|
||||
"Session outdated, please login again": "세션이 만료되었습니다. 다시 로그인해주세요",
|
||||
"The invitation code has already been used": "초대 코드는 이미 사용되었습니다",
|
||||
"The password must contain at least one special character": "비밀번호에는 하나 이상의 특수 문자가 포함되어야 합니다",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "비밀번호에는 하나 이상의 대문자, 소문자 및 숫자가 포함되어야 합니다",
|
||||
"The password must have at least 6 characters": "비밀번호는 최소 6자 이상이어야 합니다",
|
||||
"The password must have at least 8 characters": "비밀번호는 최소 8자 이상이어야 합니다",
|
||||
"The password must not contain any repeated characters": "비밀번호에는 반복되는 문자가 포함될 수 없습니다",
|
||||
"The user has been deleted and cannot be used 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": "LDAP 서버에 사용자 %s이(가) 존재하지 않습니다",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "이 작업은 관리자 권한이 필요합니다"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "초대 %s이(가) 존재하지 않습니다"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP 서버가 존재합니다"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA 이메일이 활성화되었지만 이메일이 비어 있습니다",
|
||||
"MFA phone is enabled but phone number is empty": "MFA 전화번호가 활성화되었지만 전화번호가 비어 있습니다",
|
||||
"New password cannot contain blank space.": "새 비밀번호에는 공백이 포함될 수 없습니다.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "새 비밀번호는 현재 비밀번호와 달라야 합니다",
|
||||
"the user's owner and name should not be empty": "사용자의 소유자와 이름은 비워둘 수 없습니다"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "사용자가 존재하지 않습니다. 먼저 회원 가입 해주세요"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "이 사용자에 대한 자격 증명을 찾을 수 없습니다",
|
||||
"Please call WebAuthnSigninBegin first": "WebAuthnSigninBegin을 먼저 호출해주세요"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Akaun untuk pembekal: %s dan nama pengguna: %s (%s) tidak wujud dan tidak dibenarkan daftar, sila hubungi sokongan IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Akaun untuk pembekal: %s dan nama pengguna: %s (%s) sudah dipautkan kepada akaun lain: %s (%s)",
|
||||
"The application: %s does not exist": "Aplikasi: %s tidak wujud",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Aplikasi: %s telah melumpuhkan pengguna untuk log masuk",
|
||||
"The group: %s does not exist": "Kumpulan: %s tidak wujud",
|
||||
"The login method: login with LDAP is not enabled for the application": "Kaedah log masuk LDAP tidak dibenarkan untuk aplikasi ini",
|
||||
"The login method: login with SMS is not enabled for the application": "Kaedah log masuk SMS tidak dibenarkan untuk aplikasi ini",
|
||||
"The login method: login with email is not enabled for the application": "Kaedah log masuk emel tidak dibenarkan untuk aplikasi ini",
|
||||
"The login method: login with face is not enabled for the application": "Kaedah log masuk muka tidak dibenarkan untuk aplikasi ini",
|
||||
"The login method: login with password is not enabled for the application": "Kaedah log masuk kata laluan tidak dibenarkan untuk aplikasi ini",
|
||||
"The organization: %s does not exist": "Organisasi: %s tidak wujud",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Organisasi: %s telah melumpuhkan pengguna untuk log masuk",
|
||||
"The plan: %s does not exist": "Pelan: %s tidak wujud",
|
||||
"The pricing: %s does not exist": "Harga: %s tidak wujud",
|
||||
"The pricing: %s does not have plan: %s": "Harga: %s tidak mempunyai pelan: %s",
|
||||
"The provider: %s does not exist": "Pembekal: %s tidak wujud",
|
||||
"The provider: %s is not enabled for the application": "Pembekal: %s tidak dibenarkan untuk aplikasi ini",
|
||||
"Unauthorized operation": "Operasi tidak dibenarkan",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Sila daftar dengan nama pengguna yang sepadan dengan kod jemputan",
|
||||
"Session outdated, please login again": "Sesi tamat, sila log masuk semula",
|
||||
"The invitation code has already been used": "Kod jemputan sudah digunakan",
|
||||
"The password must contain at least one special character": "Kata laluan mesti mengandungi sekurang-kurangnya satu aksara khas",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Kata laluan mesti mengandungi sekurang-kurangnya satu huruf besar, satu huruf kecil dan satu digit",
|
||||
"The password must have at least 6 characters": "Kata laluan mesti mempunyai sekurang-kurangnya 6 aksara",
|
||||
"The password must have at least 8 characters": "Kata laluan mesti mempunyai sekurang-kurangnya 8 aksara",
|
||||
"The password must not contain any repeated characters": "Kata laluan tidak boleh mengandungi aksara berulang",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Pengguna telah dipadamkan dan tidak boleh digunakan untuk log masuk, sila hubungi pentadbir",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Pengguna dilarang log masuk, sila hubungi pentadbir",
|
||||
"The user: %s doesn't exist in LDAP server": "Pengguna: %s tidak wujud dalam pelayan LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "operasi ini perlukan pentadbir untuk jalankan"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Jemputan %s tidak wujud"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Pelayan LDAP sudah wujud"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA emel dibenarkan tetapi emel kosong",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefon dibenarkan tetapi nombor telefon kosong",
|
||||
"New password cannot contain blank space.": "Kata laluan baharu tidak boleh ada ruang kosong.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Kata laluan baru mesti berbeza daripada kata laluan semasa anda",
|
||||
"the user's owner and name should not be empty": "pemilik dan nama pengguna tidak boleh kosong"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "pengguna tidak wujud, sila daftar dahulu"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Tiada kelayakan dijumpai untuk pengguna ini",
|
||||
"Please call WebAuthnSigninBegin first": "Sila panggil WebAuthnSigninBegin dahulu"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Gebruiker bestaat niet; aanmelden niet toegestaan, neem contact op met IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Account al gekoppeld aan andere gebruiker: %s (%s)",
|
||||
"The application: %s does not exist": "Applicatie %s bestaat niet",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "De applicatie: %s heeft het aanmelden van gebruikers uitgeschakeld",
|
||||
"The group: %s does not exist": "De groep: %s bestaat niet",
|
||||
"The login method: login with LDAP is not enabled for the application": "LDAP-login uitgeschakeld voor deze app",
|
||||
"The login method: login with SMS is not enabled for the application": "SMS-login uitgeschakeld voor deze app",
|
||||
"The login method: login with email is not enabled for the application": "E-mail-login uitgeschakeld voor deze app",
|
||||
"The login method: login with face is not enabled for the application": "Face-login uitgeschakeld voor deze app",
|
||||
"The login method: login with password is not enabled for the application": "Wachtwoord-login uitgeschakeld voor deze app",
|
||||
"The organization: %s does not exist": "Organisatie %s bestaat niet",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "De organisatie: %s heeft het aanmelden van gebruikers uitgeschakeld",
|
||||
"The plan: %s does not exist": "Het plan: %s bestaat niet",
|
||||
"The pricing: %s does not exist": "De prijsstelling: %s bestaat niet",
|
||||
"The pricing: %s does not have plan: %s": "De prijsstelling: %s heeft geen plan: %s",
|
||||
"The provider: %s does not exist": "Provider %s bestaat niet",
|
||||
"The provider: %s is not enabled for the application": "Provider %s uitgeschakeld voor deze app",
|
||||
"Unauthorized operation": "Niet toegestane handeling",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Registreer met de gebruikersnaam die bij de code hoort",
|
||||
"Session outdated, please login again": "Sessie verlopen, log opnieuw in",
|
||||
"The invitation code has already been used": "Code al gebruikt",
|
||||
"The password must contain at least one special character": "Het wachtwoord moet minstens één speciaal teken bevatten",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Het wachtwoord moet minstens één hoofdletter, één kleine letter en één cijfer bevatten",
|
||||
"The password must have at least 6 characters": "Het wachtwoord moet minstens 6 tekens bevatten",
|
||||
"The password must have at least 8 characters": "Het wachtwoord moet minstens 8 tekens bevatten",
|
||||
"The password must not contain any repeated characters": "Het wachtwoord mag geen herhaalde tekens bevatten",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "De gebruiker is verwijderd en kan niet worden gebruikt om in te loggen, neem contact op met de beheerder",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Inloggen verboden, neem contact op met beheerder",
|
||||
"The user: %s doesn't exist in LDAP server": "Gebruiker %s ontbreekt in LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "Alleen beheerder kan deze handeling uitvoeren"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Uitnodiging %s bestaat niet"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-server bestaat al"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA-e-mail ingeschakeld maar e-mailadres leeg",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-telefoon ingeschakeld maar nummer leeg",
|
||||
"New password cannot contain blank space.": "Nieuw wachtwoord mag geen spaties bevatten",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Het nieuwe wachtwoord moet verschillen van uw huidige wachtwoord",
|
||||
"the user's owner and name should not be empty": "Eigenaar en naam van gebruiker mogen niet leeg zijn"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "Gebruiker bestaat niet; meld je eerst aan"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Geen inloggegevens gevonden voor deze gebruiker",
|
||||
"Please call WebAuthnSigninBegin first": "Roep eerst WebAuthnSigninBegin aan"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Konto dla dostawcy: %s i nazwy użytkownika: %s (%s) nie istnieje i nie można się zarejestrować jako nowe konto, skontaktuj się z pomocą IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Konto dla dostawcy: %s i nazwy użytkownika: %s (%s) jest już powiązane z innym kontem: %s (%s)",
|
||||
"The application: %s does not exist": "Aplikacja: %s nie istnieje",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Aplikacja: %s wyłączyła logowanie użytkowników",
|
||||
"The group: %s does not exist": "Grupa: %s nie istnieje",
|
||||
"The login method: login with LDAP is not enabled for the application": "Metoda logowania: logowanie przez LDAP nie jest włączone dla aplikacji",
|
||||
"The login method: login with SMS is not enabled for the application": "Metoda logowania: logowanie przez SMS nie jest włączona dla aplikacji",
|
||||
"The login method: login with email is not enabled for the application": "Metoda logowania: logowanie przez email nie jest włączona dla aplikacji",
|
||||
"The login method: login with face is not enabled for the application": "Metoda logowania: logowanie przez twarz nie jest włączona dla aplikacji",
|
||||
"The login method: login with password is not enabled for the application": "Metoda logowania: logowanie przez hasło nie jest włączone dla aplikacji",
|
||||
"The organization: %s does not exist": "Organizacja: %s nie istnieje",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Organizacja: %s wyłączyła logowanie użytkowników",
|
||||
"The plan: %s does not exist": "Plan: %s nie istnieje",
|
||||
"The pricing: %s does not exist": "Cennik: %s nie istnieje",
|
||||
"The pricing: %s does not have plan: %s": "Cennik: %s nie ma planu: %s",
|
||||
"The provider: %s does not exist": "Dostawca: %s nie istnieje",
|
||||
"The provider: %s is not enabled for the application": "Dostawca: %s nie jest włączony dla aplikacji",
|
||||
"Unauthorized operation": "Nieautoryzowana operacja",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Zarejestruj się używając nazwy użytkownika odpowiadającej kodowi zaproszenia",
|
||||
"Session outdated, please login again": "Sesja wygasła, zaloguj się ponownie",
|
||||
"The invitation code has already been used": "Kod zaproszenia został już wykorzystany",
|
||||
"The password must contain at least one special character": "Hasło musi zawierać co najmniej jeden znak specjalny",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Hasło musi zawierać co najmniej jedną wielką literę, jedną małą literę i jedną cyfrę",
|
||||
"The password must have at least 6 characters": "Hasło musi zawierać co najmniej 6 znaków",
|
||||
"The password must have at least 8 characters": "Hasło musi zawierać co najmniej 8 znaków",
|
||||
"The password must not contain any repeated characters": "Hasło nie może zawierać powtarzających się znaków",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Użytkownik został usunięty i nie może być używany do logowania, skontaktuj się z administratorem",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Użytkownikowi zabroniono logowania, skontaktuj się z administratorem",
|
||||
"The user: %s doesn't exist in LDAP server": "Użytkownik: %s nie istnieje w serwerze LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "ta operacja wymaga administratora do wykonania"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Zaproszenie %s nie istnieje"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Serwer LDAP istnieje"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA email jest włączone, ale email jest pusty",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefon jest włączony, ale numer telefonu jest pusty",
|
||||
"New password cannot contain blank space.": "Nowe hasło nie może zawierać spacji.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Nowe hasło musi różnić się od obecnego hasła",
|
||||
"the user's owner and name should not be empty": "właściciel i nazwa użytkownika nie powinny być puste"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "użytkownik nie istnieje, najpierw się zarejestruj"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Nie znaleziono danych uwierzytelniających dla tego użytkownika",
|
||||
"Please call WebAuthnSigninBegin first": "Najpierw wywołaj WebAuthnSigninBegin"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "A conta do provedor: %s e nome de usuário: %s (%s) já está vinculada a outra conta: %s (%s)",
|
||||
"The application: %s does not exist": "O aplicativo: %s não existe",
|
||||
"The application: %s has disabled users to signin": "O aplicativo: %s desativou o login de usuários",
|
||||
"The group: %s does not exist": "O grupo: %s não existe",
|
||||
"The login method: login with LDAP is not enabled for the application": "O método de login com LDAP não está habilitado para o aplicativo",
|
||||
"The login method: login with SMS is not enabled for the application": "O método de login com SMS não está habilitado para o aplicativo",
|
||||
"The login method: login with email is not enabled for the application": "O método de login com e-mail não está habilitado para o aplicativo",
|
||||
@@ -24,6 +25,9 @@
|
||||
"The login method: login with password is not enabled for the application": "O método de login com senha não está habilitado para o aplicativo",
|
||||
"The organization: %s does not exist": "A organização: %s não existe",
|
||||
"The organization: %s has disabled users to signin": "A organização: %s desativou o login de usuários",
|
||||
"The plan: %s does not exist": "O plano: %s não existe",
|
||||
"The pricing: %s does not exist": "O preço: %s não existe",
|
||||
"The pricing: %s does not have plan: %s": "O preço: %s não tem o plano: %s",
|
||||
"The provider: %s does not exist": "O provedor: %s não existe",
|
||||
"The provider: %s is not enabled for the application": "O provedor: %s não está habilitado para o aplicativo",
|
||||
"Unauthorized operation": "Operação não autorizada",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Por favor, registre-se usando o nome de usuário correspondente ao código de convite",
|
||||
"Session outdated, please login again": "Sessão expirada, faça login novamente",
|
||||
"The invitation code has already been used": "O código de convite já foi utilizado",
|
||||
"The password must contain at least one special character": "A senha deve conter pelo menos um caractere especial",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "A senha deve conter pelo menos uma letra maiúscula, uma letra minúscula e um dígito",
|
||||
"The password must have at least 6 characters": "A senha deve ter pelo menos 6 caracteres",
|
||||
"The password must have at least 8 characters": "A senha deve ter pelo menos 8 caracteres",
|
||||
"The password must not contain any repeated characters": "A senha não deve conter caracteres repetidos",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "O usuário foi excluído e não pode ser usado para fazer login, entre em contato com o administrador",
|
||||
"The user is forbidden to sign in, please contact the administrator": "O usuário está proibido de entrar, entre em contato com o administrador",
|
||||
"The user: %s doesn't exist in LDAP server": "O usuário: %s não existe no servidor LDAP",
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Аккаунт для провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован как новый аккаунт. Пожалуйста, обратитесь в службу поддержки IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Аккаунт поставщика: %s и имя пользователя: %s (%s) уже связаны с другим аккаунтом: %s (%s)",
|
||||
"The application: %s does not exist": "Приложение: %s не существует",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Приложение: %s отключило вход пользователей",
|
||||
"The group: %s does not exist": "Группа: %s не существует",
|
||||
"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": "Метод входа через SMS отключен для этого приложения",
|
||||
"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 organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Организация: %s отключила вход пользователей",
|
||||
"The plan: %s does not exist": "План: %s не существует",
|
||||
"The pricing: %s does not exist": "Тариф: %s не существует",
|
||||
"The pricing: %s does not have plan: %s": "Тариф: %s не имеет план: %s",
|
||||
"The provider: %s does not exist": "Провайдер: %s не существует",
|
||||
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
|
||||
"Unauthorized operation": "Несанкционированная операция",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь, используя имя пользователя, соответствующее коду приглашения",
|
||||
"Session outdated, please login again": "Сессия устарела, пожалуйста, войдите снова",
|
||||
"The invitation code has already been used": "Код приглашения уже использован",
|
||||
"The password must contain at least one special character": "Пароль должен содержать хотя бы один специальный символ",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Пароль должен содержать хотя бы одну заглавную букву, одну строчную букву и одну цифру",
|
||||
"The password must have at least 6 characters": "Пароль должен содержать не менее 6 символов",
|
||||
"The password must have at least 8 characters": "Пароль должен содержать не менее 8 символов",
|
||||
"The password must not contain any repeated characters": "Пароль не должен содержать повторяющихся символов",
|
||||
"The user has been deleted and cannot be used 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": "Пользователь: %s не существует на сервере LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "эта операция требует прав администратора"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Приглашение %s не существует"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-сервер существует"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA по электронной почте включен, но электронная почта не указана",
|
||||
"MFA phone is enabled but phone number is empty": "MFA по телефону включен, но номер телефона не указан",
|
||||
"New password cannot contain blank space.": "Новый пароль не может содержать пробелы.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Новый пароль должен отличаться от текущего пароля",
|
||||
"the user's owner and name should not be empty": "владелец и имя пользователя не должны быть пустыми"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Учетные данные для этого пользователя не найдены",
|
||||
"Please call WebAuthnSigninBegin first": "Пожалуйста, сначала вызовите WebAuthnSigninBegin"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) neexistuje a nie je povolené zaregistrovať nový účet, prosím kontaktujte vašu IT podporu",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) je už prepojený s iným účtom: %s (%s)",
|
||||
"The application: %s does not exist": "Aplikácia: %s neexistuje",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Aplikácia: %s zakázala prihlasovanie používateľov",
|
||||
"The group: %s does not exist": "Skupina: %s neexistuje",
|
||||
"The login method: login with LDAP is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou LDAP nie je pre aplikáciu povolená",
|
||||
"The login method: login with SMS is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou SMS nie je pre aplikáciu povolená",
|
||||
"The login method: login with email is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou e-mailu nie je pre aplikáciu povolená",
|
||||
"The login method: login with face is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou tváre nie je pre aplikáciu povolená",
|
||||
"The login method: login with password is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou hesla nie je pre aplikáciu povolená",
|
||||
"The organization: %s does not exist": "Organizácia: %s neexistuje",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Organizácia: %s zakázala prihlasovanie používateľov",
|
||||
"The plan: %s does not exist": "Plán: %s neexistuje",
|
||||
"The pricing: %s does not exist": "Cenník: %s neexistuje",
|
||||
"The pricing: %s does not have plan: %s": "Cenník: %s nemá plán: %s",
|
||||
"The provider: %s does not exist": "Poskytovatel: %s neexistuje",
|
||||
"The provider: %s is not enabled for the application": "Poskytovateľ: %s nie je pre aplikáciu povolený",
|
||||
"Unauthorized operation": "Neautorizovaná operácia",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Prosím, zaregistrujte sa pomocou používateľského mena zodpovedajúceho kódu pozvania",
|
||||
"Session outdated, please login again": "Relácia je zastaraná, prosím, prihláste sa znova",
|
||||
"The invitation code has already been used": "Kód pozvania už bol použitý",
|
||||
"The password must contain at least one special character": "Heslo musí obsahovať aspoň jeden špeciálny znak",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Heslo musí obsahovať aspoň jedno veľké písmeno, jedno malé písmeno a jednu číslicu",
|
||||
"The password must have at least 6 characters": "Heslo musí mať aspoň 6 znakov",
|
||||
"The password must have at least 8 characters": "Heslo musí mať aspoň 8 znakov",
|
||||
"The password must not contain any repeated characters": "Heslo nesmie obsahovať opakujúce sa znaky",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Používateľ bol odstránený a nie je možné ho použiť na prihlásenie, kontaktujte prosím správcu",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Používateľovi je zakázané prihlásenie, prosím, kontaktujte administrátora",
|
||||
"The user: %s doesn't exist in LDAP server": "Používateľ: %s neexistuje na LDAP serveri",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "táto operácia vyžaduje vykonanie administrátorom"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Pozvánka %s neexistuje"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP server existuje"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA e-mail je zapnutý, ale e-mail je prázdny",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefón je zapnutý, ale telefónne číslo je prázdne",
|
||||
"New password cannot contain blank space.": "Nové heslo nemôže obsahovať medzery.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Nové heslo sa musí líšiť od vášho aktuálneho hesla",
|
||||
"the user's owner and name should not be empty": "vlastník a meno používateľa nesmú byť prázdne"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "používateľ neexistuje, prosím, zaregistrujte sa najskôr"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Pre tohto používateľa sa nenašli žiadne prihlasovacie údaje",
|
||||
"Please call WebAuthnSigninBegin first": "Najskôr prosím zavolajte WebAuthnSigninBegin"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Kontot för leverantör: %s och användarnamn: %s (%s) finns inte och det är inte tillåtet att registrera ett nytt konto, kontakta din IT-support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Kontot för leverantör: %s och användarnamn: %s (%s) är redan länkat till ett annat konto: %s (%s)",
|
||||
"The application: %s does not exist": "Applikationen: %s finns inte",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Applikationen: %s har inaktiverat användarinloggning",
|
||||
"The group: %s does not exist": "Gruppen: %s finns inte",
|
||||
"The login method: login with LDAP is not enabled for the application": "Inloggningsmetoden: inloggning med LDAP är inte aktiverad för applikationen",
|
||||
"The login method: login with SMS is not enabled for the application": "Inloggningsmetoden: inloggning med SMS är inte aktiverad för applikationen",
|
||||
"The login method: login with email is not enabled for the application": "Inloggningsmetoden: inloggning med e-post är inte aktiverad för applikationen",
|
||||
"The login method: login with face is not enabled for the application": "Inloggningsmetoden: inloggning med ansikte är inte aktiverad för applikationen",
|
||||
"The login method: login with password is not enabled for the application": "Inloggningsmetoden: inloggning med lösenord är inte aktiverad för applikationen",
|
||||
"The organization: %s does not exist": "Organisationen: %s finns inte",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Organisationen: %s har inaktiverat användarinloggning",
|
||||
"The plan: %s does not exist": "Planen: %s finns inte",
|
||||
"The pricing: %s does not exist": "Prissättningen: %s finns inte",
|
||||
"The pricing: %s does not have plan: %s": "Prissättningen: %s har inte plan: %s",
|
||||
"The provider: %s does not exist": "Leverantören: %s finns inte",
|
||||
"The provider: %s is not enabled for the application": "Leverantören: %s är inte aktiverad för applikationen",
|
||||
"Unauthorized operation": "Obehörig åtgärd",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Registrera dig med det användarnamn som motsvarar inbjudningskoden",
|
||||
"Session outdated, please login again": "Sessionen har gått ut, logga in igen",
|
||||
"The invitation code has already been used": "Inbjudningskoden har redan använts",
|
||||
"The password must contain at least one special character": "Lösenordet måste innehålla minst ett specialtecken",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Lösenordet måste innehålla minst en stor bokstav, en liten bokstav och en siffra",
|
||||
"The password must have at least 6 characters": "Lösenordet måste ha minst 6 tecken",
|
||||
"The password must have at least 8 characters": "Lösenordet måste ha minst 8 tecken",
|
||||
"The password must not contain any repeated characters": "Lösenordet får inte innehålla upprepade tecken",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Användaren har tagits bort och kan inte användas för att logga in, kontakta administratören",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Användaren är förbjuden att logga in, kontakta administratören",
|
||||
"The user: %s doesn't exist in LDAP server": "Användaren: %s finns inte i LDAP-servern",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "denna åtgärd kräver administratör för att genomföras"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Inbjudan %s existerar inte"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-servern finns redan"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA-e-post är aktiverat men e-post är tom",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-telefon är aktiverat men telefonnummer är tomt",
|
||||
"New password cannot contain blank space.": "Nytt lösenord får inte innehålla mellanslag.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Det nya lösenordet måste skilja sig från ditt nuvarande lösenord",
|
||||
"the user's owner and name should not be empty": "användarens ägare och namn får inte vara tomma"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "användaren finns inte, registrera dig först"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Inga autentiseringsuppgifter hittades för denna användare",
|
||||
"Please call WebAuthnSigninBegin first": "Anropa WebAuthnSigninBegin först"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Provider: %s ve kullanıcı adı: %s (%s) için hesap mevcut değil ve yeni hesap açılmasına izin verilmiyor, lütfen BT destek ile iletişime geçin",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Provider: %s ve kullanıcı adı: %s (%s) zaten başka bir hesaba bağlı: %s (%s)",
|
||||
"The application: %s does not exist": "Uygulama: %s bulunamadı",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Uygulama: %s kullanıcıların oturum açmasını devre dışı bıraktı",
|
||||
"The group: %s does not exist": "Grup: %s mevcut değil",
|
||||
"The login method: login with LDAP is not enabled for the application": "Uygulama için LDAP ile giriş yöntemi etkin değil",
|
||||
"The login method: login with SMS is not enabled for the application": "Uygulama için SMS ile giriş yöntemi etkin değil",
|
||||
"The login method: login with email is not enabled for the application": "Uygulama için e-posta ile giriş yöntemi etkin değil",
|
||||
"The login method: login with face is not enabled for the application": "Uygulama için yüz ile giriş yöntemi etkin değil",
|
||||
"The login method: login with password is not enabled for the application": "Şifre ile giriş yöntemi bu uygulama için etkin değil",
|
||||
"The organization: %s does not exist": "Organizasyon: %s mevcut değil",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Organizasyon: %s kullanıcıların oturum açmasını devre dışı bıraktı",
|
||||
"The plan: %s does not exist": "Plan: %s mevcut değil",
|
||||
"The pricing: %s does not exist": "Fiyatlandırma: %s mevcut değil",
|
||||
"The pricing: %s does not have plan: %s": "Fiyatlandırma: %s planı yok: %s",
|
||||
"The provider: %s does not exist": "Sağlayıcı: %s mevcut değil",
|
||||
"The provider: %s is not enabled for the application": "Provider: %s bu uygulama için etkin değil",
|
||||
"Unauthorized operation": "Yetkisiz işlem",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Lütfen davet koduna karşılık gelen kullanıcı adıyla kayıt olun",
|
||||
"Session outdated, please login again": "Oturum süresi doldu, lütfen tekrar giriş yapın",
|
||||
"The invitation code has already been used": "Davet kodu zaten kullanılmış",
|
||||
"The password must contain at least one special character": "Şifre en az bir özel karakter içermelidir",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Şifre en az bir büyük harf, bir küçük harf ve bir rakam içermelidir",
|
||||
"The password must have at least 6 characters": "Şifre en az 6 karakter içermelidir",
|
||||
"The password must have at least 8 characters": "Şifre en az 8 karakter içermelidir",
|
||||
"The password must not contain any repeated characters": "Şifre tekrarlanan karakterler içermemelidir",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Kullanıcı silinmiş ve oturum açmak için kullanılamaz, lütfen yöneticiyle iletişime geçin",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Kullanıcı giriş yapmaktan men edildi, lütfen yönetici ile iletişime geçin",
|
||||
"The user: %s doesn't exist in LDAP server": "Kullanıcı: %s LDAP sunucusunda mevcut değil",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "bu işlem yönetici tarafından gerçekleştirilmelidir"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Davetiye %s mevcut değil"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP sunucusu zaten var"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA e-postası etkin ancak e-posta boş",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefonu etkin ancak telefon numarası boş",
|
||||
"New password cannot contain blank space.": "Yeni şifre boşluk içeremez.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Yeni şifre mevcut şifrenizden farklı olmalıdır",
|
||||
"the user's owner and name should not be empty": "kullanıcının sahibi ve adı boş olmamalıdır"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "kullanıcı mevcut değil, lütfen önce kaydolun"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Bu kullanıcı için kimlik bilgisi bulunamadı",
|
||||
"Please call WebAuthnSigninBegin first": "Lütfen önce WebAuthnSigninBegin çağırın"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Обліковий запис для провайдера: %s та імені користувача: %s (%s) не існує і не дозволяється реєструвати як новий, зверніться до IT-підтримки",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Обліковий запис для провайдера: %s та імені користувача: %s (%s) уже пов’язаний з іншим обліковим записом: %s (%s)",
|
||||
"The application: %s does not exist": "Додаток: %s не існує",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Додаток: %s вимкнув вхід користувачів",
|
||||
"The group: %s does not exist": "Група: %s не існує",
|
||||
"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": "Метод входу через SMS не увімкнено для цього додатка",
|
||||
"The login method: login with email is not enabled for the application": "Метод входу через email не увімкнено для цього додатка",
|
||||
"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 organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Організація: %s вимкнула вхід користувачів",
|
||||
"The plan: %s does not exist": "План: %s не існує",
|
||||
"The pricing: %s does not exist": "Тариф: %s не існує",
|
||||
"The pricing: %s does not have plan: %s": "Тариф: %s не має план: %s",
|
||||
"The provider: %s does not exist": "Провайдер: %s не існує",
|
||||
"The provider: %s is not enabled for the application": "Провайдер: %s не увімкнено для цього додатка",
|
||||
"Unauthorized operation": "Неавторизована операція",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Будь ласка, зареєструйтесь, використовуючи ім’я користувача, що відповідає коду запрошення",
|
||||
"Session outdated, please login again": "Сесію застаро, будь ласка, увійдіть знову",
|
||||
"The invitation code has already been used": "Код запрошення вже використано",
|
||||
"The password must contain at least one special character": "Пароль повинен містити принаймні один спеціальний символ",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Пароль повинен містити принаймні одну велику літеру, одну малу літеру та одну цифру",
|
||||
"The password must have at least 6 characters": "Пароль повинен містити принаймні 6 символів",
|
||||
"The password must have at least 8 characters": "Пароль повинен містити принаймні 8 символів",
|
||||
"The password must not contain any repeated characters": "Пароль не повинен містити повторюваних символів",
|
||||
"The user has been deleted and cannot be used 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": "Користувач: %s не існує на сервері LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "ця операція потребує прав адміністратора"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Запрошення %s не існує"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Сервер LDAP існує"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA email увімкнено, але email порожній",
|
||||
"MFA phone is enabled but phone number is empty": "MFA телефон увімкнено, але номер телефону порожній",
|
||||
"New password cannot contain blank space.": "Новий пароль не може містити пробіли.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Новий пароль повинен відрізнятися від поточного пароля",
|
||||
"the user's owner and name should not be empty": "власник ім’я користувача не повинні бути порожніми"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"the user does not exist, please sign up first": "користувача не існує, спочатку зареєструйтесь"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Облікові дані для цього користувача не знайдено",
|
||||
"Please call WebAuthnSigninBegin first": "Спочатку викличте WebAuthnSigninBegin"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,18 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) không tồn tại và không được phép đăng ký như một tài khoản mới, vui lòng liên hệ với bộ phận hỗ trợ công nghệ thông tin của bạn",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) đã được liên kết với tài khoản khác: %s (%s)",
|
||||
"The application: %s does not exist": "Ứng dụng: %s không tồn tại",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The application: %s has disabled users to signin": "Ứng dụng: %s đã vô hiệu hóa đăng nhập của người dùng",
|
||||
"The group: %s does not exist": "Nhóm: %s không tồn tại",
|
||||
"The login method: login with LDAP is not enabled for the application": "Phương thức đăng nhập bằng LDAP chưa được bật cho ứng dụng",
|
||||
"The login method: login with SMS is not enabled for the application": "Phương thức đăng nhập bằng SMS chưa được bật cho ứng dụng",
|
||||
"The login method: login with email is not enabled for the application": "Phương thức đăng nhập bằng email chưa được bật cho ứng dụng",
|
||||
"The login method: login with face is not enabled for the application": "Phương thức đăng nhập bằng khuôn mặt chưa được bật cho ứng dụng",
|
||||
"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": "Tổ chức: %s không tồn tại",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "Tổ chức: %s đã vô hiệu hóa đăng nhập của người dùng",
|
||||
"The plan: %s does not exist": "Kế hoạch: %s không tồn tại",
|
||||
"The pricing: %s does not exist": "Giá: %s không tồn tại",
|
||||
"The pricing: %s does not have plan: %s": "Giá: %s không có kế hoạch: %s",
|
||||
"The provider: %s does not exist": "Nhà cung cấp: %s không tồn tại",
|
||||
"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",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "Vui lòng đăng ký bằng tên người dùng tương ứng với mã mời",
|
||||
"Session outdated, please login again": "Phiên làm việc hết hạn, vui lòng đăng nhập lại",
|
||||
"The invitation code has already been used": "Mã mời đã được sử dụng",
|
||||
"The password must contain at least one special character": "Mật khẩu phải chứa ít nhất một ký tự đặc biệt",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Mật khẩu phải chứa ít nhất một chữ hoa, một chữ thường và một chữ số",
|
||||
"The password must have at least 6 characters": "Mật khẩu phải có ít nhất 6 ký tự",
|
||||
"The password must have at least 8 characters": "Mật khẩu phải có ít nhất 8 ký tự",
|
||||
"The password must not contain any repeated characters": "Mật khẩu không được chứa ký tự lặp lại",
|
||||
"The user has been deleted and cannot be used to sign in, please contact the administrator": "Người dùng đã bị xóa và không thể được sử dụng để đăng nhập, vui lòng liên hệ với quản trị viên",
|
||||
"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": "Người dùng: %s không tồn tại trên máy chủ LDAP",
|
||||
@@ -108,7 +117,7 @@
|
||||
"this operation requires administrator to perform": "thao tác này yêu cầu quản trị viên thực hiện"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
"Invitation %s does not exist": "Lời mời %s không tồn tại"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Máy chủ LDAP tồn tại"
|
||||
@@ -168,7 +177,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA email đã bật nhưng email trống",
|
||||
"MFA phone is enabled but phone number is empty": "MFA điện thoại đã bật nhưng số điện thoại trống",
|
||||
"New password cannot contain blank space.": "Mật khẩu mới không thể chứa dấu trắng.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"The new password must be different from your current password": "Mật khẩu mới phải khác với mật khẩu hiện tại của bạn",
|
||||
"the user's owner and name should not be empty": "chủ sở hữu và tên người dùng không được để trống"
|
||||
},
|
||||
"util": {
|
||||
@@ -192,7 +201,7 @@
|
||||
"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": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Found no credentials for this user": "Không tìm thấy thông tin xác thực cho người dùng này",
|
||||
"Please call WebAuthnSigninBegin first": "Vui lòng gọi WebAuthnSigninBegin trước"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "提供商账户: %s与用户名: %s (%s)已经与其他账户绑定: %s (%s)",
|
||||
"The application: %s does not exist": "应用%s不存在",
|
||||
"The application: %s has disabled users to signin": "应用: %s 禁止用户登录",
|
||||
"The group: %s does not exist": "组: %s不存在",
|
||||
"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": "该应用禁止采用邮箱登录方式",
|
||||
@@ -24,6 +25,9 @@
|
||||
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",
|
||||
"The organization: %s does not exist": "组织: %s 不存在",
|
||||
"The organization: %s has disabled users to signin": "组织: %s 禁止用户登录",
|
||||
"The plan: %s does not exist": "计划: %s不存在",
|
||||
"The pricing: %s does not exist": "定价: %s不存在",
|
||||
"The pricing: %s does not have plan: %s": "定价: %s没有计划: %s",
|
||||
"The provider: %s does not exist": "提供商: %s 不存在",
|
||||
"The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
|
||||
"Unauthorized operation": "未授权的操作",
|
||||
@@ -70,6 +74,11 @@
|
||||
"Please register using the username corresponding to the invitation code": "请使用邀请码关联的用户名注册",
|
||||
"Session outdated, please login again": "会话已过期,请重新登录",
|
||||
"The invitation code has already been used": "邀请码已被使用",
|
||||
"The password must contain at least one special character": "密码必须包含至少一个特殊字符",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "密码必须包含至少一个大写字母、一个小写字母和一个数字",
|
||||
"The password must have at least 6 characters": "密码必须至少包含6个字符",
|
||||
"The password must have at least 8 characters": "密码必须至少包含8个字符",
|
||||
"The password must not contain any repeated characters": "密码不能包含任何重复字符",
|
||||
"The user has been deleted and cannot be used 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": "用户: %s 在LDAP服务器中未找到",
|
||||
|
||||
@@ -157,6 +157,10 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dtUserInfo.OpenId == "" || dtUserInfo.UnionId == "" {
|
||||
return nil, fmt.Errorf(string(data))
|
||||
}
|
||||
|
||||
countryCode, err := util.GetCountryCode(dtUserInfo.StateCode, dtUserInfo.Mobile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
15
idp/lark.go
15
idp/lark.go
@@ -83,8 +83,6 @@ type LarkAccessToken struct {
|
||||
Expire int `json:"expire"`
|
||||
}
|
||||
|
||||
// GetToken use code get access_token (*operation of getting code ought to be done in front)
|
||||
// get more detail via: https://docs.microsoft.com/en-us/linkedIn/shared/authentication/authorization-code-flow?context=linkedIn%2Fcontext&tabs=HTTPS
|
||||
func (idp *LarkIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
params := &struct {
|
||||
AppID string `json:"app_id"`
|
||||
@@ -170,8 +168,6 @@ type LarkUserInfo struct {
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// GetUserInfo use LarkAccessToken gotten before return LinkedInUserInfo
|
||||
// get more detail via: https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context
|
||||
func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
body := &struct {
|
||||
GrantType string `json:"grant_type"`
|
||||
@@ -214,6 +210,15 @@ func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
email = larkUserInfo.Data.EnterpriseEmail
|
||||
}
|
||||
|
||||
// Use fallback mechanism for username: UserId -> UnionId -> OpenId
|
||||
username := larkUserInfo.Data.UserId
|
||||
if username == "" {
|
||||
username = larkUserInfo.Data.UnionId
|
||||
}
|
||||
if username == "" {
|
||||
username = larkUserInfo.Data.OpenId
|
||||
}
|
||||
|
||||
var phoneNumber string
|
||||
var countryCode string
|
||||
if len(larkUserInfo.Data.Mobile) != 0 {
|
||||
@@ -228,7 +233,7 @@ func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
userInfo := UserInfo{
|
||||
Id: larkUserInfo.Data.OpenId,
|
||||
DisplayName: larkUserInfo.Data.Name,
|
||||
Username: larkUserInfo.Data.UserId,
|
||||
Username: username,
|
||||
Email: email,
|
||||
AvatarUrl: larkUserInfo.Data.AvatarUrl,
|
||||
Phone: phoneNumber,
|
||||
|
||||
@@ -67,7 +67,12 @@ func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) (IdProvider, error
|
||||
case "QQ":
|
||||
return NewQqIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
case "WeChat":
|
||||
return NewWeChatIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
if idpInfo.SubType == "Mobile" {
|
||||
return NewWeChatMobileIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
} else {
|
||||
// Default to Web (PC QR code login) for backward compatibility
|
||||
return NewWeChatIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
}
|
||||
case "Facebook":
|
||||
return NewFacebookIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
case "DingTalk":
|
||||
@@ -124,6 +129,8 @@ func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) (IdProvider, error
|
||||
return NewWeb3OnboardIdProvider(), nil
|
||||
case "Twitter":
|
||||
return NewTwitterIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
case "Telegram":
|
||||
return NewTelegramIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
default:
|
||||
if isGothSupport(idpInfo.Type) {
|
||||
return NewGothIdProvider(idpInfo.Type, idpInfo.ClientId, idpInfo.ClientSecret, idpInfo.ClientId2, idpInfo.ClientSecret2, redirectUrl, idpInfo.HostUrl)
|
||||
|
||||
169
idp/telegram.go
Normal file
169
idp/telegram.go
Normal file
@@ -0,0 +1,169 @@
|
||||
// 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 idp
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type TelegramIdProvider struct {
|
||||
Client *http.Client
|
||||
ClientId string
|
||||
ClientSecret string
|
||||
RedirectUrl string
|
||||
}
|
||||
|
||||
func NewTelegramIdProvider(clientId string, clientSecret string, redirectUrl string) *TelegramIdProvider {
|
||||
idp := &TelegramIdProvider{
|
||||
ClientId: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectUrl: redirectUrl,
|
||||
}
|
||||
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *TelegramIdProvider) SetHttpClient(client *http.Client) {
|
||||
idp.Client = client
|
||||
}
|
||||
|
||||
// GetToken validates the Telegram auth data and returns a token
|
||||
// Telegram uses a widget-based authentication, not standard OAuth2
|
||||
// The "code" parameter contains the JSON-encoded auth data from Telegram
|
||||
func (idp *TelegramIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
// Decode the auth data from the code parameter
|
||||
var authData map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(code), &authData); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse Telegram auth data: %v", err)
|
||||
}
|
||||
|
||||
// Verify the data authenticity
|
||||
if err := idp.verifyTelegramAuth(authData); err != nil {
|
||||
return nil, fmt.Errorf("failed to verify Telegram auth data: %v", err)
|
||||
}
|
||||
|
||||
// Create a token with the user ID as access token
|
||||
userId, ok := authData["id"].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid user id in auth data")
|
||||
}
|
||||
|
||||
// Store the complete auth data in the token for later retrieval
|
||||
authDataJson, err := json.Marshal(authData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal auth data: %v", err)
|
||||
}
|
||||
|
||||
token := &oauth2.Token{
|
||||
AccessToken: fmt.Sprintf("telegram_%d", int64(userId)),
|
||||
TokenType: "Bearer",
|
||||
}
|
||||
|
||||
// Store auth data in token extras to avoid additional API calls
|
||||
token = token.WithExtra(map[string]interface{}{
|
||||
"telegram_auth_data": string(authDataJson),
|
||||
})
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// verifyTelegramAuth verifies the authenticity of Telegram auth data
|
||||
// According to Telegram docs: https://core.telegram.org/widgets/login#checking-authorization
|
||||
func (idp *TelegramIdProvider) verifyTelegramAuth(authData map[string]interface{}) error {
|
||||
// Extract hash from auth data
|
||||
hash, ok := authData["hash"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("hash not found in auth data")
|
||||
}
|
||||
|
||||
// Prepare data check string
|
||||
var dataCheckArr []string
|
||||
for key, value := range authData {
|
||||
if key == "hash" {
|
||||
continue
|
||||
}
|
||||
dataCheckArr = append(dataCheckArr, fmt.Sprintf("%s=%v", key, value))
|
||||
}
|
||||
sort.Strings(dataCheckArr)
|
||||
dataCheckString := strings.Join(dataCheckArr, "\n")
|
||||
|
||||
// Calculate secret key
|
||||
secretKey := sha256.Sum256([]byte(idp.ClientSecret))
|
||||
|
||||
// Calculate hash
|
||||
h := hmac.New(sha256.New, secretKey[:])
|
||||
h.Write([]byte(dataCheckString))
|
||||
calculatedHash := hex.EncodeToString(h.Sum(nil))
|
||||
|
||||
// Compare hashes
|
||||
if calculatedHash != hash {
|
||||
return fmt.Errorf("data verification failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (idp *TelegramIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
// Extract auth data from token
|
||||
authDataStr, ok := token.Extra("telegram_auth_data").(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("telegram auth data not found in token")
|
||||
}
|
||||
|
||||
// Parse the auth data
|
||||
var authData map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(authDataStr), &authData); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse auth data: %v", err)
|
||||
}
|
||||
|
||||
// Extract user information from auth data
|
||||
userId, ok := authData["id"].(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid user id in auth data")
|
||||
}
|
||||
|
||||
firstName, _ := authData["first_name"].(string)
|
||||
lastName, _ := authData["last_name"].(string)
|
||||
username, _ := authData["username"].(string)
|
||||
photoUrl, _ := authData["photo_url"].(string)
|
||||
|
||||
// Build display name with fallback
|
||||
displayName := strings.TrimSpace(firstName + " " + lastName)
|
||||
if displayName == "" {
|
||||
displayName = username
|
||||
}
|
||||
if displayName == "" {
|
||||
displayName = strconv.FormatInt(int64(userId), 10)
|
||||
}
|
||||
|
||||
userInfo := UserInfo{
|
||||
Id: strconv.FormatInt(int64(userId), 10),
|
||||
Username: username,
|
||||
DisplayName: displayName,
|
||||
AvatarUrl: photoUrl,
|
||||
}
|
||||
|
||||
return &userInfo, nil
|
||||
}
|
||||
169
idp/wechat_mobile.go
Normal file
169
idp/wechat_mobile.go
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright 2025 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 idp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// WeChatMobileIdProvider is for WeChat OAuth Mobile (in-app browser) login
|
||||
// This uses snsapi_userinfo scope for mobile authorization
|
||||
type WeChatMobileIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
}
|
||||
|
||||
func NewWeChatMobileIdProvider(clientId string, clientSecret string, redirectUrl string) *WeChatMobileIdProvider {
|
||||
idp := &WeChatMobileIdProvider{}
|
||||
|
||||
config := idp.getConfig(clientId, clientSecret, redirectUrl)
|
||||
idp.Config = config
|
||||
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *WeChatMobileIdProvider) SetHttpClient(client *http.Client) {
|
||||
idp.Client = client
|
||||
}
|
||||
|
||||
// getConfig returns OAuth2 config for WeChat Mobile
|
||||
func (idp *WeChatMobileIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
|
||||
endpoint := oauth2.Endpoint{
|
||||
AuthURL: "https://open.weixin.qq.com/connect/oauth2/authorize",
|
||||
TokenURL: "https://api.weixin.qq.com/sns/oauth2/access_token",
|
||||
}
|
||||
|
||||
config := &oauth2.Config{
|
||||
Scopes: []string{"snsapi_userinfo"},
|
||||
Endpoint: endpoint,
|
||||
ClientID: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirectUrl,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// GetToken exchanges authorization code for access token
|
||||
func (idp *WeChatMobileIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
params := url.Values{}
|
||||
params.Add("grant_type", "authorization_code")
|
||||
params.Add("appid", idp.Config.ClientID)
|
||||
params.Add("secret", idp.Config.ClientSecret)
|
||||
params.Add("code", code)
|
||||
|
||||
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?%s", params.Encode())
|
||||
tokenResponse, err := idp.Client.Get(accessTokenUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(tokenResponse.Body)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = buf.ReadFrom(tokenResponse.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check for error response
|
||||
if bytes.Contains(buf.Bytes(), []byte("errcode")) {
|
||||
return nil, fmt.Errorf(buf.String())
|
||||
}
|
||||
|
||||
var wechatAccessToken WechatAccessToken
|
||||
if err = json.Unmarshal(buf.Bytes(), &wechatAccessToken); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token := oauth2.Token{
|
||||
AccessToken: wechatAccessToken.AccessToken,
|
||||
TokenType: "WeChatAccessToken",
|
||||
RefreshToken: wechatAccessToken.RefreshToken,
|
||||
Expiry: time.Time{},
|
||||
}
|
||||
|
||||
raw := make(map[string]string)
|
||||
raw["Openid"] = wechatAccessToken.Openid
|
||||
token.WithExtra(raw)
|
||||
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
// GetUserInfo retrieves user information using the access token
|
||||
func (idp *WeChatMobileIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
var wechatUserInfo WechatUserInfo
|
||||
accessToken := token.AccessToken
|
||||
openid := token.Extra("Openid")
|
||||
|
||||
userInfoUrl := fmt.Sprintf("https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN", accessToken, openid)
|
||||
resp, err := idp.Client.Get(userInfoUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = buf.ReadFrom(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(buf.Bytes(), &wechatUserInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check for error response
|
||||
if wechatUserInfo.Openid == "" {
|
||||
return nil, fmt.Errorf("failed to get user info: %s", buf.String())
|
||||
}
|
||||
|
||||
id := wechatUserInfo.Unionid
|
||||
if id == "" {
|
||||
id = wechatUserInfo.Openid
|
||||
}
|
||||
|
||||
extra := make(map[string]string)
|
||||
extra["wechat_unionid"] = wechatUserInfo.Openid
|
||||
// For WeChat, different appId corresponds to different openId
|
||||
extra[BuildWechatOpenIdKey(idp.Config.ClientID)] = wechatUserInfo.Openid
|
||||
userInfo := UserInfo{
|
||||
Id: id,
|
||||
Username: wechatUserInfo.Nickname,
|
||||
DisplayName: wechatUserInfo.Nickname,
|
||||
AvatarUrl: wechatUserInfo.Headimgurl,
|
||||
Extra: extra,
|
||||
}
|
||||
return &userInfo, nil
|
||||
}
|
||||
@@ -212,6 +212,10 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
||||
e.AddAttribute("homeDirectory", message.AttributeValue("/home/"+user.Name))
|
||||
e.AddAttribute("cn", message.AttributeValue(user.Name))
|
||||
e.AddAttribute("uid", message.AttributeValue(user.Id))
|
||||
e.AddAttribute("mail", message.AttributeValue(user.Email))
|
||||
e.AddAttribute("mobile", message.AttributeValue(user.Phone))
|
||||
e.AddAttribute("sn", message.AttributeValue(user.LastName))
|
||||
e.AddAttribute("givenName", message.AttributeValue(user.FirstName))
|
||||
for _, group := range user.Groups {
|
||||
e.AddAttribute(ldapMemberOfAttr, message.AttributeValue(group))
|
||||
}
|
||||
|
||||
12
main.go
12
main.go
@@ -38,6 +38,18 @@ func main() {
|
||||
object.CreateTables()
|
||||
|
||||
object.InitDb()
|
||||
|
||||
// Handle export command
|
||||
if object.ShouldExportData() {
|
||||
exportPath := object.GetExportFilePath()
|
||||
err := object.DumpToFile(exportPath)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error exporting data to %s: %v", exportPath, err))
|
||||
}
|
||||
fmt.Printf("Data exported successfully to %s\n", exportPath)
|
||||
return
|
||||
}
|
||||
|
||||
object.InitDefaultStorageProvider()
|
||||
object.InitLdapAutoSynchronizer()
|
||||
proxy.InitHttpClient()
|
||||
|
||||
@@ -55,6 +55,8 @@ func GetNotificationProvider(typ string, clientId string, clientSecret string, c
|
||||
return NewViberProvider(clientId, clientSecret, appId, receiver)
|
||||
} else if typ == "CUCloud" {
|
||||
return NewCucloudProvider(clientId, clientSecret, appId, title, regionId, clientId2, metaData)
|
||||
} else if typ == "WeCom" {
|
||||
return NewWeComProvider(clientSecret)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
||||
104
notification/wecom.go
Normal file
104
notification/wecom.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2025 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 notification
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/notify"
|
||||
)
|
||||
|
||||
// wecomService encapsulates the WeCom webhook client
|
||||
type wecomService struct {
|
||||
webhookURL string
|
||||
}
|
||||
|
||||
// wecomResponse represents the response from WeCom webhook API
|
||||
type wecomResponse struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
}
|
||||
|
||||
// NewWeComProvider returns a new instance of a WeCom notification service
|
||||
// WeCom (WeChat Work) uses webhook for group chat notifications
|
||||
// Reference: https://developer.work.weixin.qq.com/document/path/90236
|
||||
func NewWeComProvider(webhookURL string) (notify.Notifier, error) {
|
||||
wecomSrv := &wecomService{
|
||||
webhookURL: webhookURL,
|
||||
}
|
||||
|
||||
notifier := notify.New()
|
||||
notifier.UseServices(wecomSrv)
|
||||
|
||||
return notifier, nil
|
||||
}
|
||||
|
||||
// Send sends a text message to WeCom group chat via webhook
|
||||
func (s *wecomService) Send(ctx context.Context, subject, content string) error {
|
||||
text := subject
|
||||
if content != "" {
|
||||
text = subject + "\n" + content
|
||||
}
|
||||
|
||||
// WeCom webhook message format
|
||||
message := map[string]interface{}{
|
||||
"msgtype": "text",
|
||||
"text": map[string]string{
|
||||
"content": text,
|
||||
},
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(message)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal WeCom message: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", s.webhookURL, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create WeCom request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to send WeCom message: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("WeCom webhook returned HTTP status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Parse WeCom API response
|
||||
var wecomResp wecomResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&wecomResp); err != nil {
|
||||
return fmt.Errorf("failed to decode WeCom response: %w", err)
|
||||
}
|
||||
|
||||
// Check WeCom API error code
|
||||
if wecomResp.Errcode != 0 {
|
||||
return fmt.Errorf("WeCom API error: errcode=%d, errmsg=%s", wecomResp.Errcode, wecomResp.Errmsg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -88,12 +88,18 @@ func getAdapter(owner, name string) (*Adapter, error) {
|
||||
}
|
||||
|
||||
func GetAdapter(id string) (*Adapter, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getAdapter(owner, name)
|
||||
}
|
||||
|
||||
func UpdateAdapter(id string, adapter *Adapter) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if adapter, err := getAdapter(owner, name); adapter == nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ type Application struct {
|
||||
EnableSamlCompress bool `json:"enableSamlCompress"`
|
||||
EnableSamlC14n10 bool `json:"enableSamlC14n10"`
|
||||
EnableSamlPostBinding bool `json:"enableSamlPostBinding"`
|
||||
DisableSamlAttributes bool `json:"disableSamlAttributes"`
|
||||
UseEmailAsSamlNameId bool `json:"useEmailAsSamlNameId"`
|
||||
EnableWebAuthn bool `json:"enableWebAuthn"`
|
||||
EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
|
||||
@@ -117,8 +118,8 @@ type Application struct {
|
||||
TokenSigningMethod string `xorm:"varchar(100)" json:"tokenSigningMethod"`
|
||||
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
|
||||
TokenAttributes []*JwtItem `xorm:"mediumtext" json:"tokenAttributes"`
|
||||
ExpireInHours int `json:"expireInHours"`
|
||||
RefreshExpireInHours int `json:"refreshExpireInHours"`
|
||||
ExpireInHours float64 `json:"expireInHours"`
|
||||
RefreshExpireInHours float64 `json:"refreshExpireInHours"`
|
||||
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
|
||||
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
|
||||
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
|
||||
@@ -449,7 +450,10 @@ func GetApplicationByUser(user *User) (*Application, error) {
|
||||
}
|
||||
|
||||
func GetApplicationByUserId(userId string) (application *Application, err error) {
|
||||
_, name := util.GetOwnerAndNameFromId(userId)
|
||||
_, name, err := util.GetOwnerAndNameFromIdWithError(userId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if IsAppUser(userId) {
|
||||
application, err = getApplication("admin", name)
|
||||
return
|
||||
@@ -557,6 +561,7 @@ func GetMaskedApplication(application *Application, userId string) *Application
|
||||
application.EnableSamlCompress = false
|
||||
application.EnableSamlC14n10 = false
|
||||
application.EnableSamlPostBinding = false
|
||||
application.DisableSamlAttributes = false
|
||||
application.EnableWebAuthn = false
|
||||
application.EnableLinkWithEmail = false
|
||||
application.SamlReplyUrl = "***"
|
||||
@@ -645,15 +650,18 @@ func GetAllowedApplications(applications []*Application, userId string, lang str
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func UpdateApplication(id string, application *Application, isGlobalAdmin bool) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
func UpdateApplication(id string, application *Application, isGlobalAdmin bool, lang string) (bool, error) {
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
oldApplication, err := getApplication(owner, name)
|
||||
if oldApplication == nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !isGlobalAdmin && oldApplication.Organization != application.Organization {
|
||||
return false, fmt.Errorf("auth:Unauthorized operation")
|
||||
return false, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
}
|
||||
|
||||
if name == "app-built-in" {
|
||||
|
||||
@@ -149,7 +149,10 @@ func getCertByName(name string) (*Cert, error) {
|
||||
}
|
||||
|
||||
func GetCert(id string) (*Cert, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, err := getCert(owner, name)
|
||||
if cert == nil && owner != "admin" {
|
||||
return getCert("admin", name)
|
||||
@@ -159,7 +162,10 @@ func GetCert(id string) (*Cert, error) {
|
||||
}
|
||||
|
||||
func UpdateCert(id string, cert *Cert) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if c, err := getCert(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if c == nil {
|
||||
@@ -167,13 +173,13 @@ func UpdateCert(id string, cert *Cert) (bool, error) {
|
||||
}
|
||||
|
||||
if name != cert.Name {
|
||||
err := certChangeTrigger(name, cert.Name)
|
||||
err = certChangeTrigger(name, cert.Name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
err := cert.populateContent()
|
||||
err = cert.populateContent()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -59,8 +59,11 @@ func CheckUserSignup(application *Application, organization *Organization, authF
|
||||
if HasUserByField(organization.Name, "name", authForm.Username) {
|
||||
return i18n.Translate(lang, "check:Username already exists")
|
||||
}
|
||||
if HasUserByField(organization.Name, "email", authForm.Email) {
|
||||
return i18n.Translate(lang, "check:Email already exists")
|
||||
if authForm.Email != "" {
|
||||
normalizedEmail := strings.ToLower(authForm.Email)
|
||||
if HasUserByField(organization.Name, "email", normalizedEmail) {
|
||||
return i18n.Translate(lang, "check:Email already exists")
|
||||
}
|
||||
}
|
||||
if HasUserByField(organization.Name, "phone", authForm.Phone) {
|
||||
return i18n.Translate(lang, "check:Phone already exists")
|
||||
@@ -68,7 +71,7 @@ func CheckUserSignup(application *Application, organization *Organization, authF
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Password") {
|
||||
msg := CheckPasswordComplexityByOrg(organization, authForm.Password)
|
||||
msg := CheckPasswordComplexityByOrg(organization, authForm.Password, lang)
|
||||
if msg != "" {
|
||||
return msg
|
||||
}
|
||||
@@ -80,7 +83,8 @@ func CheckUserSignup(application *Application, organization *Organization, authF
|
||||
return i18n.Translate(lang, "check:Email cannot be empty")
|
||||
}
|
||||
} else {
|
||||
if HasUserByField(organization.Name, "email", authForm.Email) {
|
||||
normalizedEmail := strings.ToLower(authForm.Email)
|
||||
if HasUserByField(organization.Name, "email", normalizedEmail) {
|
||||
return i18n.Translate(lang, "check:Email already exists")
|
||||
} else if !util.IsEmailValid(authForm.Email) {
|
||||
return i18n.Translate(lang, "check:Email is invalid")
|
||||
@@ -278,14 +282,14 @@ func CheckPassword(user *User, password string, lang string, options ...bool) er
|
||||
return resetUserSigninErrorTimes(user)
|
||||
}
|
||||
|
||||
func CheckPasswordComplexityByOrg(organization *Organization, password string) string {
|
||||
errorMsg := checkPasswordComplexity(password, organization.PasswordOptions)
|
||||
func CheckPasswordComplexityByOrg(organization *Organization, password string, lang string) string {
|
||||
errorMsg := checkPasswordComplexity(password, organization.PasswordOptions, lang)
|
||||
return errorMsg
|
||||
}
|
||||
|
||||
func CheckPasswordComplexity(user *User, password string) string {
|
||||
func CheckPasswordComplexity(user *User, password string, lang string) string {
|
||||
organization, _ := GetOrganizationByUser(user)
|
||||
return CheckPasswordComplexityByOrg(organization, password)
|
||||
return CheckPasswordComplexityByOrg(organization, password, lang)
|
||||
}
|
||||
|
||||
func CheckLdapUserPassword(user *User, password string, lang string) error {
|
||||
@@ -363,6 +367,11 @@ func CheckUserPassword(organization string, username string, password string, la
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user is forbidden to sign in, please contact the administrator"))
|
||||
}
|
||||
|
||||
// Prevent direct login for guest users without upgrading
|
||||
if user.Tag == "guest-user" {
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:Guest users must upgrade their account by setting a username and password before they can sign in directly"))
|
||||
}
|
||||
|
||||
if isSigninViaLdap {
|
||||
if user.Ldap == "" {
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
|
||||
@@ -564,7 +573,10 @@ func CheckApiPermission(userId string, organization string, path string, method
|
||||
}
|
||||
|
||||
func CheckLoginPermission(userId string, application *Application) (bool, error) {
|
||||
owner, _ := util.GetOwnerAndNameFromId(userId)
|
||||
owner, _, err := util.GetOwnerAndNameFromIdWithError(userId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if owner == "built-in" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@ import (
|
||||
"regexp"
|
||||
|
||||
"github.com/casdoor/casdoor/cred"
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
)
|
||||
|
||||
type ValidatorFunc func(password string) string
|
||||
type ValidatorFunc func(password string, lang string) string
|
||||
|
||||
var (
|
||||
regexLowerCase = regexp.MustCompile(`[a-z]`)
|
||||
@@ -29,50 +30,50 @@ var (
|
||||
regexSpecial = regexp.MustCompile("[!-/:-@[-`{-~]")
|
||||
)
|
||||
|
||||
func isValidOption_AtLeast6(password string) string {
|
||||
func isValidOption_AtLeast6(password string, lang string) string {
|
||||
if len(password) < 6 {
|
||||
return "The password must have at least 6 characters"
|
||||
return i18n.Translate(lang, "check:The password must have at least 6 characters")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isValidOption_AtLeast8(password string) string {
|
||||
func isValidOption_AtLeast8(password string, lang string) string {
|
||||
if len(password) < 8 {
|
||||
return "The password must have at least 8 characters"
|
||||
return i18n.Translate(lang, "check:The password must have at least 8 characters")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isValidOption_Aa123(password string) string {
|
||||
func isValidOption_Aa123(password string, lang string) string {
|
||||
hasLowerCase := regexLowerCase.MatchString(password)
|
||||
hasUpperCase := regexUpperCase.MatchString(password)
|
||||
hasDigit := regexDigit.MatchString(password)
|
||||
|
||||
if !hasLowerCase || !hasUpperCase || !hasDigit {
|
||||
return "The password must contain at least one uppercase letter, one lowercase letter and one digit"
|
||||
return i18n.Translate(lang, "check:The password must contain at least one uppercase letter, one lowercase letter and one digit")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isValidOption_SpecialChar(password string) string {
|
||||
func isValidOption_SpecialChar(password string, lang string) string {
|
||||
if !regexSpecial.MatchString(password) {
|
||||
return "The password must contain at least one special character"
|
||||
return i18n.Translate(lang, "check:The password must contain at least one special character")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func isValidOption_NoRepeat(password string) string {
|
||||
func isValidOption_NoRepeat(password string, lang string) string {
|
||||
for i := 0; i < len(password)-1; i++ {
|
||||
if password[i] == password[i+1] {
|
||||
return "The password must not contain any repeated characters"
|
||||
return i18n.Translate(lang, "check:The password must not contain any repeated characters")
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func checkPasswordComplexity(password string, options []string) string {
|
||||
func checkPasswordComplexity(password string, options []string, lang string) string {
|
||||
if len(password) == 0 {
|
||||
return "Please input your password!"
|
||||
return i18n.Translate(lang, "check:Password cannot be empty")
|
||||
}
|
||||
|
||||
if len(options) == 0 {
|
||||
@@ -90,7 +91,7 @@ func checkPasswordComplexity(password string, options []string) string {
|
||||
for _, option := range options {
|
||||
checkerFunc, ok := checkers[option]
|
||||
if ok {
|
||||
errorMsg := checkerFunc(password)
|
||||
errorMsg := checkerFunc(password, lang)
|
||||
if errorMsg != "" {
|
||||
return errorMsg
|
||||
}
|
||||
|
||||
@@ -84,12 +84,18 @@ func getEnforcer(owner string, name string) (*Enforcer, error) {
|
||||
}
|
||||
|
||||
func GetEnforcer(id string) (*Enforcer, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getEnforcer(owner, name)
|
||||
}
|
||||
|
||||
func UpdateEnforcer(id string, enforcer *Enforcer) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if oldEnforcer, err := getEnforcer(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if oldEnforcer == nil {
|
||||
@@ -360,7 +366,7 @@ func (enforcer *Enforcer) LoadModelCfg() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
model, err := GetModelEx(enforcer.Model)
|
||||
model, err := getModelEx(enforcer.Model)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if model == nil {
|
||||
|
||||
@@ -96,12 +96,18 @@ func getForm(owner string, name string) (*Form, error) {
|
||||
}
|
||||
|
||||
func GetForm(id string) (*Form, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getForm(owner, name)
|
||||
}
|
||||
|
||||
func UpdateForm(id string, form *Form) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
existingForm, err := getForm(owner, name)
|
||||
if existingForm == nil {
|
||||
return false, fmt.Errorf("the form: %s is not found", id)
|
||||
|
||||
@@ -135,12 +135,18 @@ func getGroup(owner string, name string) (*Group, error) {
|
||||
}
|
||||
|
||||
func GetGroup(id string) (*Group, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getGroup(owner, name)
|
||||
}
|
||||
|
||||
func UpdateGroup(id string, group *Group) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
oldGroup, err := getGroup(owner, name)
|
||||
if oldGroup == nil {
|
||||
return false, err
|
||||
@@ -299,7 +305,10 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
|
||||
}
|
||||
|
||||
func GetGroupUserCount(groupId string, field, value string) (int64, error) {
|
||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||
owner, _, err := util.GetOwnerAndNameFromIdWithError(groupId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -318,7 +327,10 @@ func GetGroupUserCount(groupId string, field, value string) (int64, error) {
|
||||
|
||||
func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||
users := []*User{}
|
||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||
owner, _, err := util.GetOwnerAndNameFromIdWithError(groupId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -417,7 +417,7 @@ func initBuiltInPermission() {
|
||||
Groups: []string{},
|
||||
Roles: []string{},
|
||||
Domains: []string{},
|
||||
Model: "user-model-built-in",
|
||||
Model: "built-in/user-model-built-in",
|
||||
Adapter: "",
|
||||
ResourceType: "Application",
|
||||
Resources: []string{"app-built-in"},
|
||||
|
||||
@@ -863,7 +863,7 @@ func initDefinedTransaction(transaction *Transaction) {
|
||||
if initDataNewOnly {
|
||||
return
|
||||
}
|
||||
affected, err := DeleteTransaction(transaction)
|
||||
affected, err := DeleteTransaction(transaction, "en")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -872,7 +872,7 @@ func initDefinedTransaction(transaction *Transaction) {
|
||||
}
|
||||
}
|
||||
transaction.CreatedTime = util.GetCurrentTime()
|
||||
_, err = AddTransaction(transaction)
|
||||
_, _, err = AddTransaction(transaction, "en", false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package object
|
||||
|
||||
|
||||
@@ -90,7 +90,10 @@ func getInvitation(owner string, name string) (*Invitation, error) {
|
||||
}
|
||||
|
||||
func GetInvitation(id string) (*Invitation, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getInvitation(owner, name)
|
||||
}
|
||||
|
||||
@@ -133,7 +136,10 @@ func GetMaskedInvitation(invitation *Invitation) *Invitation {
|
||||
}
|
||||
|
||||
func UpdateInvitation(id string, invitation *Invitation, lang string) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if p, err := getInvitation(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if p == nil {
|
||||
@@ -146,7 +152,7 @@ func UpdateInvitation(id string, invitation *Invitation, lang string) (bool, err
|
||||
invitation.IsRegexp = isRegexp
|
||||
}
|
||||
|
||||
err := CheckInvitationDefaultCode(invitation.Code, invitation.DefaultCode, lang)
|
||||
err = CheckInvitationDefaultCode(invitation.Code, invitation.DefaultCode, lang)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -39,9 +39,11 @@ type MfaInterface interface {
|
||||
}
|
||||
|
||||
const (
|
||||
EmailType = "email"
|
||||
SmsType = "sms"
|
||||
TotpType = "app"
|
||||
EmailType = "email"
|
||||
SmsType = "sms"
|
||||
TotpType = "app"
|
||||
RadiusType = "radius"
|
||||
PushType = "push"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -58,6 +60,10 @@ func GetMfaUtil(mfaType string, config *MfaProps) MfaInterface {
|
||||
return NewEmailMfaUtil(config)
|
||||
case TotpType:
|
||||
return NewTotpMfaUtil(config)
|
||||
case RadiusType:
|
||||
return NewRadiusMfaUtil(config)
|
||||
case PushType:
|
||||
return NewPushMfaUtil(config)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -92,7 +98,7 @@ func MfaRecover(user *User, recoveryCode string) error {
|
||||
func GetAllMfaProps(user *User, masked bool) []*MfaProps {
|
||||
mfaProps := []*MfaProps{}
|
||||
|
||||
for _, mfaType := range []string{SmsType, EmailType, TotpType} {
|
||||
for _, mfaType := range []string{SmsType, EmailType, TotpType, RadiusType, PushType} {
|
||||
mfaProps = append(mfaProps, user.GetMfaProps(mfaType, masked))
|
||||
}
|
||||
return mfaProps
|
||||
@@ -153,6 +159,42 @@ func (user *User) GetMfaProps(mfaType string, masked bool) *MfaProps {
|
||||
} else {
|
||||
mfaProps.Secret = user.TotpSecret
|
||||
}
|
||||
} else if mfaType == RadiusType {
|
||||
if !user.MfaRadiusEnabled {
|
||||
return &MfaProps{
|
||||
Enabled: false,
|
||||
MfaType: mfaType,
|
||||
}
|
||||
}
|
||||
|
||||
mfaProps = &MfaProps{
|
||||
Enabled: user.MfaRadiusEnabled,
|
||||
MfaType: mfaType,
|
||||
}
|
||||
if masked {
|
||||
mfaProps.Secret = util.GetMaskedEmail(user.MfaRadiusUsername)
|
||||
} else {
|
||||
mfaProps.Secret = user.MfaRadiusUsername
|
||||
}
|
||||
mfaProps.URL = user.MfaRadiusProvider
|
||||
} else if mfaType == PushType {
|
||||
if !user.MfaPushEnabled {
|
||||
return &MfaProps{
|
||||
Enabled: false,
|
||||
MfaType: mfaType,
|
||||
}
|
||||
}
|
||||
|
||||
mfaProps = &MfaProps{
|
||||
Enabled: user.MfaPushEnabled,
|
||||
MfaType: mfaType,
|
||||
}
|
||||
if masked {
|
||||
mfaProps.Secret = util.GetMaskedEmail(user.MfaPushReceiver)
|
||||
} else {
|
||||
mfaProps.Secret = user.MfaPushReceiver
|
||||
}
|
||||
mfaProps.URL = user.MfaPushProvider
|
||||
}
|
||||
|
||||
if user.PreferredMfaType == mfaType {
|
||||
@@ -167,8 +209,14 @@ func DisabledMultiFactorAuth(user *User) error {
|
||||
user.MfaPhoneEnabled = false
|
||||
user.MfaEmailEnabled = false
|
||||
user.TotpSecret = ""
|
||||
user.MfaRadiusEnabled = false
|
||||
user.MfaRadiusUsername = ""
|
||||
user.MfaRadiusProvider = ""
|
||||
user.MfaPushEnabled = false
|
||||
user.MfaPushReceiver = ""
|
||||
user.MfaPushProvider = ""
|
||||
|
||||
_, err := updateUser(user.GetId(), user, []string{"preferred_mfa_type", "recovery_codes", "mfa_phone_enabled", "mfa_email_enabled", "totp_secret"})
|
||||
_, err := updateUser(user.GetId(), user, []string{"preferred_mfa_type", "recovery_codes", "mfa_phone_enabled", "mfa_email_enabled", "totp_secret", "mfa_radius_enabled", "mfa_radius_username", "mfa_radius_provider", "mfa_push_enabled", "mfa_push_receiver", "mfa_push_provider"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
170
object/mfa_push.go
Normal file
170
object/mfa_push.go
Normal file
@@ -0,0 +1,170 @@
|
||||
// Copyright 2025 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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/notification"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type PushMfa struct {
|
||||
*MfaProps
|
||||
provider *Provider
|
||||
challengeId string
|
||||
challengeExp time.Time
|
||||
}
|
||||
|
||||
func (mfa *PushMfa) Initiate(userId string) (*MfaProps, error) {
|
||||
mfaProps := MfaProps{
|
||||
MfaType: mfa.MfaType,
|
||||
}
|
||||
return &mfaProps, nil
|
||||
}
|
||||
|
||||
func (mfa *PushMfa) SetupVerify(passCode string) error {
|
||||
if mfa.Secret == "" {
|
||||
return errors.New("push notification receiver is required")
|
||||
}
|
||||
|
||||
if mfa.provider == nil {
|
||||
return errors.New("push notification provider is not configured")
|
||||
}
|
||||
|
||||
// For setup verification, send a test notification
|
||||
// Note: Full implementation would require a callback endpoint to receive approval/denial
|
||||
// from the mobile app, and passCode would contain the callback verification token
|
||||
return mfa.sendPushNotification("MFA Setup Verification", "Please approve this setup request on your device")
|
||||
}
|
||||
|
||||
func (mfa *PushMfa) Enable(user *User) error {
|
||||
columns := []string{"recovery_codes", "preferred_mfa_type", "mfa_push_enabled", "mfa_push_receiver", "mfa_push_provider"}
|
||||
|
||||
user.RecoveryCodes = append(user.RecoveryCodes, mfa.RecoveryCodes...)
|
||||
if user.PreferredMfaType == "" {
|
||||
user.PreferredMfaType = mfa.MfaType
|
||||
}
|
||||
|
||||
user.MfaPushEnabled = true
|
||||
user.MfaPushReceiver = mfa.Secret
|
||||
user.MfaPushProvider = mfa.URL
|
||||
|
||||
_, err := UpdateUser(user.GetId(), user, columns, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *PushMfa) Verify(passCode string) error {
|
||||
if mfa.Secret == "" {
|
||||
return errors.New("push notification receiver is required")
|
||||
}
|
||||
|
||||
if mfa.provider == nil {
|
||||
return errors.New("push notification provider is not configured")
|
||||
}
|
||||
|
||||
// Send the push notification for authentication
|
||||
// Note: Full implementation would require:
|
||||
// 1. A callback endpoint to receive approval/denial from the mobile app
|
||||
// 2. Persistent storage of challengeId to validate the callback
|
||||
// 3. passCode would contain the callback verification token
|
||||
// For now, this sends the notification and returns success to enable basic functionality
|
||||
return mfa.sendPushNotification("MFA Verification", "Authentication request. Please approve or deny.")
|
||||
}
|
||||
|
||||
func (mfa *PushMfa) sendPushNotification(title string, message string) error {
|
||||
if mfa.provider == nil {
|
||||
// Try to load provider if URL is set and we have database access
|
||||
if mfa.URL != "" && ormer != nil && ormer.Engine != nil {
|
||||
provider, err := GetProvider(mfa.URL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load push notification provider: %v", err)
|
||||
}
|
||||
if provider == nil {
|
||||
return errors.New("push notification provider not found")
|
||||
}
|
||||
mfa.provider = provider
|
||||
} else {
|
||||
return errors.New("push notification provider is not configured")
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a unique challenge ID for this notification
|
||||
// Note: In a full implementation, this would be stored in a cache/database
|
||||
// to validate callbacks from the mobile app
|
||||
mfa.challengeId = uuid.NewString()
|
||||
mfa.challengeExp = time.Now().Add(5 * time.Minute) // Challenge expires in 5 minutes
|
||||
|
||||
// Get the notification provider
|
||||
notifier, err := notification.GetNotificationProvider(
|
||||
mfa.provider.Type,
|
||||
mfa.provider.ClientId,
|
||||
mfa.provider.ClientSecret,
|
||||
mfa.provider.ClientId2,
|
||||
mfa.provider.ClientSecret2,
|
||||
mfa.provider.AppId,
|
||||
mfa.Secret, // receiver
|
||||
mfa.provider.Method,
|
||||
title,
|
||||
mfa.provider.Metadata,
|
||||
mfa.provider.RegionId,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create notification provider: %v", err)
|
||||
}
|
||||
|
||||
if notifier == nil {
|
||||
return errors.New("notification provider is not supported")
|
||||
}
|
||||
|
||||
// Send the push notification
|
||||
// Note: The challengeId is kept server-side and not exposed in the message
|
||||
ctx := context.Background()
|
||||
err = notifier.Send(ctx, title, message)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to send push notification: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewPushMfaUtil(config *MfaProps) *PushMfa {
|
||||
if config == nil {
|
||||
config = &MfaProps{
|
||||
MfaType: PushType,
|
||||
}
|
||||
}
|
||||
|
||||
pushMfa := &PushMfa{
|
||||
MfaProps: config,
|
||||
}
|
||||
|
||||
// Load provider if URL is specified and ormer is initialized
|
||||
if config.URL != "" && ormer != nil && ormer.Engine != nil {
|
||||
provider, err := GetProvider(config.URL)
|
||||
if err == nil && provider != nil {
|
||||
pushMfa.provider = provider
|
||||
}
|
||||
}
|
||||
|
||||
return pushMfa
|
||||
}
|
||||
146
object/mfa_radius.go
Normal file
146
object/mfa_radius.go
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright 2025 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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"layeh.com/radius"
|
||||
"layeh.com/radius/rfc2865"
|
||||
)
|
||||
|
||||
type RadiusMfa struct {
|
||||
*MfaProps
|
||||
provider *Provider
|
||||
}
|
||||
|
||||
func (mfa *RadiusMfa) Initiate(userId string) (*MfaProps, error) {
|
||||
mfaProps := MfaProps{
|
||||
MfaType: mfa.MfaType,
|
||||
}
|
||||
return &mfaProps, nil
|
||||
}
|
||||
|
||||
func (mfa *RadiusMfa) SetupVerify(passCode string) error {
|
||||
if mfa.Secret == "" {
|
||||
return errors.New("RADIUS username is required")
|
||||
}
|
||||
|
||||
if mfa.provider == nil {
|
||||
return errors.New("RADIUS provider is not configured")
|
||||
}
|
||||
|
||||
return mfa.authenticateWithRadius(mfa.Secret, passCode)
|
||||
}
|
||||
|
||||
func (mfa *RadiusMfa) Enable(user *User) error {
|
||||
columns := []string{"recovery_codes", "preferred_mfa_type", "mfa_radius_enabled", "mfa_radius_username", "mfa_radius_provider"}
|
||||
|
||||
user.RecoveryCodes = append(user.RecoveryCodes, mfa.RecoveryCodes...)
|
||||
if user.PreferredMfaType == "" {
|
||||
user.PreferredMfaType = mfa.MfaType
|
||||
}
|
||||
|
||||
user.MfaRadiusEnabled = true
|
||||
user.MfaRadiusUsername = mfa.Secret
|
||||
user.MfaRadiusProvider = mfa.URL
|
||||
|
||||
_, err := UpdateUser(user.GetId(), user, columns, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *RadiusMfa) Verify(passCode string) error {
|
||||
if mfa.Secret == "" {
|
||||
return errors.New("RADIUS username is required")
|
||||
}
|
||||
|
||||
if mfa.provider == nil {
|
||||
return errors.New("RADIUS provider is not configured")
|
||||
}
|
||||
|
||||
return mfa.authenticateWithRadius(mfa.Secret, passCode)
|
||||
}
|
||||
|
||||
func (mfa *RadiusMfa) authenticateWithRadius(username, password string) error {
|
||||
if mfa.provider == nil {
|
||||
// Try to load provider if URL is set and we have database access
|
||||
if mfa.URL != "" && ormer != nil && ormer.Engine != nil {
|
||||
provider, err := GetProvider(mfa.URL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load RADIUS provider: %v", err)
|
||||
}
|
||||
if provider == nil {
|
||||
return errors.New("RADIUS provider not found")
|
||||
}
|
||||
mfa.provider = provider
|
||||
} else {
|
||||
return errors.New("RADIUS provider is not configured")
|
||||
}
|
||||
}
|
||||
|
||||
// Create RADIUS packet
|
||||
packet := radius.New(radius.CodeAccessRequest, []byte(mfa.provider.ClientSecret))
|
||||
if err := rfc2865.UserName_SetString(packet, username); err != nil {
|
||||
return fmt.Errorf("failed to set RADIUS username: %v", err)
|
||||
}
|
||||
if err := rfc2865.UserPassword_SetString(packet, password); err != nil {
|
||||
return fmt.Errorf("failed to set RADIUS password: %v", err)
|
||||
}
|
||||
|
||||
// Send request to RADIUS server
|
||||
address := fmt.Sprintf("%s:%d", mfa.provider.Host, mfa.provider.Port)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
response, err := radius.Exchange(ctx, packet, address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("RADIUS authentication failed: %v", err)
|
||||
}
|
||||
|
||||
if response.Code == radius.CodeAccessAccept {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("RADIUS authentication rejected")
|
||||
}
|
||||
|
||||
func NewRadiusMfaUtil(config *MfaProps) *RadiusMfa {
|
||||
if config == nil {
|
||||
config = &MfaProps{
|
||||
MfaType: RadiusType,
|
||||
}
|
||||
}
|
||||
|
||||
radiusMfa := &RadiusMfa{
|
||||
MfaProps: config,
|
||||
}
|
||||
|
||||
// Load provider if URL is specified and ormer is initialized
|
||||
if config.URL != "" && ormer != nil && ormer.Engine != nil {
|
||||
provider, err := GetProvider(config.URL)
|
||||
if err == nil && provider != nil {
|
||||
radiusMfa.provider = provider
|
||||
}
|
||||
}
|
||||
|
||||
return radiusMfa
|
||||
}
|
||||
78
object/mfa_radius_test.go
Normal file
78
object/mfa_radius_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2025 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 (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRadiusMfaUtil(t *testing.T) {
|
||||
// Test creating a new RADIUS MFA util without provider lookup
|
||||
config := &MfaProps{
|
||||
MfaType: RadiusType,
|
||||
Secret: "testuser",
|
||||
}
|
||||
|
||||
radiusMfa := NewRadiusMfaUtil(config)
|
||||
if radiusMfa == nil {
|
||||
t.Error("NewRadiusMfaUtil returned nil")
|
||||
}
|
||||
|
||||
if radiusMfa.MfaType != RadiusType {
|
||||
t.Errorf("Expected MFA type %s, got %s", RadiusType, radiusMfa.MfaType)
|
||||
}
|
||||
|
||||
// Test Initiate
|
||||
mfaProps, err := radiusMfa.Initiate("test/user")
|
||||
if err != nil {
|
||||
t.Errorf("Initiate failed: %v", err)
|
||||
}
|
||||
|
||||
if mfaProps == nil {
|
||||
t.Error("Initiate returned nil mfaProps")
|
||||
}
|
||||
|
||||
if mfaProps.MfaType != RadiusType {
|
||||
t.Errorf("Expected MFA type %s, got %s", RadiusType, mfaProps.MfaType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMfaUtil_Radius(t *testing.T) {
|
||||
config := &MfaProps{
|
||||
MfaType: RadiusType,
|
||||
Secret: "testuser",
|
||||
}
|
||||
|
||||
mfaUtil := GetMfaUtil(RadiusType, config)
|
||||
if mfaUtil == nil {
|
||||
t.Error("GetMfaUtil returned nil for RADIUS type")
|
||||
}
|
||||
|
||||
radiusMfa, ok := mfaUtil.(*RadiusMfa)
|
||||
if !ok {
|
||||
t.Error("GetMfaUtil did not return RadiusMfa type")
|
||||
}
|
||||
|
||||
if radiusMfa.MfaType != RadiusType {
|
||||
t.Errorf("Expected MFA type %s, got %s", RadiusType, radiusMfa.MfaType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRadiusMfaType(t *testing.T) {
|
||||
// Test that RadiusType constant is defined correctly
|
||||
if RadiusType != "radius" {
|
||||
t.Errorf("Expected RadiusType to be 'radius', got '%s'", RadiusType)
|
||||
}
|
||||
}
|
||||
@@ -80,12 +80,19 @@ func getModel(owner string, name string) (*Model, error) {
|
||||
}
|
||||
|
||||
func GetModel(id string) (*Model, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getModel(owner, name)
|
||||
}
|
||||
|
||||
func GetModelEx(id string) (*Model, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
func getModelEx(id string) (*Model, error) {
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
model, err := getModel(owner, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -112,7 +119,10 @@ func UpdateModelWithCheck(id string, modelObj *Model) error {
|
||||
}
|
||||
|
||||
func UpdateModel(id string, modelObj *Model) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if m, err := getModel(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if m == nil {
|
||||
|
||||
@@ -16,6 +16,8 @@ package object
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/notification"
|
||||
"github.com/casdoor/notify"
|
||||
@@ -40,3 +42,63 @@ func SendNotification(provider *Provider, content string) error {
|
||||
err = client.Send(context.Background(), "", content)
|
||||
return err
|
||||
}
|
||||
|
||||
// SendSsoLogoutNotifications sends logout notifications to all notification providers
|
||||
// configured in the user's signup application
|
||||
func SendSsoLogoutNotifications(user *User) error {
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If user's signup application is empty, don't send notifications
|
||||
if user.SignupApplication == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the user's signup application
|
||||
application, err := GetApplication(user.SignupApplication)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get signup application: %w", err)
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
return fmt.Errorf("signup application not found: %s", user.SignupApplication)
|
||||
}
|
||||
|
||||
// Prepare sanitized user data for notification
|
||||
// Only include safe, non-sensitive fields
|
||||
sanitizedData := map[string]interface{}{
|
||||
"owner": user.Owner,
|
||||
"name": user.Name,
|
||||
"displayName": user.DisplayName,
|
||||
"email": user.Email,
|
||||
"phone": user.Phone,
|
||||
"id": user.Id,
|
||||
"event": "sso-logout",
|
||||
}
|
||||
userData, err := json.Marshal(sanitizedData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal user data: %w", err)
|
||||
}
|
||||
content := string(userData)
|
||||
|
||||
// Send notifications to all notification providers in the signup application
|
||||
for _, providerItem := range application.Providers {
|
||||
if providerItem.Provider == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Only send to notification providers
|
||||
if providerItem.Provider.Category != "Notification" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Send the notification using the provider from the providerItem
|
||||
err = SendNotification(providerItem.Provider, content)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to send SSO logout notification to provider %s/%s: %w", providerItem.Provider.Owner, providerItem.Provider.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
156
object/order.go
Normal file
156
object/order.go
Normal file
@@ -0,0 +1,156 @@
|
||||
// Copyright 2025 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 Order 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"`
|
||||
|
||||
// Product Info
|
||||
ProductName string `xorm:"varchar(100)" json:"productName"`
|
||||
Products []string `xorm:"varchar(1000)" json:"products"` // Future support for multiple products per order. Using varchar(1000) for simple JSON array storage; can be refactored to separate table if needed
|
||||
|
||||
// Subscription Info (for subscription orders)
|
||||
PricingName string `xorm:"varchar(100)" json:"pricingName"`
|
||||
PlanName string `xorm:"varchar(100)" json:"planName"`
|
||||
|
||||
// User Info
|
||||
User string `xorm:"varchar(100)" json:"user"`
|
||||
|
||||
// Payment Info
|
||||
Payment string `xorm:"varchar(100)" json:"payment"`
|
||||
Price float64 `json:"price"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
|
||||
// Order State
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
Message string `xorm:"varchar(2000)" json:"message"`
|
||||
|
||||
// Order Duration
|
||||
StartTime string `xorm:"varchar(100)" json:"startTime"`
|
||||
EndTime string `xorm:"varchar(100)" json:"endTime"`
|
||||
}
|
||||
|
||||
func GetOrderCount(owner, field, value string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
return session.Count(&Order{Owner: owner})
|
||||
}
|
||||
|
||||
func GetOrders(owner string) ([]*Order, error) {
|
||||
orders := []*Order{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&orders, &Order{Owner: owner})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return orders, nil
|
||||
}
|
||||
|
||||
func GetUserOrders(owner, user string) ([]*Order, error) {
|
||||
orders := []*Order{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&orders, &Order{Owner: owner, User: user})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return orders, nil
|
||||
}
|
||||
|
||||
func GetPaginationOrders(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Order, error) {
|
||||
orders := []*Order{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&orders, &Order{Owner: owner})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return orders, nil
|
||||
}
|
||||
|
||||
func getOrder(owner string, name string) (*Order, error) {
|
||||
if owner == "" || name == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
order := Order{Owner: owner, Name: name}
|
||||
existed, err := ormer.Engine.Get(&order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &order, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetOrder(id string) (*Order, error) {
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getOrder(owner, name)
|
||||
}
|
||||
|
||||
func UpdateOrder(id string, order *Order) (bool, error) {
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if o, err := getOrder(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if o == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(order)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func AddOrder(order *Order) (bool, error) {
|
||||
affected, err := ormer.Engine.Insert(order)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func DeleteOrder(order *Order) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{order.Owner, order.Name}).Delete(&Order{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func (order *Order) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", order.Owner, order.Name)
|
||||
}
|
||||
332
object/order_pay.go
Normal file
332
object/order_pay.go
Normal file
@@ -0,0 +1,332 @@
|
||||
// Copyright 2025 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/idp"
|
||||
"github.com/casdoor/casdoor/pp"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
func PlaceOrder(productId string, user *User, pricingName string, planName string, customPrice float64) (*Order, error) {
|
||||
product, err := GetProduct(productId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if product == nil {
|
||||
return nil, fmt.Errorf("the product: %s does not exist", productId)
|
||||
}
|
||||
|
||||
if !product.IsRecharge && product.Quantity <= 0 {
|
||||
return nil, fmt.Errorf("the product: %s is out of stock", product.Name)
|
||||
}
|
||||
|
||||
userBalanceCurrency := user.BalanceCurrency
|
||||
if userBalanceCurrency == "" {
|
||||
org, err := getOrganization("admin", user.Owner)
|
||||
if err == nil && org != nil && org.BalanceCurrency != "" {
|
||||
userBalanceCurrency = org.BalanceCurrency
|
||||
} else {
|
||||
userBalanceCurrency = "USD"
|
||||
}
|
||||
}
|
||||
|
||||
productCurrency := product.Currency
|
||||
if productCurrency == "" {
|
||||
productCurrency = "USD"
|
||||
}
|
||||
|
||||
var productPrice float64
|
||||
if product.IsRecharge {
|
||||
if customPrice <= 0 {
|
||||
return nil, fmt.Errorf("the custom price should be greater than zero")
|
||||
}
|
||||
productPrice = customPrice
|
||||
} else {
|
||||
productPrice = product.Price
|
||||
}
|
||||
price := ConvertCurrency(productPrice, productCurrency, userBalanceCurrency)
|
||||
|
||||
orderName := fmt.Sprintf("order_%v", util.GenerateTimeId())
|
||||
order := &Order{
|
||||
Owner: product.Owner,
|
||||
Name: orderName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: fmt.Sprintf("Order for %s", product.DisplayName),
|
||||
ProductName: product.Name,
|
||||
Products: []string{product.Name},
|
||||
PricingName: pricingName,
|
||||
PlanName: planName,
|
||||
User: user.Name,
|
||||
Payment: "", // Payment will be set when user pays
|
||||
Price: price,
|
||||
Currency: userBalanceCurrency,
|
||||
State: "Created",
|
||||
Message: "",
|
||||
StartTime: util.GetCurrentTime(),
|
||||
EndTime: "",
|
||||
}
|
||||
|
||||
affected, err := AddOrder(order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !affected {
|
||||
return nil, fmt.Errorf("failed to add order: %s", util.StructToJson(order))
|
||||
}
|
||||
|
||||
return order, nil
|
||||
}
|
||||
|
||||
func PayOrder(providerName, host, paymentEnv string, order *Order) (payment *Payment, attachInfo map[string]interface{}, err error) {
|
||||
if order.State != "Created" {
|
||||
return nil, nil, fmt.Errorf("cannot pay for order: %s, current state is %s", order.GetId(), order.State)
|
||||
}
|
||||
|
||||
productId := util.GetId(order.Owner, order.ProductName)
|
||||
product, err := GetProduct(productId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if product == nil {
|
||||
return nil, nil, fmt.Errorf("the product: %s does not exist", productId)
|
||||
}
|
||||
|
||||
if !product.IsRecharge && product.Quantity <= 0 {
|
||||
return nil, nil, fmt.Errorf("the product: %s is out of stock", product.Name)
|
||||
}
|
||||
|
||||
user, err := GetUser(util.GetId(order.Owner, order.User))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if user == nil {
|
||||
return nil, nil, fmt.Errorf("the user: %s does not exist", order.User)
|
||||
}
|
||||
|
||||
provider, err := product.getProvider(providerName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pProvider, err := GetPaymentProvider(provider)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
owner := product.Owner
|
||||
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
|
||||
paymentName := fmt.Sprintf("payment_%v", util.GenerateTimeId())
|
||||
|
||||
originFrontend, originBackend := getOriginFromHost(host)
|
||||
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
|
||||
notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s", originBackend, owner, paymentName)
|
||||
|
||||
// Create a subscription when pricing and plan are provided
|
||||
// This allows both free users and paid users to subscribe to plans
|
||||
if order.PricingName != "" && order.PlanName != "" {
|
||||
plan, err := GetPlan(util.GetId(owner, order.PlanName))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if plan == nil {
|
||||
return nil, nil, fmt.Errorf("the plan: %s does not exist", order.PlanName)
|
||||
}
|
||||
|
||||
sub, err := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
affected, err := AddSubscription(sub)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !affected {
|
||||
return nil, nil, fmt.Errorf("failed to add subscription: %s", sub.Name)
|
||||
}
|
||||
|
||||
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, order.PricingName, sub.Name)
|
||||
}
|
||||
|
||||
if product.SuccessUrl != "" {
|
||||
returnUrl = fmt.Sprintf("%s?transactionOwner=%s&transactionName=%s", product.SuccessUrl, owner, paymentName)
|
||||
}
|
||||
|
||||
payReq := &pp.PayReq{
|
||||
ProviderName: providerName,
|
||||
ProductName: product.Name,
|
||||
PayerName: payerName,
|
||||
PayerId: user.Id,
|
||||
PayerEmail: user.Email,
|
||||
PaymentName: paymentName,
|
||||
ProductDisplayName: product.DisplayName,
|
||||
ProductDescription: product.Description,
|
||||
ProductImage: product.Image,
|
||||
Price: order.Price,
|
||||
Currency: order.Currency,
|
||||
ReturnUrl: returnUrl,
|
||||
NotifyUrl: notifyUrl,
|
||||
PaymentEnv: paymentEnv,
|
||||
}
|
||||
|
||||
if provider.Type == "WeChat Pay" {
|
||||
payReq.PayerId, err = getUserExtraProperty(user, "WeChat", idp.BuildWechatOpenIdKey(provider.ClientId2))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else if provider.Type == "Balance" {
|
||||
payReq.PayerId = user.GetId()
|
||||
}
|
||||
|
||||
payResp, err := pProvider.Pay(payReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
payment = &Payment{
|
||||
Owner: product.Owner,
|
||||
Name: paymentName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: paymentName,
|
||||
|
||||
Provider: provider.Name,
|
||||
Type: provider.Type,
|
||||
|
||||
ProductName: product.Name,
|
||||
ProductDisplayName: product.DisplayName,
|
||||
Detail: product.Detail,
|
||||
Tag: product.Tag,
|
||||
Currency: order.Currency,
|
||||
Price: order.Price,
|
||||
IsRecharge: product.IsRecharge,
|
||||
|
||||
User: user.Name,
|
||||
Order: order.Name,
|
||||
PayUrl: payResp.PayUrl,
|
||||
SuccessUrl: returnUrl,
|
||||
State: pp.PaymentStateCreated,
|
||||
OutOrderId: payResp.OrderId,
|
||||
}
|
||||
|
||||
transaction := &Transaction{
|
||||
Owner: payment.Owner,
|
||||
Name: payment.Name,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: payment.DisplayName,
|
||||
Application: owner,
|
||||
Domain: "",
|
||||
Amount: payment.Price,
|
||||
Currency: order.Currency,
|
||||
Payment: payment.Name,
|
||||
State: pp.PaymentStateCreated,
|
||||
}
|
||||
|
||||
if product.IsRecharge {
|
||||
transaction.Category = "Recharge"
|
||||
transaction.Type = ""
|
||||
transaction.Subtype = ""
|
||||
transaction.Provider = ""
|
||||
transaction.Tag = "User"
|
||||
transaction.User = payment.User
|
||||
transaction.State = pp.PaymentStatePaid
|
||||
} else {
|
||||
transaction.Category = ""
|
||||
transaction.Type = provider.Category
|
||||
transaction.Subtype = provider.Type
|
||||
transaction.Provider = provider.Name
|
||||
transaction.Tag = product.Tag
|
||||
transaction.User = payment.User
|
||||
}
|
||||
|
||||
if provider.Type == "Dummy" {
|
||||
payment.State = pp.PaymentStatePaid
|
||||
currency := payment.Currency
|
||||
if currency == "" {
|
||||
currency = "USD"
|
||||
}
|
||||
err = UpdateUserBalance(user.Owner, user.Name, payment.Price, currency, "en")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else if provider.Type == "Balance" {
|
||||
convertedPrice := ConvertCurrency(order.Price, order.Currency, user.BalanceCurrency)
|
||||
if convertedPrice > user.Balance {
|
||||
return nil, nil, fmt.Errorf("insufficient user balance")
|
||||
}
|
||||
transaction.Amount = -transaction.Amount
|
||||
err = UpdateUserBalance(user.Owner, user.Name, -convertedPrice, user.BalanceCurrency, "en")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
payment.State = pp.PaymentStatePaid
|
||||
transaction.State = pp.PaymentStatePaid
|
||||
}
|
||||
|
||||
affected, err := AddPayment(payment)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !affected {
|
||||
return nil, nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
||||
}
|
||||
|
||||
if product.IsRecharge || provider.Type == "Balance" {
|
||||
affected, _, err = AddTransaction(transaction, "en", false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !affected {
|
||||
return nil, nil, fmt.Errorf("failed to add transaction: %s", util.StructToJson(transaction))
|
||||
}
|
||||
}
|
||||
|
||||
order.Payment = payment.Name
|
||||
if provider.Type == "Dummy" || provider.Type == "Balance" {
|
||||
order.State = "Paid"
|
||||
order.Message = "Payment successful"
|
||||
order.EndTime = util.GetCurrentTime()
|
||||
}
|
||||
|
||||
// Update order state first to avoid inconsistency
|
||||
_, err = UpdateOrder(order.GetId(), order)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Update product stock after order state is persisted (for instant payment methods)
|
||||
if provider.Type == "Dummy" || provider.Type == "Balance" {
|
||||
err = UpdateProductStock(product)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return payment, payResp.AttachInfo, nil
|
||||
}
|
||||
|
||||
func CancelOrder(order *Order) (bool, error) {
|
||||
if order.State != "Created" {
|
||||
return false, fmt.Errorf("cannot cancel order in state: %s", order.State)
|
||||
}
|
||||
|
||||
order.State = "Canceled"
|
||||
order.Message = "Canceled by user"
|
||||
order.EndTime = util.GetCurrentTime()
|
||||
return UpdateOrder(order.GetId(), order)
|
||||
}
|
||||
@@ -83,11 +83,17 @@ type Organization struct {
|
||||
DisableSignin bool `json:"disableSignin"`
|
||||
IpRestriction string `json:"ipRestriction"`
|
||||
NavItems []string `xorm:"mediumtext" json:"navItems"`
|
||||
UserNavItems []string `xorm:"mediumtext" json:"userNavItems"`
|
||||
WidgetItems []string `xorm:"mediumtext" json:"widgetItems"`
|
||||
|
||||
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
|
||||
MfaRememberInHours int `json:"mfaRememberInHours"`
|
||||
AccountItems []*AccountItem `xorm:"mediumtext" json:"accountItems"`
|
||||
|
||||
OrgBalance float64 `json:"orgBalance"`
|
||||
UserBalance float64 `json:"userBalance"`
|
||||
BalanceCredit float64 `json:"balanceCredit"`
|
||||
BalanceCurrency string `xorm:"varchar(100)" json:"balanceCurrency"`
|
||||
}
|
||||
|
||||
func GetOrganizationCount(owner, name, field, value string) (int64, error) {
|
||||
@@ -202,7 +208,10 @@ func GetMaskedOrganizations(organizations []*Organization, errs ...error) ([]*Or
|
||||
}
|
||||
|
||||
func UpdateOrganization(id string, organization *Organization, isGlobalAdmin bool) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
org, err := getOrganization(owner, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -231,6 +240,7 @@ func UpdateOrganization(id string, organization *Organization, isGlobalAdmin boo
|
||||
|
||||
if !isGlobalAdmin {
|
||||
organization.NavItems = org.NavItems
|
||||
organization.UserNavItems = org.UserNavItems
|
||||
organization.WidgetItems = org.WidgetItems
|
||||
}
|
||||
|
||||
@@ -422,14 +432,20 @@ func organizationChangeTrigger(oldName string, newName string) error {
|
||||
}
|
||||
for i, u := range role.Users {
|
||||
// u = organization/username
|
||||
owner, name := util.GetOwnerAndNameFromId(u)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if name == oldName {
|
||||
role.Users[i] = util.GetId(owner, newName)
|
||||
}
|
||||
}
|
||||
for i, u := range role.Roles {
|
||||
// u = organization/username
|
||||
owner, name := util.GetOwnerAndNameFromId(u)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if name == oldName {
|
||||
role.Roles[i] = util.GetId(owner, newName)
|
||||
}
|
||||
@@ -447,14 +463,20 @@ func organizationChangeTrigger(oldName string, newName string) error {
|
||||
}
|
||||
for i, u := range permission.Users {
|
||||
// u = organization/username
|
||||
owner, name := util.GetOwnerAndNameFromId(u)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if name == oldName {
|
||||
permission.Users[i] = util.GetId(owner, newName)
|
||||
}
|
||||
}
|
||||
for i, u := range permission.Roles {
|
||||
// u = organization/username
|
||||
owner, name := util.GetOwnerAndNameFromId(u)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if name == oldName {
|
||||
permission.Roles[i] = util.GetId(owner, newName)
|
||||
}
|
||||
@@ -567,3 +589,40 @@ func (org *Organization) GetInitScore() (int, error) {
|
||||
return strconv.Atoi(conf.GetConfigString("initScore"))
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateOrganizationBalance(owner string, name string, balance float64, currency string, isOrgBalance bool, lang string) error {
|
||||
organization, err := getOrganization(owner, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if organization == nil {
|
||||
return fmt.Errorf(i18n.Translate(lang, "auth:the organization: %s is not found"), fmt.Sprintf("%s/%s", owner, name))
|
||||
}
|
||||
|
||||
// Convert the balance amount from transaction currency to organization's balance currency
|
||||
balanceCurrency := organization.BalanceCurrency
|
||||
if balanceCurrency == "" {
|
||||
balanceCurrency = "USD"
|
||||
}
|
||||
convertedBalance := ConvertCurrency(balance, currency, balanceCurrency)
|
||||
|
||||
var columns []string
|
||||
var newBalance float64
|
||||
if isOrgBalance {
|
||||
newBalance = AddPrices(organization.OrgBalance, convertedBalance)
|
||||
// Check organization balance credit limit
|
||||
if newBalance < organization.BalanceCredit {
|
||||
return fmt.Errorf(i18n.Translate(lang, "general:Insufficient balance: new organization balance %v would be below credit limit %v"), newBalance, organization.BalanceCredit)
|
||||
}
|
||||
organization.OrgBalance = newBalance
|
||||
columns = []string{"org_balance"}
|
||||
} else {
|
||||
// User balance is just a sum of all users' balances, no credit limit check here
|
||||
// Individual user credit limits are checked in UpdateUserBalance
|
||||
organization.UserBalance = AddPrices(organization.UserBalance, convertedBalance)
|
||||
columns = []string{"user_balance"}
|
||||
}
|
||||
|
||||
_, err = ormer.Engine.ID(core.PK{owner, name}).Cols(columns...).Update(organization)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -38,27 +38,38 @@ import (
|
||||
_ "modernc.org/sqlite" // db = sqlite
|
||||
)
|
||||
|
||||
const (
|
||||
defaultConfigPath = "conf/app.conf"
|
||||
defaultExportFilePath = "init_data_dump.json"
|
||||
)
|
||||
|
||||
var (
|
||||
ormer *Ormer = nil
|
||||
createDatabase = true
|
||||
configPath = "conf/app.conf"
|
||||
configPath = defaultConfigPath
|
||||
exportData = false
|
||||
exportFilePath = defaultExportFilePath
|
||||
)
|
||||
|
||||
func InitFlag() {
|
||||
createDatabase = getCreateDatabaseFlag()
|
||||
configPath = getConfigFlag()
|
||||
createDatabasePtr := flag.Bool("createDatabase", false, "true if you need to create database")
|
||||
configPathPtr := flag.String("config", defaultConfigPath, "set it to \"/your/path/app.conf\" if your config file is not in: \"/conf/app.conf\"")
|
||||
exportDataPtr := flag.Bool("export", false, "export database to JSON file and exit (use -exportPath to specify custom location)")
|
||||
exportFilePathPtr := flag.String("exportPath", defaultExportFilePath, "path to the exported data file (used with -export)")
|
||||
flag.Parse()
|
||||
|
||||
createDatabase = *createDatabasePtr
|
||||
configPath = *configPathPtr
|
||||
exportData = *exportDataPtr
|
||||
exportFilePath = *exportFilePathPtr
|
||||
}
|
||||
|
||||
func getCreateDatabaseFlag() bool {
|
||||
res := flag.Bool("createDatabase", false, "true if you need to create database")
|
||||
flag.Parse()
|
||||
return *res
|
||||
func ShouldExportData() bool {
|
||||
return exportData
|
||||
}
|
||||
|
||||
func getConfigFlag() string {
|
||||
res := flag.String("config", "conf/app.conf", "set it to \"/your/path/app.conf\" if your config file is not in: \"/conf/app.conf\"")
|
||||
flag.Parse()
|
||||
return *res
|
||||
func GetExportFilePath() string {
|
||||
return exportFilePath
|
||||
}
|
||||
|
||||
func InitConfig() {
|
||||
@@ -373,6 +384,11 @@ func (a *Ormer) createTable() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Order))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Plan))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -38,7 +38,6 @@ type Payment struct {
|
||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
Price float64 `json:"price"`
|
||||
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
|
||||
IsRecharge bool `xorm:"bool" json:"isRecharge"`
|
||||
|
||||
// Payer Info
|
||||
@@ -54,7 +53,8 @@ type Payment struct {
|
||||
InvoiceRemark string `xorm:"varchar(100)" json:"invoiceRemark"`
|
||||
InvoiceUrl string `xorm:"varchar(255)" json:"invoiceUrl"`
|
||||
// Order Info
|
||||
OutOrderId string `xorm:"varchar(100)" json:"outOrderId"`
|
||||
Order string `xorm:"varchar(100)" json:"order"` // Internal order name
|
||||
OutOrderId string `xorm:"varchar(100)" json:"outOrderId"` // External payment provider's order ID
|
||||
PayUrl string `xorm:"varchar(2000)" json:"payUrl"`
|
||||
SuccessUrl string `xorm:"varchar(2000)" json:"successUrl"` // `successUrl` is redirected from `payUrl` after pay success
|
||||
State pp.PaymentState `xorm:"varchar(100)" json:"state"`
|
||||
@@ -116,12 +116,18 @@ func getPayment(owner string, name string) (*Payment, error) {
|
||||
}
|
||||
|
||||
func GetPayment(id string) (*Payment, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getPayment(owner, name)
|
||||
}
|
||||
|
||||
func UpdatePayment(id string, payment *Payment) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if p, err := getPayment(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if p == nil {
|
||||
@@ -201,7 +207,11 @@ func notifyPayment(body []byte, owner string, paymentName string) (*Payment, *pp
|
||||
}
|
||||
|
||||
if payment.IsRecharge {
|
||||
err = UpdateUserBalance(payment.Owner, payment.User, payment.Price)
|
||||
currency := payment.Currency
|
||||
if currency == "" {
|
||||
currency = "USD"
|
||||
}
|
||||
err = UpdateUserBalance(payment.Owner, payment.User, payment.Price, currency, "en")
|
||||
return payment, notifyResult, err
|
||||
}
|
||||
|
||||
@@ -230,11 +240,57 @@ func NotifyPayment(body []byte, owner string, paymentName string) (*Payment, err
|
||||
|
||||
if transaction != nil {
|
||||
transaction.State = payment.State
|
||||
_, err = UpdateTransaction(transaction.GetId(), transaction)
|
||||
_, err = UpdateTransaction(transaction.GetId(), transaction, "en")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Update order state based on payment status
|
||||
if payment.Order != "" {
|
||||
order, err := getOrder(payment.Owner, payment.Order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if order == nil {
|
||||
return nil, fmt.Errorf("the order: %s does not exist", payment.Order)
|
||||
}
|
||||
|
||||
if payment.State == pp.PaymentStatePaid {
|
||||
order.State = "Paid"
|
||||
order.Message = "Payment successful"
|
||||
order.EndTime = util.GetCurrentTime()
|
||||
} else if payment.State == pp.PaymentStateError {
|
||||
order.State = "PaymentFailed"
|
||||
order.Message = payment.Message
|
||||
} else if payment.State == pp.PaymentStateCanceled {
|
||||
order.State = "Canceled"
|
||||
order.Message = "Payment was cancelled"
|
||||
} else if payment.State == pp.PaymentStateTimeout {
|
||||
order.State = "Timeout"
|
||||
order.Message = "Payment timed out"
|
||||
}
|
||||
_, err = UpdateOrder(order.GetId(), order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update product stock after order state is persisted
|
||||
if payment.State == pp.PaymentStatePaid {
|
||||
product, err := getProduct(payment.Owner, payment.ProductName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if product == nil {
|
||||
return nil, fmt.Errorf("the product: %s does not exist", payment.ProductName)
|
||||
}
|
||||
|
||||
err = UpdateProductStock(product)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return payment, nil
|
||||
|
||||
@@ -148,7 +148,7 @@ func UpdatePermission(id string, permission *Permission) (bool, error) {
|
||||
}
|
||||
|
||||
if permission.ResourceType == "Application" && permission.Model != "" {
|
||||
model, err := GetModelEx(util.GetId(permission.Owner, permission.Model))
|
||||
model, err := getModelEx(permission.Model)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if model == nil {
|
||||
@@ -477,13 +477,19 @@ func (p *Permission) GetModelAndAdapter() string {
|
||||
}
|
||||
|
||||
func (p *Permission) isUserHit(name string) bool {
|
||||
targetOrg, targetName := util.GetOwnerAndNameFromId(name)
|
||||
targetOrg, targetName, err := util.GetOwnerAndNameFromIdWithError(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, user := range p.Users {
|
||||
if user == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
userOrg, userName := util.GetOwnerAndNameFromId(user)
|
||||
userOrg, userName, err := util.GetOwnerAndNameFromIdWithError(user)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if userOrg == targetOrg && (userName == "*" || userName == targetName) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -90,9 +90,13 @@ func (p *Permission) setEnforcerAdapter(enforcer *casbin.Enforcer) error {
|
||||
}
|
||||
|
||||
func (p *Permission) setEnforcerModel(enforcer *casbin.Enforcer) error {
|
||||
permissionModel, err := getModel(p.Owner, p.Model)
|
||||
if err != nil {
|
||||
return err
|
||||
var permissionModel *Model
|
||||
var err error
|
||||
if p.Model != "" {
|
||||
permissionModel, err = GetModel(p.Model)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: return error if permissionModel is nil.
|
||||
@@ -138,7 +142,10 @@ func getPolicies(permission *Permission) [][]string {
|
||||
}
|
||||
|
||||
func getRolesInRole(roleId string, visited map[string]struct{}) ([]*Role, error) {
|
||||
roleOwner, roleName := util.GetOwnerAndNameFromId(roleId)
|
||||
roleOwner, roleName, err := util.GetOwnerAndNameFromIdWithError(roleId)
|
||||
if err != nil {
|
||||
return []*Role{}, err
|
||||
}
|
||||
if roleName == "*" {
|
||||
roles, err := GetRoles(roleOwner)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/xlsx"
|
||||
)
|
||||
|
||||
@@ -36,45 +39,30 @@ func getPermissionMap(owner string) (map[string]*Permission, error) {
|
||||
func UploadPermissions(owner string, path string) (bool, error) {
|
||||
table := xlsx.ReadXlsxFile(path)
|
||||
|
||||
oldUserMap, err := getPermissionMap(owner)
|
||||
if len(table) == 0 {
|
||||
return false, fmt.Errorf("empty table")
|
||||
}
|
||||
|
||||
for idx, row := range table[0] {
|
||||
splitRow := strings.Split(row, "#")
|
||||
if len(splitRow) > 1 {
|
||||
table[0][idx] = splitRow[1]
|
||||
}
|
||||
}
|
||||
|
||||
uploadedPermissions, err := StringArrayToStruct[Permission](table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
oldPermissionMap, err := getPermissionMap(owner)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
newPermissions := []*Permission{}
|
||||
for index, line := range table {
|
||||
line := line
|
||||
if index == 0 || parseLineItem(&line, 0) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
permission := &Permission{
|
||||
Owner: parseLineItem(&line, 0),
|
||||
Name: parseLineItem(&line, 1),
|
||||
CreatedTime: parseLineItem(&line, 2),
|
||||
DisplayName: parseLineItem(&line, 3),
|
||||
|
||||
Users: parseListItem(&line, 4),
|
||||
Roles: parseListItem(&line, 5),
|
||||
Domains: parseListItem(&line, 6),
|
||||
|
||||
Model: parseLineItem(&line, 7),
|
||||
Adapter: parseLineItem(&line, 8),
|
||||
ResourceType: parseLineItem(&line, 9),
|
||||
|
||||
Resources: parseListItem(&line, 10),
|
||||
Actions: parseListItem(&line, 11),
|
||||
|
||||
Effect: parseLineItem(&line, 12),
|
||||
IsEnabled: parseLineItemBool(&line, 13),
|
||||
|
||||
Submitter: parseLineItem(&line, 14),
|
||||
Approver: parseLineItem(&line, 15),
|
||||
ApproveTime: parseLineItem(&line, 16),
|
||||
State: parseLineItem(&line, 17),
|
||||
}
|
||||
|
||||
if _, ok := oldUserMap[permission.GetId()]; !ok {
|
||||
for _, permission := range uploadedPermissions {
|
||||
if _, ok := oldPermissionMap[permission.GetId()]; !ok {
|
||||
newPermissions = append(newPermissions, permission)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,12 +108,18 @@ func getPlan(owner, name string) (*Plan, error) {
|
||||
}
|
||||
|
||||
func GetPlan(id string) (*Plan, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getPlan(owner, name)
|
||||
}
|
||||
|
||||
func UpdatePlan(id string, plan *Plan) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if p, err := getPlan(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if p == nil {
|
||||
|
||||
@@ -17,6 +17,7 @@ package object
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
@@ -38,14 +39,14 @@ func (pricing *Pricing) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", pricing.Owner, pricing.Name)
|
||||
}
|
||||
|
||||
func (pricing *Pricing) HasPlan(planName string) (bool, error) {
|
||||
func (pricing *Pricing) HasPlan(planName string, lang string) (bool, error) {
|
||||
planId := util.GetId(pricing.Owner, planName)
|
||||
plan, err := GetPlan(planId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if plan == nil {
|
||||
return false, fmt.Errorf("plan: %s does not exist", planId)
|
||||
return false, fmt.Errorf(i18n.Translate(lang, "auth:The plan: %s does not exist"), planId)
|
||||
}
|
||||
|
||||
if util.InSlice(pricing.Plans, plan.Name) {
|
||||
@@ -97,7 +98,10 @@ func getPricing(owner, name string) (*Pricing, error) {
|
||||
}
|
||||
|
||||
func GetPricing(id string) (*Pricing, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getPricing(owner, name)
|
||||
}
|
||||
|
||||
@@ -116,7 +120,10 @@ func GetApplicationDefaultPricing(owner, appName string) (*Pricing, error) {
|
||||
}
|
||||
|
||||
func UpdatePricing(id string, pricing *Pricing) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if p, err := getPricing(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if p == nil {
|
||||
@@ -147,21 +154,21 @@ func DeletePricing(pricing *Pricing) (bool, error) {
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func CheckPricingAndPlan(owner, pricingName, planName string) error {
|
||||
func CheckPricingAndPlan(owner, pricingName, planName string, lang string) error {
|
||||
pricingId := util.GetId(owner, pricingName)
|
||||
pricing, err := GetPricing(pricingId)
|
||||
if pricing == nil || err != nil {
|
||||
if pricing == nil && err == nil {
|
||||
err = fmt.Errorf("pricing: %s does not exist", pricingName)
|
||||
err = fmt.Errorf(i18n.Translate(lang, "auth:The pricing: %s does not exist"), pricingName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
ok, err := pricing.HasPlan(planName)
|
||||
ok, err := pricing.HasPlan(planName, lang)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("pricing: %s does not have plan: %s", pricingName, planName)
|
||||
return fmt.Errorf(i18n.Translate(lang, "auth:The pricing: %s does not have plan: %s"), pricingName, planName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@ package object
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/idp"
|
||||
|
||||
"github.com/casdoor/casdoor/pp"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
@@ -31,18 +27,19 @@ type Product struct {
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
|
||||
Image string `xorm:"varchar(100)" json:"image"`
|
||||
Detail string `xorm:"varchar(1000)" json:"detail"`
|
||||
Description string `xorm:"varchar(200)" json:"description"`
|
||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
Price float64 `json:"price"`
|
||||
Quantity int `json:"quantity"`
|
||||
Sold int `json:"sold"`
|
||||
IsRecharge bool `json:"isRecharge"`
|
||||
Providers []string `xorm:"varchar(255)" json:"providers"`
|
||||
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
|
||||
SuccessUrl string `xorm:"varchar(1000)" json:"successUrl"`
|
||||
Image string `xorm:"varchar(100)" json:"image"`
|
||||
Detail string `xorm:"varchar(1000)" json:"detail"`
|
||||
Description string `xorm:"varchar(200)" json:"description"`
|
||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
Price float64 `json:"price"`
|
||||
Quantity int `json:"quantity"`
|
||||
Sold int `json:"sold"`
|
||||
IsRecharge bool `json:"isRecharge"`
|
||||
RechargeOptions []float64 `xorm:"varchar(500)" json:"rechargeOptions"`
|
||||
DisableCustomRecharge bool `json:"disableCustomRecharge"`
|
||||
Providers []string `xorm:"varchar(255)" json:"providers"`
|
||||
SuccessUrl string `xorm:"varchar(1000)" json:"successUrl"`
|
||||
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
|
||||
@@ -94,18 +91,52 @@ func getProduct(owner string, name string) (*Product, error) {
|
||||
}
|
||||
|
||||
func GetProduct(id string) (*Product, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getProduct(owner, name)
|
||||
}
|
||||
|
||||
func UpdateProductStock(product *Product) error {
|
||||
var (
|
||||
affected int64
|
||||
err error
|
||||
)
|
||||
if product.IsRecharge {
|
||||
affected, err = ormer.Engine.ID(core.PK{product.Owner, product.Name}).
|
||||
Incr("sold", 1).
|
||||
Update(&Product{})
|
||||
} else {
|
||||
affected, err = ormer.Engine.ID(core.PK{product.Owner, product.Name}).
|
||||
Where("quantity > 0").
|
||||
Decr("quantity", 1).
|
||||
Incr("sold", 1).
|
||||
Update(&Product{})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
if product.IsRecharge {
|
||||
return fmt.Errorf("failed to update stock for product: %s", product.Name)
|
||||
}
|
||||
return fmt.Errorf("insufficient stock for product: %s", product.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateProduct(id string, product *Product) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if p, err := getProduct(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(product)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -162,190 +193,6 @@ func (product *Product) getProvider(providerName string) (*Provider, error) {
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
func BuyProduct(id string, user *User, providerName, pricingName, planName, host, paymentEnv string, customPrice float64) (payment *Payment, attachInfo map[string]interface{}, err error) {
|
||||
product, err := GetProduct(id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if product == nil {
|
||||
return nil, nil, fmt.Errorf("the product: %s does not exist", id)
|
||||
}
|
||||
|
||||
if product.IsRecharge {
|
||||
if customPrice <= 0 {
|
||||
return nil, nil, fmt.Errorf("the custom price should bigger than zero")
|
||||
} else {
|
||||
product.Price = customPrice
|
||||
}
|
||||
}
|
||||
|
||||
provider, err := product.getProvider(providerName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pProvider, err := GetPaymentProvider(provider)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
owner := product.Owner
|
||||
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
|
||||
paymentName := fmt.Sprintf("payment_%v", util.GenerateTimeId())
|
||||
|
||||
originFrontend, originBackend := getOriginFromHost(host)
|
||||
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
|
||||
notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s", originBackend, owner, paymentName)
|
||||
|
||||
// Create a subscription when pricing and plan are provided
|
||||
// This allows both free users and paid users to subscribe to plans
|
||||
if pricingName != "" && planName != "" {
|
||||
plan, err := GetPlan(util.GetId(owner, planName))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if plan == nil {
|
||||
return nil, nil, fmt.Errorf("the plan: %s does not exist", planName)
|
||||
}
|
||||
|
||||
sub, err := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
_, err = AddSubscription(sub)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
|
||||
}
|
||||
|
||||
if product.SuccessUrl != "" {
|
||||
returnUrl = fmt.Sprintf("%s?transactionOwner=%s&transactionName=%s", product.SuccessUrl, owner, paymentName)
|
||||
}
|
||||
// Create an order
|
||||
payReq := &pp.PayReq{
|
||||
ProviderName: providerName,
|
||||
ProductName: product.Name,
|
||||
PayerName: payerName,
|
||||
PayerId: user.Id,
|
||||
PayerEmail: user.Email,
|
||||
PaymentName: paymentName,
|
||||
ProductDisplayName: product.DisplayName,
|
||||
ProductDescription: product.Description,
|
||||
ProductImage: product.Image,
|
||||
Price: product.Price,
|
||||
Currency: product.Currency,
|
||||
ReturnUrl: returnUrl,
|
||||
NotifyUrl: notifyUrl,
|
||||
PaymentEnv: paymentEnv,
|
||||
}
|
||||
|
||||
// custom process for WeChat & WeChat Pay
|
||||
if provider.Type == "WeChat Pay" {
|
||||
payReq.PayerId, err = getUserExtraProperty(user, "WeChat", idp.BuildWechatOpenIdKey(provider.ClientId2))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else if provider.Type == "Balance" {
|
||||
payReq.PayerId = user.GetId()
|
||||
}
|
||||
|
||||
payResp, err := pProvider.Pay(payReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Create a Payment linked with Product and Order
|
||||
payment = &Payment{
|
||||
Owner: product.Owner,
|
||||
Name: paymentName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: paymentName,
|
||||
|
||||
Provider: provider.Name,
|
||||
Type: provider.Type,
|
||||
|
||||
ProductName: product.Name,
|
||||
ProductDisplayName: product.DisplayName,
|
||||
Detail: product.Detail,
|
||||
Tag: product.Tag,
|
||||
Currency: product.Currency,
|
||||
Price: product.Price,
|
||||
ReturnUrl: product.ReturnUrl,
|
||||
IsRecharge: product.IsRecharge,
|
||||
|
||||
User: user.Name,
|
||||
PayUrl: payResp.PayUrl,
|
||||
SuccessUrl: returnUrl,
|
||||
State: pp.PaymentStateCreated,
|
||||
OutOrderId: payResp.OrderId,
|
||||
}
|
||||
|
||||
transaction := &Transaction{
|
||||
Owner: payment.Owner,
|
||||
Name: payment.Name,
|
||||
DisplayName: payment.DisplayName,
|
||||
Provider: provider.Name,
|
||||
Category: provider.Category,
|
||||
Type: provider.Type,
|
||||
|
||||
ProductName: product.Name,
|
||||
ProductDisplayName: product.DisplayName,
|
||||
Detail: product.Detail,
|
||||
Tag: product.Tag,
|
||||
Currency: product.Currency,
|
||||
Amount: payment.Price,
|
||||
ReturnUrl: payment.ReturnUrl,
|
||||
|
||||
User: payment.User,
|
||||
Application: owner,
|
||||
Payment: payment.GetId(),
|
||||
|
||||
State: pp.PaymentStateCreated,
|
||||
}
|
||||
|
||||
if provider.Type == "Dummy" {
|
||||
payment.State = pp.PaymentStatePaid
|
||||
err = UpdateUserBalance(user.Owner, user.Name, payment.Price)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
} else if provider.Type == "Balance" {
|
||||
if product.Price > user.Balance {
|
||||
return nil, nil, fmt.Errorf("insufficient user balance")
|
||||
}
|
||||
transaction.Amount = -transaction.Amount
|
||||
err = UpdateUserBalance(user.Owner, user.Name, -product.Price)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
payment.State = pp.PaymentStatePaid
|
||||
transaction.State = pp.PaymentStatePaid
|
||||
}
|
||||
|
||||
affected, err := AddPayment(payment)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !affected {
|
||||
return nil, nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
||||
}
|
||||
|
||||
if product.IsRecharge || provider.Type == "Balance" {
|
||||
affected, err = AddTransaction(transaction)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !affected {
|
||||
return nil, nil, fmt.Errorf("failed to add transaction: %s", util.StructToJson(payment))
|
||||
}
|
||||
}
|
||||
|
||||
return payment, payResp.AttachInfo, nil
|
||||
}
|
||||
|
||||
func ExtendProductWithProviders(product *Product) error {
|
||||
if product == nil {
|
||||
return nil
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package object
|
||||
|
||||
|
||||
@@ -182,7 +182,10 @@ func getProvider(owner string, name string) (*Provider, error) {
|
||||
}
|
||||
|
||||
func GetProvider(id string) (*Provider, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getProvider(owner, name)
|
||||
}
|
||||
|
||||
@@ -197,7 +200,10 @@ func GetWechatMiniProgramProvider(application *Application) *Provider {
|
||||
}
|
||||
|
||||
func UpdateProvider(id string, provider *Provider) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if p, err := getProvider(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if p == nil {
|
||||
@@ -339,6 +345,30 @@ func GetPaymentProvider(p *Provider) (pp.PaymentProvider, error) {
|
||||
return nil, err
|
||||
}
|
||||
return pp, nil
|
||||
} else if typ == "Polar" {
|
||||
pp, err := pp.NewPolarPaymentProvider(p.ClientSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pp, nil
|
||||
} else if typ == "Paddle" {
|
||||
pp, err := pp.NewPaddlePaymentProvider(p.ClientSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pp, nil
|
||||
} else if typ == "FastSpring" {
|
||||
pp, err := pp.NewFastSpringPaymentProvider(p.ClientId, p.ClientSecret, p.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pp, nil
|
||||
} else if typ == "Lemon Squeezy" {
|
||||
pp, err := pp.NewLemonSqueezyPaymentProvider(p.ClientId, p.ClientSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pp, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
|
||||
}
|
||||
@@ -349,7 +379,10 @@ func (p *Provider) GetId() string {
|
||||
}
|
||||
|
||||
func GetCaptchaProviderByOwnerName(applicationId, lang string) (*Provider, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(applicationId)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(applicationId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
provider := Provider{Owner: owner, Name: name, Category: "Captcha"}
|
||||
existed, err := ormer.Engine.Get(&provider)
|
||||
if err != nil {
|
||||
@@ -387,7 +420,10 @@ func GetCaptchaProviderByApplication(applicationId, isCurrentProvider, lang stri
|
||||
}
|
||||
|
||||
func GetFaceIdProviderByOwnerName(applicationId, lang string) (*Provider, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(applicationId)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(applicationId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
provider := Provider{Owner: owner, Name: name, Category: "Face ID"}
|
||||
existed, err := ormer.Engine.Get(&provider)
|
||||
if err != nil {
|
||||
|
||||
@@ -69,7 +69,10 @@ func getPaginationRadiusAccounting(owner, field, value, sortField, sortOrder str
|
||||
}
|
||||
|
||||
func GetRadiusAccounting(id string) (*RadiusAccounting, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getRadiusAccounting(owner, name)
|
||||
}
|
||||
|
||||
@@ -95,8 +98,11 @@ func DeleteRadiusAccounting(ra *RadiusAccounting) error {
|
||||
}
|
||||
|
||||
func UpdateRadiusAccounting(id string, ra *RadiusAccounting) error {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
_, err := ormer.Engine.ID(core.PK{owner, name}).Update(ra)
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = ormer.Engine.ID(core.PK{owner, name}).Update(ra)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user