mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-25 08:20:30 +08:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e3f28e8b4c | ||
![]() |
3373174c65 | ||
![]() |
2fb79e4092 | ||
![]() |
5846e337c7 | ||
![]() |
44f4de1440 | ||
![]() |
27adeb4620 | ||
![]() |
5c107db43b | ||
![]() |
27187b3a54 | ||
![]() |
14fcedcc5d | ||
![]() |
e7c015f288 | ||
![]() |
c4819602ec | ||
![]() |
dea03cdd15 | ||
![]() |
21f394847e | ||
![]() |
9bef9691fb | ||
![]() |
141f22a707 | ||
![]() |
02329d342a | ||
![]() |
b9d3e2184c | ||
![]() |
28caf8550e | ||
![]() |
79159dc809 | ||
![]() |
63081641d6 | ||
![]() |
698f24f762 | ||
![]() |
5499e62d7f | ||
![]() |
f8905ae64c | ||
![]() |
a42594859f | ||
![]() |
46e0bc1a39 | ||
![]() |
ffe2330238 | ||
![]() |
ec53616dc8 | ||
![]() |
067276d739 | ||
![]() |
468ceb6b71 | ||
![]() |
b31a317585 | ||
![]() |
396b6fb65f | ||
![]() |
be637fca81 | ||
![]() |
374928e719 | ||
![]() |
5c103e8cd3 | ||
![]() |
85b86e8831 | ||
![]() |
08864686f3 |
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
cache-dependency-path: ./web/yarn.lock
|
cache-dependency-path: ./web/yarn.lock
|
||||||
- run: yarn install && CI=false yarn run build
|
- run: yarn install && CI=false yarn run build
|
||||||
@@ -101,7 +101,7 @@ jobs:
|
|||||||
working-directory: ./
|
working-directory: ./
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
cache-dependency-path: ./web/yarn.lock
|
cache-dependency-path: ./web/yarn.lock
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
@@ -137,7 +137,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
|
|
||||||
- name: Fetch Previous version
|
- name: Fetch Previous version
|
||||||
id: get-previous-tag
|
id: get-previous-tag
|
||||||
|
40
.github/workflows/helm.yml
vendored
Normal file
40
.github/workflows/helm.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
name: Helm Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'manifests/casdoor/Chart.yaml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-helm-chart:
|
||||||
|
name: Release Helm Chart
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Helm
|
||||||
|
uses: azure/setup-helm@v3
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Release Helm Chart
|
||||||
|
run: |
|
||||||
|
cd manifests/casdoor
|
||||||
|
REGISTRY=oci://registry-1.docker.io/casbin
|
||||||
|
helm package .
|
||||||
|
PKG_NAME=$(ls *.tgz)
|
||||||
|
helm repo index . --url $REGISTRY --merge index.yaml
|
||||||
|
helm push $PKG_NAME $REGISTRY
|
||||||
|
rm $PKG_NAME
|
||||||
|
|
||||||
|
- name: Commit updated helm index.yaml
|
||||||
|
uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
|
with:
|
||||||
|
commit_message: 'ci: update helm index.yaml'
|
@@ -1,10 +1,10 @@
|
|||||||
FROM node:16.18.0 AS FRONT
|
FROM node:18.19.0 AS FRONT
|
||||||
WORKDIR /web
|
WORKDIR /web
|
||||||
COPY ./web .
|
COPY ./web .
|
||||||
RUN yarn install --frozen-lockfile --network-timeout 1000000 && yarn run build
|
RUN yarn install --frozen-lockfile --network-timeout 1000000 && yarn run build
|
||||||
|
|
||||||
|
|
||||||
FROM golang:1.19.9 AS BACK
|
FROM golang:1.20.12 AS BACK
|
||||||
WORKDIR /go/src/casdoor
|
WORKDIR /go/src/casdoor
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN ./build.sh
|
RUN ./build.sh
|
||||||
|
14
README.md
14
README.md
@@ -42,6 +42,20 @@
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<sup>Sponsored by</sup>
|
||||||
|
<br>
|
||||||
|
<a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.casbin.org/img/stytch-white.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://cdn.casbin.org/img/stytch-charcoal.png">
|
||||||
|
<img src="https://cdn.casbin.org/img/stytch-charcoal.png" width="275">
|
||||||
|
</picture>
|
||||||
|
</a><br/>
|
||||||
|
<a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin"><b>Build auth with fraud prevention, faster.</b><br/> Try Stytch for API-first authentication, user & org management, multi-tenant SSO, MFA, device fingerprinting, and more.</a>
|
||||||
|
<br>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Online demo
|
## Online demo
|
||||||
|
|
||||||
- Read-only site: https://door.casdoor.com (any modification operation will fail)
|
- Read-only site: https://door.casdoor.com (any modification operation will fail)
|
||||||
|
@@ -92,6 +92,9 @@ p, *, *, GET, /api/get-plan, *, *
|
|||||||
p, *, *, GET, /api/get-subscription, *, *
|
p, *, *, GET, /api/get-subscription, *, *
|
||||||
p, *, *, GET, /api/get-provider, *, *
|
p, *, *, GET, /api/get-provider, *, *
|
||||||
p, *, *, GET, /api/get-organization-names, *, *
|
p, *, *, GET, /api/get-organization-names, *, *
|
||||||
|
p, *, *, GET, /api/get-all-objects, *, *
|
||||||
|
p, *, *, GET, /api/get-all-actions, *, *
|
||||||
|
p, *, *, GET, /api/get-all-roles, *, *
|
||||||
`
|
`
|
||||||
|
|
||||||
sa := stringadapter.NewAdapter(ruleText)
|
sa := stringadapter.NewAdapter(ruleText)
|
||||||
@@ -147,7 +150,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 {
|
func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
||||||
if method == "POST" {
|
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" {
|
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/check-user-password" || strings.HasPrefix(urlPath, "/api/mfa/") {
|
||||||
return true
|
return true
|
||||||
} else if urlPath == "/api/update-user" {
|
} else if urlPath == "/api/update-user" {
|
||||||
// Allow ordinary users to update their own information
|
// Allow ordinary users to update their own information
|
||||||
|
@@ -56,6 +56,17 @@ type Captcha struct {
|
|||||||
SubType string `json:"subType"`
|
SubType string `json:"subType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this API is used by "Api URL" of Flarum's FoF Passport plugin
|
||||||
|
// https://github.com/FriendsOfFlarum/passport
|
||||||
|
type LaravelResponse struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
EmailVerifiedAt string `json:"email_verified_at"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
// Signup
|
// Signup
|
||||||
// @Tag Login API
|
// @Tag Login API
|
||||||
// @Title Signup
|
// @Title Signup
|
||||||
@@ -238,7 +249,7 @@ func (c *ApiController) Signup() {
|
|||||||
// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri"
|
// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri"
|
||||||
// @Param state query string false "state"
|
// @Param state query string false "state"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /logout [get,post]
|
// @router /logout [post]
|
||||||
func (c *ApiController) Logout() {
|
func (c *ApiController) Logout() {
|
||||||
// https://openid.net/specs/openid-connect-rpinitiated-1_0-final.html
|
// https://openid.net/specs/openid-connect-rpinitiated-1_0-final.html
|
||||||
accessToken := c.Input().Get("id_token_hint")
|
accessToken := c.Input().Get("id_token_hint")
|
||||||
@@ -418,7 +429,7 @@ func (c *ApiController) GetUserinfo() {
|
|||||||
// @Title UserInfo2
|
// @Title UserInfo2
|
||||||
// @Tag Account API
|
// @Tag Account API
|
||||||
// @Description return Laravel compatible user information according to OAuth 2.0
|
// @Description return Laravel compatible user information according to OAuth 2.0
|
||||||
// @Success 200 {object} LaravelResponse The Response object
|
// @Success 200 {object} controllers.LaravelResponse The Response object
|
||||||
// @router /user [get]
|
// @router /user [get]
|
||||||
func (c *ApiController) GetUserinfo2() {
|
func (c *ApiController) GetUserinfo2() {
|
||||||
user, ok := c.RequireSignedInUser()
|
user, ok := c.RequireSignedInUser()
|
||||||
@@ -426,17 +437,6 @@ func (c *ApiController) GetUserinfo2() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// this API is used by "Api URL" of Flarum's FoF Passport plugin
|
|
||||||
// https://github.com/FriendsOfFlarum/passport
|
|
||||||
type LaravelResponse struct {
|
|
||||||
Id string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
EmailVerifiedAt string `json:"email_verified_at"`
|
|
||||||
CreatedAt string `json:"created_at"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
response := LaravelResponse{
|
response := LaravelResponse{
|
||||||
Id: user.Id,
|
Id: user.Id,
|
||||||
Name: user.Name,
|
Name: user.Name,
|
||||||
@@ -454,6 +454,7 @@ func (c *ApiController) GetUserinfo2() {
|
|||||||
// @Tag Login API
|
// @Tag Login API
|
||||||
// @Title GetCaptcha
|
// @Title GetCaptcha
|
||||||
// @router /api/get-captcha [get]
|
// @router /api/get-captcha [get]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) GetCaptcha() {
|
func (c *ApiController) GetCaptcha() {
|
||||||
applicationId := c.Input().Get("applicationId")
|
applicationId := c.Input().Get("applicationId")
|
||||||
isCurrentProvider := c.Input().Get("isCurrentProvider")
|
isCurrentProvider := c.Input().Get("isCurrentProvider")
|
||||||
|
@@ -110,6 +110,14 @@ func (c *ApiController) GetApplication() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0 as an initialization value, corresponding to the default configuration parameters
|
||||||
|
if application.FailedSigninLimit == 0 {
|
||||||
|
application.FailedSigninLimit = object.DefaultFailedSigninLimit
|
||||||
|
}
|
||||||
|
if application.FailedSigninfrozenTime == 0 {
|
||||||
|
application.FailedSigninfrozenTime = object.DefaultFailedSigninfrozenTime
|
||||||
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetMaskedApplication(application, userId))
|
c.ResponseOk(object.GetMaskedApplication(application, userId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -222,7 +222,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
|||||||
// @Param redirectUri query string true "redirect uri"
|
// @Param redirectUri query string true "redirect uri"
|
||||||
// @Param scope query string true "scope"
|
// @Param scope query string true "scope"
|
||||||
// @Param state query string true "state"
|
// @Param state query string true "state"
|
||||||
// @Success 200 {object} Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /get-app-login [get]
|
// @router /get-app-login [get]
|
||||||
func (c *ApiController) GetApplicationLogin() {
|
func (c *ApiController) GetApplicationLogin() {
|
||||||
clientId := c.Input().Get("clientId")
|
clientId := c.Input().Get("clientId")
|
||||||
@@ -342,7 +342,28 @@ func (c *ApiController) Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var application *object.Application
|
||||||
|
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error(), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if application == nil {
|
||||||
|
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
verificationCodeType := object.GetVerifyType(authForm.Username)
|
verificationCodeType := object.GetVerifyType(authForm.Username)
|
||||||
|
if verificationCodeType == object.VerifyTypeEmail && !application.IsCodeSigninViaEmailEnabled() {
|
||||||
|
c.ResponseError(c.T("auth:The login method: login with email is not enabled for the application"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if verificationCodeType == object.VerifyTypePhone && !application.IsCodeSigninViaSmsEnabled() {
|
||||||
|
c.ResponseError(c.T("auth:The login method: login with SMS is not enabled for the application"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var checkDest string
|
var checkDest string
|
||||||
if verificationCodeType == object.VerifyTypePhone {
|
if verificationCodeType == object.VerifyTypePhone {
|
||||||
authForm.CountryCode = user.GetCountryCode(authForm.CountryCode)
|
authForm.CountryCode = user.GetCountryCode(authForm.CountryCode)
|
||||||
@@ -378,7 +399,7 @@ func (c *ApiController) Login() {
|
|||||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !application.EnablePassword {
|
if !application.IsPasswordEnabled() {
|
||||||
c.ResponseError(c.T("auth:The login method: login with password is not enabled for the application"))
|
c.ResponseError(c.T("auth:The login method: login with password is not enabled for the application"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -887,6 +908,7 @@ func (c *ApiController) HandleSamlLogin() {
|
|||||||
// @Tag HandleOfficialAccountEvent API
|
// @Tag HandleOfficialAccountEvent API
|
||||||
// @Title HandleOfficialAccountEvent
|
// @Title HandleOfficialAccountEvent
|
||||||
// @router /api/webhook [POST]
|
// @router /api/webhook [POST]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) HandleOfficialAccountEvent() {
|
func (c *ApiController) HandleOfficialAccountEvent() {
|
||||||
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
|
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -917,6 +939,7 @@ func (c *ApiController) HandleOfficialAccountEvent() {
|
|||||||
// @Tag GetWebhookEventType API
|
// @Tag GetWebhookEventType API
|
||||||
// @Title GetWebhookEventType
|
// @Title GetWebhookEventType
|
||||||
// @router /api/get-webhook-event [GET]
|
// @router /api/get-webhook-event [GET]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) GetWebhookEventType() {
|
func (c *ApiController) GetWebhookEventType() {
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
@@ -946,8 +969,14 @@ func (c *ApiController) GetCaptchaStatus() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
failedSigninLimit, _, err := object.GetFailedSigninConfigByUser(user)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var captchaEnabled bool
|
var captchaEnabled bool
|
||||||
if user != nil && user.SigninWrongTimes >= object.SigninWrongTimesLimit {
|
if user != nil && user.SigninWrongTimes >= failedSigninLimit {
|
||||||
captchaEnabled = true
|
captchaEnabled = true
|
||||||
}
|
}
|
||||||
c.ResponseOk(captchaEnabled)
|
c.ResponseOk(captchaEnabled)
|
||||||
@@ -958,6 +987,7 @@ func (c *ApiController) GetCaptchaStatus() {
|
|||||||
// @Tag Callback API
|
// @Tag Callback API
|
||||||
// @Description Get Login Error Counts
|
// @Description Get Login Error Counts
|
||||||
// @router /api/Callback [post]
|
// @router /api/Callback [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) Callback() {
|
func (c *ApiController) Callback() {
|
||||||
code := c.GetString("code")
|
code := c.GetString("code")
|
||||||
state := c.GetString("state")
|
state := c.GetString("state")
|
||||||
|
@@ -16,6 +16,7 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/object"
|
"github.com/casdoor/casdoor/object"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@@ -25,7 +26,7 @@ import (
|
|||||||
// @Title Enforce
|
// @Title Enforce
|
||||||
// @Tag Enforce API
|
// @Tag Enforce API
|
||||||
// @Description Call Casbin Enforce API
|
// @Description Call Casbin Enforce API
|
||||||
// @Param body body object.CasbinRequest true "Casbin request"
|
// @Param body body []string true "Casbin request"
|
||||||
// @Param permissionId query string false "permission id"
|
// @Param permissionId query string false "permission id"
|
||||||
// @Param modelId query string false "model id"
|
// @Param modelId query string false "model id"
|
||||||
// @Param resourceId query string false "resource id"
|
// @Param resourceId query string false "resource id"
|
||||||
@@ -42,7 +43,7 @@ func (c *ApiController) Enforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request object.CasbinRequest
|
var request []string
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
@@ -56,13 +57,22 @@ func (c *ApiController) Enforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := enforcer.Enforce(request...)
|
res := []bool{}
|
||||||
|
keyRes := []string{}
|
||||||
|
|
||||||
|
// type transformation
|
||||||
|
interfaceRequest := util.StringToInterfaceArray(request)
|
||||||
|
|
||||||
|
enforceResult, err := enforcer.Enforce(interfaceRequest...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
res = append(res, enforceResult)
|
||||||
|
keyRes = append(keyRes, enforcer.GetModelAndAdapter())
|
||||||
|
|
||||||
|
c.ResponseOk(res, keyRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,22 +82,24 @@ func (c *ApiController) Enforce() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res := []bool{}
|
|
||||||
|
|
||||||
if permission == nil {
|
if permission == nil {
|
||||||
res = append(res, false)
|
c.ResponseError(fmt.Sprintf("permission: %s doesn't exist", permissionId))
|
||||||
} else {
|
return
|
||||||
enforceResult, err := object.Enforce(permission, &request)
|
|
||||||
if err != nil {
|
|
||||||
c.ResponseError(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, enforceResult)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
res := []bool{}
|
||||||
|
keyRes := []string{}
|
||||||
|
|
||||||
|
enforceResult, err := object.Enforce(permission, request)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, enforceResult)
|
||||||
|
keyRes = append(keyRes, permission.GetModelAndAdapter())
|
||||||
|
|
||||||
|
c.ResponseOk(res, keyRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,32 +123,33 @@ func (c *ApiController) Enforce() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res := []bool{}
|
res := []bool{}
|
||||||
|
keyRes := []string{}
|
||||||
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
||||||
for _, permissionIds := range listPermissionIdMap {
|
for key, permissionIds := range listPermissionIdMap {
|
||||||
firstPermission, err := object.GetPermission(permissionIds[0])
|
firstPermission, err := object.GetPermission(permissionIds[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
enforceResult, err := object.Enforce(firstPermission, &request, permissionIds...)
|
enforceResult, err := object.Enforce(firstPermission, request, permissionIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, enforceResult)
|
res = append(res, enforceResult)
|
||||||
|
keyRes = append(keyRes, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
c.ResponseOk(res, keyRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchEnforce
|
// BatchEnforce
|
||||||
// @Title BatchEnforce
|
// @Title BatchEnforce
|
||||||
// @Tag Enforce API
|
// @Tag Enforce API
|
||||||
// @Description Call Casbin BatchEnforce API
|
// @Description Call Casbin BatchEnforce API
|
||||||
// @Param body body object.CasbinRequest true "array of casbin requests"
|
// @Param body body []string true "array of casbin requests"
|
||||||
// @Param permissionId query string false "permission id"
|
// @Param permissionId query string false "permission id"
|
||||||
// @Param modelId query string false "model id"
|
// @Param modelId query string false "model id"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
@@ -146,7 +159,7 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
modelId := c.Input().Get("modelId")
|
modelId := c.Input().Get("modelId")
|
||||||
enforcerId := c.Input().Get("enforcerId")
|
enforcerId := c.Input().Get("enforcerId")
|
||||||
|
|
||||||
var requests []object.CasbinRequest
|
var requests [][]string
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
@@ -160,13 +173,22 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := enforcer.BatchEnforce(requests)
|
res := [][]bool{}
|
||||||
|
keyRes := []string{}
|
||||||
|
|
||||||
|
// type transformation
|
||||||
|
interfaceRequests := util.StringToInterfaceArray2d(requests)
|
||||||
|
|
||||||
|
enforceResult, err := enforcer.BatchEnforce(interfaceRequests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
res = append(res, enforceResult)
|
||||||
|
keyRes = append(keyRes, enforcer.GetModelAndAdapter())
|
||||||
|
|
||||||
|
c.ResponseOk(res, keyRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,28 +198,24 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res := [][]bool{}
|
|
||||||
|
|
||||||
if permission == nil {
|
if permission == nil {
|
||||||
l := len(requests)
|
c.ResponseError(fmt.Sprintf("permission: %s doesn't exist", permissionId))
|
||||||
resRequest := make([]bool, l)
|
return
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
resRequest[i] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, resRequest)
|
|
||||||
} else {
|
|
||||||
enforceResult, err := object.BatchEnforce(permission, &requests)
|
|
||||||
if err != nil {
|
|
||||||
c.ResponseError(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, enforceResult)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
res := [][]bool{}
|
||||||
|
keyRes := []string{}
|
||||||
|
|
||||||
|
enforceResult, err := object.BatchEnforce(permission, requests)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, enforceResult)
|
||||||
|
keyRes = append(keyRes, permission.GetModelAndAdapter())
|
||||||
|
|
||||||
|
c.ResponseOk(res, keyRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +233,7 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res := [][]bool{}
|
res := [][]bool{}
|
||||||
|
keyRes := []string{}
|
||||||
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
||||||
for _, permissionIds := range listPermissionIdMap {
|
for _, permissionIds := range listPermissionIdMap {
|
||||||
firstPermission, err := object.GetPermission(permissionIds[0])
|
firstPermission, err := object.GetPermission(permissionIds[0])
|
||||||
@@ -224,16 +242,17 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
enforceResult, err := object.BatchEnforce(firstPermission, &requests, permissionIds...)
|
enforceResult, err := object.BatchEnforce(firstPermission, requests, permissionIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, enforceResult)
|
res = append(res, enforceResult)
|
||||||
|
keyRes = append(keyRes, firstPermission.GetModelAndAdapter())
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
c.ResponseOk(res, keyRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) GetAllObjects() {
|
func (c *ApiController) GetAllObjects() {
|
||||||
|
@@ -71,7 +71,7 @@ func (c *ApiController) GetEnforcers() {
|
|||||||
// @Tag Enforcer API
|
// @Tag Enforcer API
|
||||||
// @Description get enforcer
|
// @Description get enforcer
|
||||||
// @Param id query string true "The id ( owner/name ) of enforcer"
|
// @Param id query string true "The id ( owner/name ) of enforcer"
|
||||||
// @Success 200 {object} object
|
// @Success 200 {object} object.Enforcer
|
||||||
// @router /get-enforcer [get]
|
// @router /get-enforcer [get]
|
||||||
func (c *ApiController) GetEnforcer() {
|
func (c *ApiController) GetEnforcer() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
@@ -99,7 +99,7 @@ func (c *ApiController) GetEnforcer() {
|
|||||||
// @Description update enforcer
|
// @Description update enforcer
|
||||||
// @Param id query string true "The id ( owner/name ) of enforcer"
|
// @Param id query string true "The id ( owner/name ) of enforcer"
|
||||||
// @Param enforcer body object true "The enforcer object"
|
// @Param enforcer body object true "The enforcer object"
|
||||||
// @Success 200 {object} object
|
// @Success 200 {object} object.Enforcer
|
||||||
// @router /update-enforcer [post]
|
// @router /update-enforcer [post]
|
||||||
func (c *ApiController) UpdateEnforcer() {
|
func (c *ApiController) UpdateEnforcer() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
@@ -120,7 +120,7 @@ func (c *ApiController) UpdateEnforcer() {
|
|||||||
// @Tag Enforcer API
|
// @Tag Enforcer API
|
||||||
// @Description add enforcer
|
// @Description add enforcer
|
||||||
// @Param enforcer body object true "The enforcer object"
|
// @Param enforcer body object true "The enforcer object"
|
||||||
// @Success 200 {object} object
|
// @Success 200 {object} object.Enforcer
|
||||||
// @router /add-enforcer [post]
|
// @router /add-enforcer [post]
|
||||||
func (c *ApiController) AddEnforcer() {
|
func (c *ApiController) AddEnforcer() {
|
||||||
enforcer := object.Enforcer{}
|
enforcer := object.Enforcer{}
|
||||||
@@ -138,8 +138,8 @@ func (c *ApiController) AddEnforcer() {
|
|||||||
// @Title DeleteEnforcer
|
// @Title DeleteEnforcer
|
||||||
// @Tag Enforcer API
|
// @Tag Enforcer API
|
||||||
// @Description delete enforcer
|
// @Description delete enforcer
|
||||||
// @Param body body object.Enforce true "The enforcer object"
|
// @Param body body object.Enforcer true "The enforcer object"
|
||||||
// @Success 200 {object} object
|
// @Success 200 {object} object.Enforcer
|
||||||
// @router /delete-enforcer [post]
|
// @router /delete-enforcer [post]
|
||||||
func (c *ApiController) DeleteEnforcer() {
|
func (c *ApiController) DeleteEnforcer() {
|
||||||
var enforcer object.Enforcer
|
var enforcer object.Enforcer
|
||||||
|
164
controllers/invitation.go
Normal file
164
controllers/invitation.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/beego/beego/utils/pagination"
|
||||||
|
"github.com/casdoor/casdoor/object"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetInvitations
|
||||||
|
// @Title GetInvitations
|
||||||
|
// @Tag Invitation API
|
||||||
|
// @Description get invitations
|
||||||
|
// @Param owner query string true "The owner of invitations"
|
||||||
|
// @Success 200 {array} object.Invitation The Response object
|
||||||
|
// @router /get-invitations [get]
|
||||||
|
func (c *ApiController) GetInvitations() {
|
||||||
|
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 == "" {
|
||||||
|
invitations, err := object.GetInvitations(owner)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(invitations)
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
count, err := object.GetInvitationCount(owner, field, value)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||||
|
invitations, err := object.GetPaginationInvitations(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(invitations, paginator.Nums())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInvitation
|
||||||
|
// @Title GetInvitation
|
||||||
|
// @Tag Invitation API
|
||||||
|
// @Description get invitation
|
||||||
|
// @Param id query string true "The id ( owner/name ) of the invitation"
|
||||||
|
// @Success 200 {object} object.Invitation The Response object
|
||||||
|
// @router /get-invitation [get]
|
||||||
|
func (c *ApiController) GetInvitation() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
|
invitation, err := object.GetInvitation(id)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(invitation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateInvitation
|
||||||
|
// @Title UpdateInvitation
|
||||||
|
// @Tag Invitation API
|
||||||
|
// @Description update invitation
|
||||||
|
// @Param id query string true "The id ( owner/name ) of the invitation"
|
||||||
|
// @Param body body object.Invitation true "The details of the invitation"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /update-invitation [post]
|
||||||
|
func (c *ApiController) UpdateInvitation() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
|
var invitation object.Invitation
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &invitation)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.UpdateInvitation(id, &invitation))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInvitation
|
||||||
|
// @Title AddInvitation
|
||||||
|
// @Tag Invitation API
|
||||||
|
// @Description add invitation
|
||||||
|
// @Param body body object.Invitation true "The details of the invitation"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /add-invitation [post]
|
||||||
|
func (c *ApiController) AddInvitation() {
|
||||||
|
var invitation object.Invitation
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &invitation)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.AddInvitation(&invitation))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteInvitation
|
||||||
|
// @Title DeleteInvitation
|
||||||
|
// @Tag Invitation API
|
||||||
|
// @Description delete invitation
|
||||||
|
// @Param body body object.Invitation true "The details of the invitation"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /delete-invitation [post]
|
||||||
|
func (c *ApiController) DeleteInvitation() {
|
||||||
|
var invitation object.Invitation
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &invitation)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.DeleteInvitation(&invitation))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyInvitation
|
||||||
|
// @Title VerifyInvitation
|
||||||
|
// @Tag Invitation API
|
||||||
|
// @Description verify invitation
|
||||||
|
// @Param id query string true "The id ( owner/name ) of the invitation"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /verify-invitation [get]
|
||||||
|
func (c *ApiController) VerifyInvitation() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
|
payment, attachInfo, err := object.VerifyInvitation(id)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(payment, attachInfo)
|
||||||
|
}
|
@@ -42,7 +42,7 @@ type LdapSyncResp struct {
|
|||||||
// @Tag Account API
|
// @Tag Account API
|
||||||
// @Description get ldap users
|
// @Description get ldap users
|
||||||
// Param id string true "id"
|
// Param id string true "id"
|
||||||
// @Success 200 {object} LdapResp The Response object
|
// @Success 200 {object} controllers.LdapResp The Response object
|
||||||
// @router /get-ldap-users [get]
|
// @router /get-ldap-users [get]
|
||||||
func (c *ApiController) GetLdapUsers() {
|
func (c *ApiController) GetLdapUsers() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
@@ -250,7 +250,7 @@ func (c *ApiController) DeleteLdap() {
|
|||||||
// @Tag Account API
|
// @Tag Account API
|
||||||
// @Description sync ldap users
|
// @Description sync ldap users
|
||||||
// @Param id query string true "id"
|
// @Param id query string true "id"
|
||||||
// @Success 200 {object} LdapSyncResp The Response object
|
// @Success 200 {object} controllers.LdapSyncResp The Response object
|
||||||
// @router /sync-ldap-users [post]
|
// @router /sync-ldap-users [post]
|
||||||
func (c *ApiController) SyncLdapUsers() {
|
func (c *ApiController) SyncLdapUsers() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
@@ -26,8 +26,10 @@ type LinkForm struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unlink ...
|
// Unlink ...
|
||||||
// @router /unlink [post]
|
|
||||||
// @Tag Login API
|
// @Tag Login API
|
||||||
|
// @Title Unlink
|
||||||
|
// @router /unlink [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) Unlink() {
|
func (c *ApiController) Unlink() {
|
||||||
user, ok := c.RequireSignedInUser()
|
user, ok := c.RequireSignedInUser()
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@@ -73,7 +73,7 @@ func (c *ApiController) MfaSetupInitiate() {
|
|||||||
// @Description setup verify totp
|
// @Description setup verify totp
|
||||||
// @param secret form string true "MFA secret"
|
// @param secret form string true "MFA secret"
|
||||||
// @param passcode form string true "MFA passcode"
|
// @param passcode form string true "MFA passcode"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /mfa/setup/verify [post]
|
// @router /mfa/setup/verify [post]
|
||||||
func (c *ApiController) MfaSetupVerify() {
|
func (c *ApiController) MfaSetupVerify() {
|
||||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||||
@@ -104,7 +104,7 @@ func (c *ApiController) MfaSetupVerify() {
|
|||||||
// @param owner form string true "owner of user"
|
// @param owner form string true "owner of user"
|
||||||
// @param name form string true "name of user"
|
// @param name form string true "name of user"
|
||||||
// @param type form string true "MFA auth type"
|
// @param type form string true "MFA auth type"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /mfa/setup/enable [post]
|
// @router /mfa/setup/enable [post]
|
||||||
func (c *ApiController) MfaSetupEnable() {
|
func (c *ApiController) MfaSetupEnable() {
|
||||||
owner := c.Ctx.Request.Form.Get("owner")
|
owner := c.Ctx.Request.Form.Get("owner")
|
||||||
@@ -143,7 +143,7 @@ func (c *ApiController) MfaSetupEnable() {
|
|||||||
// @Description: Delete MFA
|
// @Description: Delete MFA
|
||||||
// @param owner form string true "owner of user"
|
// @param owner form string true "owner of user"
|
||||||
// @param name form string true "name of user"
|
// @param name form string true "name of user"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /delete-mfa/ [post]
|
// @router /delete-mfa/ [post]
|
||||||
func (c *ApiController) DeleteMfa() {
|
func (c *ApiController) DeleteMfa() {
|
||||||
owner := c.Ctx.Request.Form.Get("owner")
|
owner := c.Ctx.Request.Form.Get("owner")
|
||||||
@@ -176,7 +176,7 @@ func (c *ApiController) DeleteMfa() {
|
|||||||
// @param owner form string true "owner of user"
|
// @param owner form string true "owner of user"
|
||||||
// @param name form string true "name of user"
|
// @param name form string true "name of user"
|
||||||
// @param id form string true "id of user's MFA props"
|
// @param id form string true "id of user's MFA props"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /set-preferred-mfa [post]
|
// @router /set-preferred-mfa [post]
|
||||||
func (c *ApiController) SetPreferredMfa() {
|
func (c *ApiController) SetPreferredMfa() {
|
||||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||||
|
@@ -178,7 +178,7 @@ func (c *ApiController) DeleteOrganization() {
|
|||||||
// @Tag Organization API
|
// @Tag Organization API
|
||||||
// @Description get default application
|
// @Description get default application
|
||||||
// @Param id query string true "organization id"
|
// @Param id query string true "organization id"
|
||||||
// @Success 200 {object} Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /get-default-application [get]
|
// @router /get-default-application [get]
|
||||||
func (c *ApiController) GetDefaultApplication() {
|
func (c *ApiController) GetDefaultApplication() {
|
||||||
userId := c.GetSessionUsername()
|
userId := c.GetSessionUsername()
|
||||||
|
@@ -51,9 +51,14 @@ type NotificationForm struct {
|
|||||||
// @Param clientId query string true "The clientId of the application"
|
// @Param clientId query string true "The clientId of the application"
|
||||||
// @Param clientSecret query string true "The clientSecret of the application"
|
// @Param clientSecret query string true "The clientSecret of the application"
|
||||||
// @Param from body controllers.EmailForm true "Details of the email request"
|
// @Param from body controllers.EmailForm true "Details of the email request"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /api/send-email [post]
|
// @router /api/send-email [post]
|
||||||
func (c *ApiController) SendEmail() {
|
func (c *ApiController) SendEmail() {
|
||||||
|
user, ok := c.RequireSignedInUser()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var emailForm EmailForm
|
var emailForm EmailForm
|
||||||
|
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &emailForm)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &emailForm)
|
||||||
@@ -108,8 +113,13 @@ func (c *ApiController) SendEmail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
code := "123456"
|
code := "123456"
|
||||||
|
|
||||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||||
content := fmt.Sprintf(emailForm.Content, code)
|
content := strings.Replace(provider.Content, "%s", code, 1)
|
||||||
|
if user != nil {
|
||||||
|
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
|
||||||
|
}
|
||||||
|
|
||||||
for _, receiver := range emailForm.Receivers {
|
for _, receiver := range emailForm.Receivers {
|
||||||
err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender)
|
err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -128,7 +138,7 @@ func (c *ApiController) SendEmail() {
|
|||||||
// @Param clientId query string true "The clientId of the application"
|
// @Param clientId query string true "The clientId of the application"
|
||||||
// @Param clientSecret query string true "The clientSecret of the application"
|
// @Param clientSecret query string true "The clientSecret of the application"
|
||||||
// @Param from body controllers.SmsForm true "Details of the sms request"
|
// @Param from body controllers.SmsForm true "Details of the sms request"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /api/send-sms [post]
|
// @router /api/send-sms [post]
|
||||||
func (c *ApiController) SendSms() {
|
func (c *ApiController) SendSms() {
|
||||||
provider, err := c.GetProviderFromContext("SMS")
|
provider, err := c.GetProviderFromContext("SMS")
|
||||||
@@ -166,7 +176,7 @@ func (c *ApiController) SendSms() {
|
|||||||
// @Tag Service API
|
// @Tag Service API
|
||||||
// @Description This API is not for Casdoor frontend to call, it is for Casdoor SDKs.
|
// @Description This API is not for Casdoor frontend to call, it is for Casdoor SDKs.
|
||||||
// @Param from body controllers.NotificationForm true "Details of the notification request"
|
// @Param from body controllers.NotificationForm true "Details of the notification request"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /api/send-notification [post]
|
// @router /api/send-notification [post]
|
||||||
func (c *ApiController) SendNotification() {
|
func (c *ApiController) SendNotification() {
|
||||||
provider, err := c.GetProviderFromContext("Notification")
|
provider, err := c.GetProviderFromContext("Notification")
|
||||||
|
@@ -510,6 +510,7 @@ func (c *ApiController) SetPassword() {
|
|||||||
// @Title CheckUserPassword
|
// @Title CheckUserPassword
|
||||||
// @router /check-user-password [post]
|
// @router /check-user-password [post]
|
||||||
// @Tag User API
|
// @Tag User API
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) CheckUserPassword() {
|
func (c *ApiController) CheckUserPassword() {
|
||||||
var user object.User
|
var user object.User
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
||||||
@@ -580,6 +581,7 @@ func (c *ApiController) GetUserCount() {
|
|||||||
// @Title AddUserKeys
|
// @Title AddUserKeys
|
||||||
// @router /add-user-keys [post]
|
// @router /add-user-keys [post]
|
||||||
// @Tag User API
|
// @Tag User API
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) AddUserKeys() {
|
func (c *ApiController) AddUserKeys() {
|
||||||
var user object.User
|
var user object.User
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
||||||
|
@@ -39,6 +39,7 @@ const (
|
|||||||
// @Title SendVerificationCode
|
// @Title SendVerificationCode
|
||||||
// @Tag Verification API
|
// @Tag Verification API
|
||||||
// @router /send-verification-code [post]
|
// @router /send-verification-code [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) SendVerificationCode() {
|
func (c *ApiController) SendVerificationCode() {
|
||||||
var vform form.VerificationForm
|
var vform form.VerificationForm
|
||||||
err := c.ParseForm(&vform)
|
err := c.ParseForm(&vform)
|
||||||
@@ -229,6 +230,7 @@ func (c *ApiController) SendVerificationCode() {
|
|||||||
// @Title VerifyCaptcha
|
// @Title VerifyCaptcha
|
||||||
// @Tag Verification API
|
// @Tag Verification API
|
||||||
// @router /verify-captcha [post]
|
// @router /verify-captcha [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) VerifyCaptcha() {
|
func (c *ApiController) VerifyCaptcha() {
|
||||||
var vform form.VerificationForm
|
var vform form.VerificationForm
|
||||||
err := c.ParseForm(&vform)
|
err := c.ParseForm(&vform)
|
||||||
@@ -271,6 +273,7 @@ func (c *ApiController) VerifyCaptcha() {
|
|||||||
// @Tag Account API
|
// @Tag Account API
|
||||||
// @Title ResetEmailOrPhone
|
// @Title ResetEmailOrPhone
|
||||||
// @router /api/reset-email-or-phone [post]
|
// @router /api/reset-email-or-phone [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) ResetEmailOrPhone() {
|
func (c *ApiController) ResetEmailOrPhone() {
|
||||||
user, ok := c.RequireSignedInUser()
|
user, ok := c.RequireSignedInUser()
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -365,6 +368,7 @@ func (c *ApiController) ResetEmailOrPhone() {
|
|||||||
// @Tag Verification API
|
// @Tag Verification API
|
||||||
// @Title VerifyCode
|
// @Title VerifyCode
|
||||||
// @router /api/verify-code [post]
|
// @router /api/verify-code [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) VerifyCode() {
|
func (c *ApiController) VerifyCode() {
|
||||||
var authForm form.AuthForm
|
var authForm form.AuthForm
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &authForm)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &authForm)
|
||||||
|
@@ -146,7 +146,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WebAuthnSigninFinish
|
// WebAuthnSigninFinish
|
||||||
// @Title WebAuthnSigninBegin
|
// @Title WebAuthnSigninFinish
|
||||||
// @Tag Login API
|
// @Tag Login API
|
||||||
// @Description WebAuthn Login Flow 2nd stage
|
// @Description WebAuthn Login Flow 2nd stage
|
||||||
// @Param body body protocol.CredentialAssertionResponse true "authenticator assertion Response"
|
// @Param body body protocol.CredentialAssertionResponse true "authenticator assertion Response"
|
||||||
|
82
email/custom_http.go
Normal file
82
email/custom_http.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package email
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HttpEmailProvider struct {
|
||||||
|
endpoint string
|
||||||
|
method string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHttpEmailProvider(endpoint string, method string) *HttpEmailProvider {
|
||||||
|
client := &HttpEmailProvider{
|
||||||
|
endpoint: endpoint,
|
||||||
|
method: method,
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
|
||||||
|
var req *http.Request
|
||||||
|
var err error
|
||||||
|
if c.method == "POST" {
|
||||||
|
formValues := url.Values{}
|
||||||
|
formValues.Set("fromName", fromName)
|
||||||
|
formValues.Set("toAddress", toAddress)
|
||||||
|
formValues.Set("subject", subject)
|
||||||
|
formValues.Set("content", content)
|
||||||
|
req, err = http.NewRequest(c.method, c.endpoint, strings.NewReader(formValues.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
} else if c.method == "GET" {
|
||||||
|
req, err = http.NewRequest(c.method, c.endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("fromName", fromName)
|
||||||
|
q.Add("toAddress", toAddress)
|
||||||
|
q.Add("subject", subject)
|
||||||
|
q.Add("content", content)
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("HttpEmailProvider's Send() error, unsupported method: %s", c.method)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := proxy.DefaultHttpClient
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("HttpEmailProvider's Send() error, custom HTTP Email request failed with status: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
@@ -18,9 +18,11 @@ type EmailProvider interface {
|
|||||||
Send(fromAddress string, fromName, toAddress string, subject string, content string) error
|
Send(fromAddress string, fromName, toAddress string, subject string, content string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool) EmailProvider {
|
func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool, endpoint string, method string) EmailProvider {
|
||||||
if typ == "Azure ACS" {
|
if typ == "Azure ACS" {
|
||||||
return NewAzureACSEmailProvider(clientSecret, host)
|
return NewAzureACSEmailProvider(clientSecret, host)
|
||||||
|
} else if typ == "Custom HTTP Email" {
|
||||||
|
return NewHttpEmailProvider(endpoint, method)
|
||||||
} else {
|
} else {
|
||||||
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl)
|
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl)
|
||||||
}
|
}
|
||||||
|
19
go.mod
19
go.mod
@@ -4,16 +4,15 @@ go 1.16
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/squirrel v1.5.3
|
github.com/Masterminds/squirrel v1.5.3
|
||||||
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b
|
|
||||||
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
|
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
|
||||||
github.com/aws/aws-sdk-go v1.45.5
|
github.com/aws/aws-sdk-go v1.45.5
|
||||||
github.com/beego/beego v1.12.12
|
github.com/beego/beego v1.12.12
|
||||||
github.com/beevik/etree v1.1.0
|
github.com/beevik/etree v1.1.0
|
||||||
github.com/casbin/casbin/v2 v2.77.2
|
github.com/casbin/casbin/v2 v2.77.2
|
||||||
github.com/casdoor/go-sms-sender v0.17.0
|
github.com/casdoor/go-sms-sender v0.19.0
|
||||||
github.com/casdoor/gomail/v2 v2.0.1
|
github.com/casdoor/gomail/v2 v2.0.1
|
||||||
github.com/casdoor/notify v0.45.0
|
github.com/casdoor/notify v0.45.0
|
||||||
github.com/casdoor/oss v1.3.0
|
github.com/casdoor/oss v1.4.1
|
||||||
github.com/casdoor/xorm-adapter/v3 v3.1.0
|
github.com/casdoor/xorm-adapter/v3 v3.1.0
|
||||||
github.com/casvisor/casvisor-go-sdk v1.0.3
|
github.com/casvisor/casvisor-go-sdk v1.0.3
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
@@ -31,19 +30,17 @@ require (
|
|||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||||
github.com/go-webauthn/webauthn v0.6.0
|
github.com/go-webauthn/webauthn v0.6.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.4.0
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
|
||||||
github.com/lestrrat-go/jwx v1.2.21
|
github.com/lestrrat-go/jwx v1.2.21
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
||||||
github.com/markbates/goth v1.75.2
|
github.com/markbates/goth v1.75.2
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
|
||||||
github.com/nyaruka/phonenumbers v1.1.5
|
github.com/nyaruka/phonenumbers v1.1.5
|
||||||
github.com/pquerna/otp v1.4.0
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/prometheus/client_golang v1.11.1
|
github.com/prometheus/client_golang v1.11.1
|
||||||
github.com/prometheus/client_model v0.3.0
|
github.com/prometheus/client_model v0.4.0
|
||||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/russellhaering/gosaml2 v0.9.0
|
github.com/russellhaering/gosaml2 v0.9.0
|
||||||
@@ -62,10 +59,10 @@ require (
|
|||||||
github.com/xorm-io/core v0.7.4
|
github.com/xorm-io/core v0.7.4
|
||||||
github.com/xorm-io/xorm v1.1.6
|
github.com/xorm-io/xorm v1.1.6
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
golang.org/x/crypto v0.13.0
|
golang.org/x/crypto v0.14.0
|
||||||
golang.org/x/net v0.14.0
|
golang.org/x/net v0.17.0
|
||||||
golang.org/x/oauth2 v0.11.0
|
golang.org/x/oauth2 v0.13.0
|
||||||
google.golang.org/api v0.138.0
|
google.golang.org/api v0.150.0
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
layeh.com/radius v0.0.0-20221205141417-e7fbddd11d68
|
layeh.com/radius v0.0.0-20221205141417-e7fbddd11d68
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"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) 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 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 does not exist": "Die Anwendung: %s existiert nicht",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "Die Anmeldeart \"Anmeldung mit Passwort\" 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 provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert",
|
"The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert",
|
||||||
"Unauthorized operation": "Nicht autorisierte Operation",
|
"Unauthorized operation": "Nicht autorisierte Operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s und %s stimmen nicht überein"
|
"Service %s and %s do not match": "Service %s und %s stimmen nicht überein"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "E-Mail ist ungültig",
|
"Email is invalid": "E-Mail ist ungültig",
|
||||||
"Empty username.": "Leerer Benutzername.",
|
"Empty username.": "Leerer Benutzername.",
|
||||||
"FirstName cannot be blank": "Vorname darf nicht leer sein",
|
"FirstName cannot be blank": "Vorname darf nicht leer sein",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Ldap Benutzername oder Passwort falsch",
|
"LDAP user name or password incorrect": "Ldap Benutzername oder Passwort falsch",
|
||||||
"LastName cannot be blank": "Nachname darf nicht leer sein",
|
"LastName cannot be blank": "Nachname darf nicht leer sein",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Mehrere Konten mit derselben uid, bitte überprüfen Sie Ihren LDAP-Server",
|
"Multiple accounts with same uid, please check your ldap server": "Mehrere Konten mit derselben uid, bitte überprüfen Sie Ihren LDAP-Server",
|
||||||
"Organization does not exist": "Organisation existiert nicht",
|
"Organization does not exist": "Organisation existiert nicht",
|
||||||
"Password must have at least 6 characters": "Das Passwort muss mindestens 6 Zeichen enthalten",
|
|
||||||
"Phone already exists": "Telefon existiert bereits",
|
"Phone already exists": "Telefon existiert bereits",
|
||||||
"Phone cannot be empty": "Das Telefon darf nicht leer sein",
|
"Phone cannot be empty": "Das Telefon darf nicht leer sein",
|
||||||
"Phone number is invalid": "Die Telefonnummer ist ungültig",
|
"Phone number is invalid": "Die Telefonnummer ist ungültig",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"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) 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 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 does not exist": "La aplicación: %s no existe",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with 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 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 provider: %s is not enabled for the application": "El proveedor: %s no está habilitado para la aplicación",
|
"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",
|
"Unauthorized operation": "Operación no autorizada",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Los servicios %s y %s no coinciden"
|
"Service %s and %s do not match": "Los servicios %s y %s no coinciden"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "El correo electrónico no es válido",
|
"Email is invalid": "El correo electrónico no es válido",
|
||||||
"Empty username.": "Nombre de usuario vacío.",
|
"Empty username.": "Nombre de usuario vacío.",
|
||||||
"FirstName cannot be blank": "El nombre no puede estar en blanco",
|
"FirstName cannot be blank": "El nombre no puede estar en blanco",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Nombre de usuario o contraseña de Ldap incorrectos",
|
"LDAP user name or password incorrect": "Nombre de usuario o contraseña de Ldap incorrectos",
|
||||||
"LastName cannot be blank": "El apellido no puede estar en blanco",
|
"LastName cannot be blank": "El apellido no puede estar en blanco",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Cuentas múltiples con el mismo uid, por favor revise su servidor ldap",
|
"Multiple accounts with same uid, please check your ldap server": "Cuentas múltiples con el mismo uid, por favor revise su servidor ldap",
|
||||||
"Organization does not exist": "La organización no existe",
|
"Organization does not exist": "La organización no existe",
|
||||||
"Password must have at least 6 characters": "La contraseña debe tener al menos 6 caracteres",
|
|
||||||
"Phone already exists": "El teléfono ya existe",
|
"Phone already exists": "El teléfono ya existe",
|
||||||
"Phone cannot be empty": "Teléfono no puede estar vacío",
|
"Phone cannot be empty": "Teléfono no puede estar vacío",
|
||||||
"Phone number is invalid": "El número de teléfono no es válido",
|
"Phone number is invalid": "El número de teléfono no es válido",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"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) 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 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 does not exist": "L'application : %s n'existe pas",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with 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 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 provider: %s is not enabled for the application": "Le fournisseur :%s n'est pas activé pour l'application",
|
"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",
|
"Unauthorized operation": "Opération non autorisée",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "Le tag de l’utilisateur %s n’est pas répertorié dans les tags de l’application"
|
"User's tag: %s is not listed in the application's tags": "Le tag de l’utilisateur %s n’est pas répertorié dans les tags de l’application",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Les services %s et %s ne correspondent pas"
|
"Service %s and %s do not match": "Les services %s et %s ne correspondent pas"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "L'adresse e-mail est invalide",
|
"Email is invalid": "L'adresse e-mail est invalide",
|
||||||
"Empty username.": "Nom d'utilisateur vide.",
|
"Empty username.": "Nom d'utilisateur vide.",
|
||||||
"FirstName cannot be blank": "Le prénom ne peut pas être laissé vide",
|
"FirstName cannot be blank": "Le prénom ne peut pas être laissé vide",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Nom d'utilisateur ou mot de passe LDAP incorrect",
|
"LDAP user name or password incorrect": "Nom d'utilisateur ou mot de passe LDAP incorrect",
|
||||||
"LastName cannot be blank": "Le nom de famille ne peut pas être vide",
|
"LastName cannot be blank": "Le nom de famille ne peut pas être vide",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Plusieurs comptes avec le même identifiant d'utilisateur, veuillez vérifier votre serveur LDAP",
|
"Multiple accounts with same uid, please check your ldap server": "Plusieurs comptes avec le même identifiant d'utilisateur, veuillez vérifier votre serveur LDAP",
|
||||||
"Organization does not exist": "L'organisation n'existe pas",
|
"Organization does not exist": "L'organisation n'existe pas",
|
||||||
"Password must have at least 6 characters": "Le mot de passe doit comporter au moins 6 caractères",
|
|
||||||
"Phone already exists": "Le téléphone existe déjà",
|
"Phone already exists": "Le téléphone existe déjà",
|
||||||
"Phone cannot be empty": "Le téléphone ne peut pas être vide",
|
"Phone cannot be empty": "Le téléphone ne peut pas être vide",
|
||||||
"Phone number is invalid": "Le numéro de téléphone est invalide",
|
"Phone number is invalid": "Le numéro de téléphone est invalide",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"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) 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 provider: %s dan username: %s (%s) sudah terhubung dengan akun lain: %s (%s)",
|
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Akun untuk provider: %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 does not exist": "Aplikasi: %s tidak ada",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "Metode login: login dengan kata sandi tidak diaktifkan untuk aplikasi tersebut",
|
"The login method: login with password is not enabled for the application": "Metode login: login dengan kata sandi tidak diaktifkan untuk aplikasi tersebut",
|
||||||
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
|
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
|
||||||
"Unauthorized operation": "Operasi tidak sah",
|
"Unauthorized operation": "Operasi tidak sah",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok"
|
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email tidak valid",
|
"Email is invalid": "Email tidak valid",
|
||||||
"Empty username.": "Nama pengguna kosong.",
|
"Empty username.": "Nama pengguna kosong.",
|
||||||
"FirstName cannot be blank": "Nama depan tidak boleh kosong",
|
"FirstName cannot be blank": "Nama depan tidak boleh kosong",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Nama pengguna atau kata sandi Ldap salah",
|
"LDAP user name or password incorrect": "Nama pengguna atau kata sandi Ldap salah",
|
||||||
"LastName cannot be blank": "Nama belakang tidak boleh kosong",
|
"LastName cannot be blank": "Nama belakang tidak boleh kosong",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Beberapa akun dengan uid yang sama, harap periksa server ldap Anda",
|
"Multiple accounts with same uid, please check your ldap server": "Beberapa akun dengan uid yang sama, harap periksa server ldap Anda",
|
||||||
"Organization does not exist": "Organisasi tidak ada",
|
"Organization does not exist": "Organisasi tidak ada",
|
||||||
"Password must have at least 6 characters": "Kata sandi harus memiliki minimal 6 karakter",
|
|
||||||
"Phone already exists": "Telepon sudah ada",
|
"Phone already exists": "Telepon sudah ada",
|
||||||
"Phone cannot be empty": "Telepon tidak boleh kosong",
|
"Phone cannot be empty": "Telepon tidak boleh kosong",
|
||||||
"Phone number is invalid": "Nomor telepon tidak valid",
|
"Phone number is invalid": "Nomor telepon tidak valid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"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) 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 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 does not exist": "アプリケーション: %sは存在しません",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません",
|
"The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません",
|
||||||
"The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません",
|
"The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません",
|
||||||
"Unauthorized operation": "不正操作",
|
"Unauthorized operation": "不正操作",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s",
|
"Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "サービス%sと%sは一致しません"
|
"Service %s and %s do not match": "サービス%sと%sは一致しません"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "電子メールは無効です",
|
"Email is invalid": "電子メールは無効です",
|
||||||
"Empty username.": "空のユーザー名。",
|
"Empty username.": "空のユーザー名。",
|
||||||
"FirstName cannot be blank": "ファーストネームは空白にできません",
|
"FirstName cannot be blank": "ファーストネームは空白にできません",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Ldapのユーザー名またはパスワードが間違っています",
|
"LDAP user name or password incorrect": "Ldapのユーザー名またはパスワードが間違っています",
|
||||||
"LastName cannot be blank": "姓は空白にできません",
|
"LastName cannot be blank": "姓は空白にできません",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "同じuidを持つ複数のアカウントがあります。あなたのLDAPサーバーを確認してください",
|
"Multiple accounts with same uid, please check your ldap server": "同じuidを持つ複数のアカウントがあります。あなたのLDAPサーバーを確認してください",
|
||||||
"Organization does not exist": "組織は存在しません",
|
"Organization does not exist": "組織は存在しません",
|
||||||
"Password must have at least 6 characters": "パスワードは少なくとも6つの文字が必要です",
|
|
||||||
"Phone already exists": "電話はすでに存在しています",
|
"Phone already exists": "電話はすでに存在しています",
|
||||||
"Phone cannot be empty": "電話は空っぽにできません",
|
"Phone cannot be empty": "電話は空っぽにできません",
|
||||||
"Phone number is invalid": "電話番号が無効です",
|
"Phone number is invalid": "電話番号が無効です",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"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) 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 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 does not exist": "해당 애플리케이션(%s)이 존재하지 않습니다",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "어플리케이션에서는 암호를 사용한 로그인 방법이 활성화되어 있지 않습니다",
|
"The login method: login with password is not enabled for the application": "어플리케이션에서는 암호를 사용한 로그인 방법이 활성화되어 있지 않습니다",
|
||||||
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
|
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
|
||||||
"Unauthorized operation": "무단 조작",
|
"Unauthorized operation": "무단 조작",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s",
|
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"
|
"Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "이메일이 유효하지 않습니다",
|
"Email is invalid": "이메일이 유효하지 않습니다",
|
||||||
"Empty username.": "빈 사용자 이름.",
|
"Empty username.": "빈 사용자 이름.",
|
||||||
"FirstName cannot be blank": "이름은 공백일 수 없습니다",
|
"FirstName cannot be blank": "이름은 공백일 수 없습니다",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP 사용자 이름 또는 암호가 잘못되었습니다",
|
"LDAP user name or password incorrect": "LDAP 사용자 이름 또는 암호가 잘못되었습니다",
|
||||||
"LastName cannot be blank": "성은 비어 있을 수 없습니다",
|
"LastName cannot be blank": "성은 비어 있을 수 없습니다",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "동일한 UID를 가진 여러 계정이 있습니다. LDAP 서버를 확인해주세요",
|
"Multiple accounts with same uid, please check your ldap server": "동일한 UID를 가진 여러 계정이 있습니다. LDAP 서버를 확인해주세요",
|
||||||
"Organization does not exist": "조직은 존재하지 않습니다",
|
"Organization does not exist": "조직은 존재하지 않습니다",
|
||||||
"Password must have at least 6 characters": "암호는 적어도 6자 이상이어야 합니다",
|
|
||||||
"Phone already exists": "전화기는 이미 존재합니다",
|
"Phone already exists": "전화기는 이미 존재합니다",
|
||||||
"Phone cannot be empty": "전화는 비워 둘 수 없습니다",
|
"Phone cannot be empty": "전화는 비워 둘 수 없습니다",
|
||||||
"Phone number is invalid": "전화번호가 유효하지 않습니다",
|
"Phone number is invalid": "전화번호가 유효하지 않습니다",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"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) 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 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 does not exist": "Приложение: %s не существует",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",
|
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",
|
||||||
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
|
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
|
||||||
"Unauthorized operation": "Несанкционированная операция",
|
"Unauthorized operation": "Несанкционированная операция",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Сервисы %s и %s не совпадают"
|
"Service %s and %s do not match": "Сервисы %s и %s не совпадают"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Адрес электронной почты недействительный",
|
"Email is invalid": "Адрес электронной почты недействительный",
|
||||||
"Empty username.": "Пустое имя пользователя.",
|
"Empty username.": "Пустое имя пользователя.",
|
||||||
"FirstName cannot be blank": "Имя не может быть пустым",
|
"FirstName cannot be blank": "Имя не может быть пустым",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Неправильное имя пользователя или пароль Ldap",
|
"LDAP user name or password incorrect": "Неправильное имя пользователя или пароль Ldap",
|
||||||
"LastName cannot be blank": "Фамилия не может быть пустой",
|
"LastName cannot be blank": "Фамилия не может быть пустой",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Множественные учетные записи с тем же UID. Пожалуйста, проверьте свой сервер LDAP",
|
"Multiple accounts with same uid, please check your ldap server": "Множественные учетные записи с тем же UID. Пожалуйста, проверьте свой сервер LDAP",
|
||||||
"Organization does not exist": "Организация не существует",
|
"Organization does not exist": "Организация не существует",
|
||||||
"Password must have at least 6 characters": "Пароль должен содержать не менее 6 символов",
|
|
||||||
"Phone already exists": "Телефон уже существует",
|
"Phone already exists": "Телефон уже существует",
|
||||||
"Phone cannot be empty": "Телефон не может быть пустым",
|
"Phone cannot be empty": "Телефон не может быть пустым",
|
||||||
"Phone number is invalid": "Номер телефона является недействительным",
|
"Phone number is invalid": "Номер телефона является недействительным",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
"The 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 does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"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) 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 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 does not exist": "Ứng dụng: %s không tồn tại",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||||
|
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||||
"The login method: login with 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 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 provider: %s is not enabled for the application": "Nhà cung cấp: %s không được kích hoạt cho ứng dụng",
|
"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",
|
"Unauthorized operation": "Hoạt động không được ủy quyền",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Dịch sang tiếng Việt: Dịch vụ %s và %s không khớp"
|
"Service %s and %s do not match": "Dịch sang tiếng Việt: Dịch vụ %s và %s không khớp"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "Địa chỉ email không hợp lệ",
|
"Email is invalid": "Địa chỉ email không hợp lệ",
|
||||||
"Empty username.": "Tên đăng nhập trống.",
|
"Empty username.": "Tên đăng nhập trống.",
|
||||||
"FirstName cannot be blank": "Tên không được để trống",
|
"FirstName cannot be blank": "Tên không được để trống",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Tên người dùng hoặc mật khẩu Ldap không chính xác",
|
"LDAP user name or password incorrect": "Tên người dùng hoặc mật khẩu Ldap không chính xác",
|
||||||
"LastName cannot be blank": "Họ không thể để trống",
|
"LastName cannot be blank": "Họ không thể để trống",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Nhiều tài khoản với cùng một uid, vui lòng kiểm tra máy chủ ldap của bạn",
|
"Multiple accounts with same uid, please check your ldap server": "Nhiều tài khoản với cùng một uid, vui lòng kiểm tra máy chủ ldap của bạn",
|
||||||
"Organization does not exist": "Tổ chức không tồn tại",
|
"Organization does not exist": "Tổ chức không tồn tại",
|
||||||
"Password must have at least 6 characters": "Mật khẩu phải ít nhất 6 ký tự",
|
|
||||||
"Phone already exists": "Điện thoại đã tồn tại",
|
"Phone already exists": "Điện thoại đã tồn tại",
|
||||||
"Phone cannot be empty": "Điện thoại không thể để trống",
|
"Phone cannot be empty": "Điện thoại không thể để trống",
|
||||||
"Phone number is invalid": "Số điện thoại không hợp lệ",
|
"Phone number is invalid": "Số điện thoại không hợp lệ",
|
||||||
|
@@ -15,11 +15,14 @@
|
|||||||
"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) 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 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 does not exist": "应用%s不存在",
|
||||||
|
"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 password is not enabled for the application": "该应用禁止采用密码登录方式",
|
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",
|
||||||
"The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
|
"The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
|
||||||
"Unauthorized operation": "未授权的操作",
|
"Unauthorized operation": "未授权的操作",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s",
|
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s",
|
||||||
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中"
|
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "服务%s与%s不匹配"
|
"Service %s and %s do not match": "服务%s与%s不匹配"
|
||||||
@@ -33,11 +36,12 @@
|
|||||||
"Email is invalid": "无效邮箱",
|
"Email is invalid": "无效邮箱",
|
||||||
"Empty username.": "用户名不可为空",
|
"Empty username.": "用户名不可为空",
|
||||||
"FirstName cannot be blank": "名不可以为空",
|
"FirstName cannot be blank": "名不可以为空",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP密码错误",
|
"LDAP user name or password incorrect": "LDAP密码错误",
|
||||||
"LastName cannot be blank": "姓不可以为空",
|
"LastName cannot be blank": "姓不可以为空",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "多个帐户具有相同的uid,请检查您的 LDAP 服务器",
|
"Multiple accounts with same uid, please check your ldap server": "多个帐户具有相同的uid,请检查您的 LDAP 服务器",
|
||||||
"Organization does not exist": "组织不存在",
|
"Organization does not exist": "组织不存在",
|
||||||
"Password must have at least 6 characters": "新密码至少为6位",
|
|
||||||
"Phone already exists": "该手机号已存在",
|
"Phone already exists": "该手机号已存在",
|
||||||
"Phone cannot be empty": "手机号不可为空",
|
"Phone cannot be empty": "手机号不可为空",
|
||||||
"Phone number is invalid": "无效手机号",
|
"Phone number is invalid": "无效手机号",
|
||||||
|
@@ -85,10 +85,12 @@ func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
payload.Set("client_id", idp.Config.ClientID)
|
payload.Set("client_id", idp.Config.ClientID)
|
||||||
payload.Set("client_secret", idp.Config.ClientSecret)
|
payload.Set("client_secret", idp.Config.ClientSecret)
|
||||||
payload.Set("redirect_uri", idp.Config.RedirectURL)
|
payload.Set("redirect_uri", idp.Config.RedirectURL)
|
||||||
|
|
||||||
resp, err := idp.Client.PostForm(idp.Config.Endpoint.TokenURL, payload)
|
resp, err := idp.Client.PostForm(idp.Config.Endpoint.TokenURL, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := io.ReadAll(resp.Body)
|
data, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -97,10 +99,10 @@ func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
pToken := &AdfsToken{}
|
pToken := &AdfsToken{}
|
||||||
err = json.Unmarshal(data, pToken)
|
err = json.Unmarshal(data, pToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fail to unmarshal token response: %s", err.Error())
|
return nil, err
|
||||||
}
|
}
|
||||||
if pToken.ErrMsg != "" {
|
if pToken.ErrMsg != "" {
|
||||||
return nil, fmt.Errorf("pToken.Errmsg = %s", pToken.ErrMsg)
|
return nil, fmt.Errorf(pToken.ErrMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
token := &oauth2.Token{
|
token := &oauth2.Token{
|
||||||
|
126
idp/azuread_b2c.go
Normal file
126
idp/azuread_b2c.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package idp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AzureADB2CProvider struct {
|
||||||
|
Client *http.Client
|
||||||
|
Config *oauth2.Config
|
||||||
|
Tenant string
|
||||||
|
UserFlow string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAzureAdB2cProvider(clientId, clientSecret, redirectUrl, tenant string, userFlow string) *AzureADB2CProvider {
|
||||||
|
return &AzureADB2CProvider{
|
||||||
|
Config: &oauth2.Config{
|
||||||
|
ClientID: clientId,
|
||||||
|
ClientSecret: clientSecret,
|
||||||
|
RedirectURL: redirectUrl,
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: fmt.Sprintf("https://%s.b2clogin.com/%s.onmicrosoft.com/%s/oauth2/v2.0/authorize", tenant, tenant, userFlow),
|
||||||
|
TokenURL: fmt.Sprintf("https://%s.b2clogin.com/%s.onmicrosoft.com/%s/oauth2/v2.0/token", tenant, tenant, userFlow),
|
||||||
|
},
|
||||||
|
Scopes: []string{"openid", "email"},
|
||||||
|
},
|
||||||
|
Tenant: tenant,
|
||||||
|
UserFlow: userFlow,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AzureADB2CProvider) SetHttpClient(client *http.Client) {
|
||||||
|
p.Client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
type AzureadB2cToken struct {
|
||||||
|
IdToken string `json:"id_token"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
NotBefore int `json:"not_before"`
|
||||||
|
IdTokenExpiresIn int `json:"id_token_expires_in"`
|
||||||
|
ProfileInfo string `json:"profile_info"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AzureADB2CProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||||
|
payload := url.Values{}
|
||||||
|
payload.Set("code", code)
|
||||||
|
payload.Set("grant_type", "authorization_code")
|
||||||
|
payload.Set("client_id", p.Config.ClientID)
|
||||||
|
payload.Set("client_secret", p.Config.ClientSecret)
|
||||||
|
payload.Set("redirect_uri", p.Config.RedirectURL)
|
||||||
|
|
||||||
|
resp, err := p.Client.PostForm(p.Config.Endpoint.TokenURL, payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pToken := &AzureadB2cToken{}
|
||||||
|
err = json.Unmarshal(data, pToken)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
token := &oauth2.Token{
|
||||||
|
AccessToken: pToken.IdToken,
|
||||||
|
Expiry: time.Unix(time.Now().Unix()+int64(pToken.IdTokenExpiresIn), 0),
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AzureADB2CProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||||
|
userInfoEndpoint := fmt.Sprintf("https://%s.b2clogin.com/%s.onmicrosoft.com/%s/openid/v2.0/userinfo", p.Tenant, p.Tenant, p.UserFlow)
|
||||||
|
req, err := http.NewRequest("GET", userInfoEndpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Add("Authorization", "Bearer "+token.AccessToken)
|
||||||
|
|
||||||
|
resp, err := p.Client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("error fetching user info: status code %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyBytes, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var userInfo UserInfo
|
||||||
|
err = json.Unmarshal(bodyBytes, &userInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userInfo, nil
|
||||||
|
}
|
@@ -91,6 +91,8 @@ func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) (IdProvider, error
|
|||||||
return NewGitlabIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
return NewGitlabIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||||
case "ADFS":
|
case "ADFS":
|
||||||
return NewAdfsIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl), nil
|
return NewAdfsIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl), nil
|
||||||
|
case "AzureADB2C":
|
||||||
|
return NewAzureAdB2cProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl, idpInfo.AppId), nil
|
||||||
case "Baidu":
|
case "Baidu":
|
||||||
return NewBaiduIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
return NewBaiduIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||||
case "Alipay":
|
case "Alipay":
|
||||||
|
@@ -45,6 +45,23 @@
|
|||||||
"alertType": "None"
|
"alertType": "None"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"signinMethods": [
|
||||||
|
{
|
||||||
|
"name": "Password",
|
||||||
|
"displayName": "Password",
|
||||||
|
"rule": "None",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Verification code",
|
||||||
|
"displayName": "Verification code",
|
||||||
|
"rule": "All",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WebAuthn",
|
||||||
|
"displayName": "WebAuthn",
|
||||||
|
"rule": "None",
|
||||||
|
},
|
||||||
|
],
|
||||||
"signupItems": [
|
"signupItems": [
|
||||||
{
|
{
|
||||||
"name": "ID",
|
"name": "ID",
|
||||||
@@ -104,7 +121,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"redirectUris": [""],
|
"redirectUris": [""],
|
||||||
"expireInHours": 168
|
"expireInHours": 168,
|
||||||
|
"failedSigninLimit": 5,
|
||||||
|
"failedSigninfrozenTime": 15
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"users": [
|
"users": [
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
name: casdoor
|
name: casdoor-helm-charts
|
||||||
description: A Helm chart for Kubernetes
|
description: A Helm chart for Kubernetes
|
||||||
|
|
||||||
# A chart can be either an 'application' or a 'library' chart.
|
# A chart can be either an 'application' or a 'library' chart.
|
||||||
@@ -15,10 +15,10 @@ type: application
|
|||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# This is the chart version. This version number should be incremented each time you make changes
|
||||||
# to the chart and its templates, including the app version.
|
# to the chart and its templates, including the app version.
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||||
version: 0.2.0
|
version: 0.3.0
|
||||||
|
|
||||||
# This is the version number of the application being deployed. This version number should be
|
# This is the version number of the application being deployed. This version number should be
|
||||||
# incremented each time you make changes to the application. Versions are not expected to
|
# incremented each time you make changes to the application. Versions are not expected to
|
||||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||||
# It is recommended to use it with quotes.
|
# It is recommended to use it with quotes.
|
||||||
appVersion: "1.17.0"
|
appVersion: "1.18.0"
|
||||||
|
@@ -15,10 +15,11 @@
|
|||||||
package notification
|
package notification
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/proxy"
|
"github.com/casdoor/casdoor/proxy"
|
||||||
)
|
)
|
||||||
@@ -39,26 +40,29 @@ func NewCustomHttpProvider(endpoint string, method string, paramName string) (*H
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *HttpNotificationClient) Send(ctx context.Context, subject string, content string) error {
|
func (c *HttpNotificationClient) Send(ctx context.Context, subject string, content string) error {
|
||||||
|
var req *http.Request
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
httpClient := proxy.DefaultHttpClient
|
|
||||||
|
|
||||||
req, err := http.NewRequest(c.method, c.endpoint, bytes.NewBufferString(content))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.method == "POST" {
|
if c.method == "POST" {
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
formValues := url.Values{}
|
||||||
req.PostForm = map[string][]string{
|
formValues.Set(c.paramName, content)
|
||||||
c.paramName: {content},
|
req, err = http.NewRequest(c.method, c.endpoint, strings.NewReader(formValues.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
} else if c.method == "GET" {
|
} else if c.method == "GET" {
|
||||||
|
req, err = http.NewRequest(c.method, c.endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
q := req.URL.Query()
|
q := req.URL.Query()
|
||||||
q.Add(c.paramName, content)
|
q.Add(c.paramName, content)
|
||||||
req.URL.RawQuery = q.Encode()
|
req.URL.RawQuery = q.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
httpClient := proxy.DefaultHttpClient
|
||||||
resp, err := httpClient.Do(req)
|
resp, err := httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -66,7 +70,7 @@ func (c *HttpNotificationClient) Send(ctx context.Context, subject string, conte
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return fmt.Errorf("SendMessage() error, custom HTTP Notification request failed with status: %s", resp.Status)
|
return fmt.Errorf("HttpNotificationClient's SendMessage() error, custom HTTP Notification request failed with status: %s", resp.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@@ -24,6 +24,12 @@ import (
|
|||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SigninMethod struct {
|
||||||
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
Rule string `json:"rule"`
|
||||||
|
}
|
||||||
|
|
||||||
type SignupItem struct {
|
type SignupItem struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Visible bool `json:"visible"`
|
Visible bool `json:"visible"`
|
||||||
@@ -63,6 +69,7 @@ type Application struct {
|
|||||||
OrgChoiceMode string `json:"orgChoiceMode"`
|
OrgChoiceMode string `json:"orgChoiceMode"`
|
||||||
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
|
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
|
||||||
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
|
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
|
||||||
|
SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"`
|
||||||
SignupItems []*SignupItem `xorm:"varchar(2000)" json:"signupItems"`
|
SignupItems []*SignupItem `xorm:"varchar(2000)" json:"signupItems"`
|
||||||
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
|
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
|
||||||
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
|
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
|
||||||
@@ -90,6 +97,9 @@ type Application struct {
|
|||||||
FormOffset int `json:"formOffset"`
|
FormOffset int `json:"formOffset"`
|
||||||
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
|
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
|
||||||
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
|
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
|
||||||
|
|
||||||
|
FailedSigninLimit int `json:"failedSigninLimit"`
|
||||||
|
FailedSigninfrozenTime int `json:"failedSigninfrozenTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApplicationCount(owner, field, value string) (int64, error) {
|
func GetApplicationCount(owner, field, value string) (int64, error) {
|
||||||
@@ -188,6 +198,25 @@ func extendApplicationWithOrg(application *Application) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extendApplicationWithSigninMethods(application *Application) (err error) {
|
||||||
|
if len(application.SigninMethods) == 0 {
|
||||||
|
if application.EnablePassword {
|
||||||
|
signinMethod := &SigninMethod{Name: "Password", DisplayName: "Password", Rule: "None"}
|
||||||
|
application.SigninMethods = append(application.SigninMethods, signinMethod)
|
||||||
|
}
|
||||||
|
if application.EnableCodeSignin {
|
||||||
|
signinMethod := &SigninMethod{Name: "Verification code", DisplayName: "Verification code", Rule: "All"}
|
||||||
|
application.SigninMethods = append(application.SigninMethods, signinMethod)
|
||||||
|
}
|
||||||
|
if application.EnableWebAuthn {
|
||||||
|
signinMethod := &SigninMethod{Name: "WebAuthn", DisplayName: "WebAuthn", Rule: "None"}
|
||||||
|
application.SigninMethods = append(application.SigninMethods, signinMethod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func getApplication(owner string, name string) (*Application, error) {
|
func getApplication(owner string, name string) (*Application, error) {
|
||||||
if owner == "" || name == "" {
|
if owner == "" || name == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -210,6 +239,11 @@ func getApplication(owner string, name string) (*Application, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = extendApplicationWithSigninMethods(&application)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &application, nil
|
return &application, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -234,6 +268,11 @@ func GetApplicationByOrganizationName(organization string) (*Application, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = extendApplicationWithSigninMethods(&application)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &application, nil
|
return &application, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -281,6 +320,11 @@ func GetApplicationByClientId(clientId string) (*Application, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = extendApplicationWithSigninMethods(&application)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &application, nil
|
return &application, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -482,6 +526,45 @@ func (application *Application) IsRedirectUriValid(redirectUri string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (application *Application) IsPasswordEnabled() bool {
|
||||||
|
if len(application.SigninMethods) == 0 {
|
||||||
|
return application.EnablePassword
|
||||||
|
} else {
|
||||||
|
for _, signinMethod := range application.SigninMethods {
|
||||||
|
if signinMethod.Name == "Password" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (application *Application) IsCodeSigninViaEmailEnabled() bool {
|
||||||
|
if len(application.SigninMethods) == 0 {
|
||||||
|
return application.EnableCodeSignin
|
||||||
|
} else {
|
||||||
|
for _, signinMethod := range application.SigninMethods {
|
||||||
|
if signinMethod.Name == "Verification code" && signinMethod.Rule != "Phone only" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (application *Application) IsCodeSigninViaSmsEnabled() bool {
|
||||||
|
if len(application.SigninMethods) == 0 {
|
||||||
|
return application.EnableCodeSignin
|
||||||
|
} else {
|
||||||
|
for _, signinMethod := range application.SigninMethods {
|
||||||
|
if signinMethod.Name == "Verification code" && signinMethod.Rule != "Email only" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func IsOriginAllowed(origin string) (bool, error) {
|
func IsOriginAllowed(origin string) (bool, error) {
|
||||||
applications, err := GetApplications("")
|
applications, err := GetApplications("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -28,8 +28,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SigninWrongTimesLimit = 5
|
DefaultFailedSigninLimit = 5
|
||||||
LastSignWrongTimeDuration = time.Minute * 15
|
// DefaultFailedSigninfrozenTime The unit of frozen time is minutes
|
||||||
|
DefaultFailedSigninfrozenTime = 15
|
||||||
)
|
)
|
||||||
|
|
||||||
func CheckUserSignup(application *Application, organization *Organization, form *form.AuthForm, lang string) string {
|
func CheckUserSignup(application *Application, organization *Organization, form *form.AuthForm, lang string) string {
|
||||||
@@ -143,10 +144,15 @@ func CheckUserSignup(application *Application, organization *Organization, form
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkSigninErrorTimes(user *User, lang string) error {
|
func checkSigninErrorTimes(user *User, lang string) error {
|
||||||
if user.SigninWrongTimes >= SigninWrongTimesLimit {
|
failedSigninLimit, failedSigninfrozenTime, err := GetFailedSigninConfigByUser(user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.SigninWrongTimes >= failedSigninLimit {
|
||||||
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
|
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
|
||||||
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
|
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
|
||||||
minutes := int(LastSignWrongTimeDuration.Minutes() - passedTime.Minutes())
|
minutes := failedSigninfrozenTime - int(passedTime.Minutes())
|
||||||
|
|
||||||
// deny the login if the error times is greater than the limit and the last login time is less than the duration
|
// deny the login if the error times is greater than the limit and the last login time is less than the duration
|
||||||
if minutes > 0 {
|
if minutes > 0 {
|
||||||
@@ -479,7 +485,14 @@ func CheckToEnableCaptcha(application *Application, organization, username strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return user != nil && user.SigninWrongTimes >= SigninWrongTimesLimit, nil
|
|
||||||
|
var failedSigninLimit int
|
||||||
|
if application.FailedSigninLimit == 0 {
|
||||||
|
failedSigninLimit = 5
|
||||||
|
} else {
|
||||||
|
failedSigninLimit = application.FailedSigninLimit
|
||||||
|
}
|
||||||
|
return user != nil && user.SigninWrongTimes >= failedSigninLimit, nil
|
||||||
}
|
}
|
||||||
return providerItem.Rule == "Always", nil
|
return providerItem.Rule == "Always", nil
|
||||||
}
|
}
|
||||||
|
@@ -47,18 +47,42 @@ func resetUserSigninErrorTimes(user *User) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetFailedSigninConfigByUser(user *User) (int, int, error) {
|
||||||
|
application, err := GetApplicationByUser(user)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
failedSigninLimit := application.FailedSigninLimit
|
||||||
|
failedSigninfrozenTime := application.FailedSigninfrozenTime
|
||||||
|
|
||||||
|
// 0 as an initialization value, corresponding to the default configuration parameters
|
||||||
|
if failedSigninLimit == 0 {
|
||||||
|
failedSigninLimit = DefaultFailedSigninLimit
|
||||||
|
}
|
||||||
|
if failedSigninfrozenTime == 0 {
|
||||||
|
failedSigninfrozenTime = DefaultFailedSigninfrozenTime
|
||||||
|
}
|
||||||
|
|
||||||
|
return failedSigninLimit, failedSigninfrozenTime, nil
|
||||||
|
}
|
||||||
|
|
||||||
func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
|
func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
|
||||||
enableCaptcha := false
|
enableCaptcha := false
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
enableCaptcha = options[0]
|
enableCaptcha = options[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
failedSigninLimit, failedSigninfrozenTime, errSignin := GetFailedSigninConfigByUser(user)
|
||||||
|
if errSignin != nil {
|
||||||
|
return errSignin
|
||||||
|
}
|
||||||
|
|
||||||
// increase failed login count
|
// increase failed login count
|
||||||
if user.SigninWrongTimes < SigninWrongTimesLimit {
|
if user.SigninWrongTimes < failedSigninLimit {
|
||||||
user.SigninWrongTimes++
|
user.SigninWrongTimes++
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.SigninWrongTimes >= SigninWrongTimesLimit {
|
if user.SigninWrongTimes >= failedSigninLimit {
|
||||||
// record the latest failed login time
|
// record the latest failed login time
|
||||||
user.LastSigninWrongTime = time.Now().UTC().Format(time.RFC3339)
|
user.LastSigninWrongTime = time.Now().UTC().Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
@@ -69,7 +93,7 @@ func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
|
leftChances := failedSigninLimit - user.SigninWrongTimes
|
||||||
if leftChances == 0 && enableCaptcha {
|
if leftChances == 0 && enableCaptcha {
|
||||||
return fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect"))
|
return fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect"))
|
||||||
} else if leftChances >= 0 {
|
} else if leftChances >= 0 {
|
||||||
@@ -77,5 +101,5 @@ func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// don't show the chance error message if the user has no chance left
|
// don't show the chance error message if the user has no chance left
|
||||||
return fmt.Errorf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), int(LastSignWrongTimeDuration.Minutes()))
|
return fmt.Errorf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), failedSigninfrozenTime)
|
||||||
}
|
}
|
||||||
|
@@ -36,7 +36,7 @@ func getDialer(provider *Provider) *gomail.Dialer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SendEmail(provider *Provider, title string, content string, dest string, sender string) error {
|
func SendEmail(provider *Provider, title string, content string, dest string, sender string) error {
|
||||||
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl)
|
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl, provider.Endpoint, provider.Method)
|
||||||
|
|
||||||
fromAddress := provider.ClientId2
|
fromAddress := provider.ClientId2
|
||||||
if fromAddress == "" {
|
if fromAddress == "" {
|
||||||
|
@@ -125,6 +125,10 @@ func (enforcer *Enforcer) GetId() string {
|
|||||||
return fmt.Sprintf("%s/%s", enforcer.Owner, enforcer.Name)
|
return fmt.Sprintf("%s/%s", enforcer.Owner, enforcer.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (enforcer *Enforcer) GetModelAndAdapter() string {
|
||||||
|
return util.GetId(enforcer.Model, enforcer.Adapter)
|
||||||
|
}
|
||||||
|
|
||||||
func (enforcer *Enforcer) InitEnforcer() error {
|
func (enforcer *Enforcer) InitEnforcer() error {
|
||||||
if enforcer.Enforcer != nil {
|
if enforcer.Enforcer != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@@ -18,6 +18,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/conf"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/xorm-io/builder"
|
"github.com/xorm-io/builder"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
@@ -224,7 +225,8 @@ func GetGroupUserCount(groupId string, field, value string) (int64, error) {
|
|||||||
if field == "" && value == "" {
|
if field == "" && value == "" {
|
||||||
return int64(len(names)), nil
|
return int64(len(names)), nil
|
||||||
} else {
|
} else {
|
||||||
return ormer.Engine.Table("user").
|
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||||
|
return ormer.Engine.Table(tableNamePrefix+"user").
|
||||||
Where("owner = ?", owner).In("name", names).
|
Where("owner = ?", owner).In("name", names).
|
||||||
And(fmt.Sprintf("user.%s like ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
And(fmt.Sprintf("user.%s like ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
||||||
Count()
|
Count()
|
||||||
@@ -239,7 +241,8 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
session := ormer.Engine.Table("user").
|
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||||
|
session := ormer.Engine.Table(tableNamePrefix+"user").
|
||||||
Where("owner = ?", owner).In("name", names)
|
Where("owner = ?", owner).In("name", names)
|
||||||
|
|
||||||
if offset != -1 && limit != -1 {
|
if offset != -1 && limit != -1 {
|
||||||
@@ -271,7 +274,9 @@ func GetGroupUsers(groupId string) ([]*User, error) {
|
|||||||
users := []*User{}
|
users := []*User{}
|
||||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
err = ormer.Engine.Where("owner = ?", owner).In("name", names).Find(&users)
|
err = ormer.Engine.Where("owner = ?", owner).In("name", names).Find(&users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -303,6 +308,9 @@ func GroupChangeTrigger(oldName, newName string) error {
|
|||||||
|
|
||||||
groups := []*Group{}
|
groups := []*Group{}
|
||||||
err = session.Where("parent_id = ?", oldName).Find(&groups)
|
err = session.Where("parent_id = ?", oldName).Find(&groups)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
group.ParentId = newName
|
group.ParentId = newName
|
||||||
_, err := session.ID(core.PK{group.Owner, group.Name}).Cols("parent_id").Update(group)
|
_, err := session.ID(core.PK{group.Owner, group.Name}).Cols("parent_id").Update(group)
|
||||||
|
@@ -180,6 +180,11 @@ func initBuiltInApplication() {
|
|||||||
Providers: []*ProviderItem{
|
Providers: []*ProviderItem{
|
||||||
{Name: "provider_captcha_default", CanSignUp: false, CanSignIn: false, CanUnlink: false, Prompted: false, SignupGroup: "", Rule: "None", Provider: nil},
|
{Name: "provider_captcha_default", CanSignUp: false, CanSignIn: false, CanUnlink: false, Prompted: false, SignupGroup: "", Rule: "None", Provider: nil},
|
||||||
},
|
},
|
||||||
|
SigninMethods: []*SigninMethod{
|
||||||
|
{Name: "Password", DisplayName: "Password", Rule: "None"},
|
||||||
|
{Name: "Verification code", DisplayName: "Verification code", Rule: "All"},
|
||||||
|
{Name: "WebAuthn", DisplayName: "WebAuthn", Rule: "None"},
|
||||||
|
},
|
||||||
SignupItems: []*SignupItem{
|
SignupItems: []*SignupItem{
|
||||||
{Name: "ID", Visible: false, Required: true, Prompted: false, Rule: "Random"},
|
{Name: "ID", Visible: false, Required: true, Prompted: false, Rule: "Random"},
|
||||||
{Name: "Username", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
{Name: "Username", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||||
|
@@ -136,6 +136,9 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
|
|||||||
if application.Providers == nil {
|
if application.Providers == nil {
|
||||||
application.Providers = []*ProviderItem{}
|
application.Providers = []*ProviderItem{}
|
||||||
}
|
}
|
||||||
|
if application.SigninMethods == nil {
|
||||||
|
application.SigninMethods = []*SigninMethod{}
|
||||||
|
}
|
||||||
if application.SignupItems == nil {
|
if application.SignupItems == nil {
|
||||||
application.SignupItems = []*SignupItem{}
|
application.SignupItems = []*SignupItem{}
|
||||||
}
|
}
|
||||||
|
134
object/invitation.go
Normal file
134
object/invitation.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
"github.com/xorm-io/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Invitation 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"`
|
||||||
|
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||||
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
|
||||||
|
Code string `xorm:"varchar(100)" json:"code"`
|
||||||
|
Quota int `json:"quota"`
|
||||||
|
UsedCount int `json:"usedCount"`
|
||||||
|
|
||||||
|
Application string `xorm:"varchar(100)" json:"application"`
|
||||||
|
Username string `xorm:"varchar(100)" json:"username"`
|
||||||
|
Email string `xorm:"varchar(100)" json:"email"`
|
||||||
|
Phone string `xorm:"varchar(100)" json:"phone"`
|
||||||
|
|
||||||
|
SignupGroup string `xorm:"varchar(100)" json:"signupGroup"`
|
||||||
|
|
||||||
|
State string `xorm:"varchar(100)" json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInvitationCount(owner, field, value string) (int64, error) {
|
||||||
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
|
return session.Count(&Invitation{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInvitations(owner string) ([]*Invitation, error) {
|
||||||
|
invitations := []*Invitation{}
|
||||||
|
err := ormer.Engine.Desc("created_time").Find(&invitations, &Invitation{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
return invitations, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invitations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaginationInvitations(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Invitation, error) {
|
||||||
|
invitations := []*Invitation{}
|
||||||
|
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
|
err := session.Find(&invitations)
|
||||||
|
if err != nil {
|
||||||
|
return invitations, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return invitations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInvitation(owner string, name string) (*Invitation, error) {
|
||||||
|
if owner == "" || name == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
invitation := Invitation{Owner: owner, Name: name}
|
||||||
|
existed, err := ormer.Engine.Get(&invitation)
|
||||||
|
if err != nil {
|
||||||
|
return &invitation, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if existed {
|
||||||
|
return &invitation, nil
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInvitation(id string) (*Invitation, error) {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
return getInvitation(owner, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateInvitation(id string, invitation *Invitation) (bool, error) {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
if p, err := getInvitation(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(invitation)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddInvitation(invitation *Invitation) (bool, error) {
|
||||||
|
affected, err := ormer.Engine.Insert(invitation)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteInvitation(invitation *Invitation) (bool, error) {
|
||||||
|
affected, err := ormer.Engine.ID(core.PK{invitation.Owner, invitation.Name}).Delete(&Invitation{})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (invitation *Invitation) GetId() string {
|
||||||
|
return fmt.Sprintf("%s/%s", invitation.Owner, invitation.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifyInvitation(id string) (payment *Payment, attachInfo map[string]interface{}, err error) {
|
||||||
|
return nil, nil, fmt.Errorf("the invitation: %s does not exist", id)
|
||||||
|
}
|
@@ -19,6 +19,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/conf"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
goldap "github.com/go-ldap/ldap/v3"
|
goldap "github.com/go-ldap/ldap/v3"
|
||||||
"github.com/thanhpk/randstr"
|
"github.com/thanhpk/randstr"
|
||||||
@@ -356,7 +357,8 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
|
|||||||
func GetExistUuids(owner string, uuids []string) ([]string, error) {
|
func GetExistUuids(owner string, uuids []string) ([]string, error) {
|
||||||
var existUuids []string
|
var existUuids []string
|
||||||
|
|
||||||
err := ormer.Engine.Table("user").Where("owner = ?", owner).Cols("ldap").
|
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||||
|
err := ormer.Engine.Table(tableNamePrefix+"user").Where("owner = ?", owner).Cols("ldap").
|
||||||
In("ldap", uuids).Select("DISTINCT ldap").Find(&existUuids)
|
In("ldap", uuids).Select("DISTINCT ldap").Find(&existUuids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return existUuids, err
|
return existUuids, err
|
||||||
|
109
object/ormer.go
109
object/ormer.go
@@ -234,12 +234,37 @@ func (a *Ormer) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Group))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(User))
|
err = a.Engine.Sync2(new(User))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Group))
|
err = a.Engine.Sync2(new(Invitation))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Application))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Provider))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Resource))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Cert))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -269,17 +294,7 @@ func (a *Ormer) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Provider))
|
err = a.Engine.Sync2(new(Session))
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Application))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Resource))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -289,26 +304,6 @@ func (a *Ormer) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(VerificationRecord))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Webhook))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Syncer))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Cert))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Product))
|
err = a.Engine.Sync2(new(Product))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -319,6 +314,36 @@ func (a *Ormer) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Plan))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Pricing))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Subscription))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Syncer))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Webhook))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(VerificationRecord))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Ldap))
|
err = a.Engine.Sync2(new(Ldap))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -333,24 +358,4 @@ func (a *Ormer) createTable() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Session))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Subscription))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Plan))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Pricing))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -446,9 +446,8 @@ func GetMaskedPermissions(permissions []*Permission) []*Permission {
|
|||||||
// as the policyFilter when the enforcer load policy).
|
// as the policyFilter when the enforcer load policy).
|
||||||
func GroupPermissionsByModelAdapter(permissions []*Permission) map[string][]string {
|
func GroupPermissionsByModelAdapter(permissions []*Permission) map[string][]string {
|
||||||
m := make(map[string][]string)
|
m := make(map[string][]string)
|
||||||
|
|
||||||
for _, permission := range permissions {
|
for _, permission := range permissions {
|
||||||
key := permission.Model + permission.Adapter
|
key := permission.GetModelAndAdapter()
|
||||||
permissionIds, ok := m[key]
|
permissionIds, ok := m[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
m[key] = []string{permission.GetId()}
|
m[key] = []string{permission.GetId()}
|
||||||
@@ -464,9 +463,17 @@ func (p *Permission) GetId() string {
|
|||||||
return util.GetId(p.Owner, p.Name)
|
return util.GetId(p.Owner, p.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Permission) GetModelAndAdapter() string {
|
||||||
|
return util.GetId(p.Model, p.Adapter)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Permission) isUserHit(name string) bool {
|
func (p *Permission) isUserHit(name string) bool {
|
||||||
targetOrg, targetName := util.GetOwnerAndNameFromId(name)
|
targetOrg, targetName := util.GetOwnerAndNameFromId(name)
|
||||||
for _, user := range p.Users {
|
for _, user := range p.Users {
|
||||||
|
if user == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
userOrg, userName := util.GetOwnerAndNameFromId(user)
|
userOrg, userName := util.GetOwnerAndNameFromId(user)
|
||||||
if userOrg == targetOrg && (userName == "*" || userName == targetName) {
|
if userOrg == targetOrg && (userName == "*" || userName == targetName) {
|
||||||
return true
|
return true
|
||||||
@@ -480,9 +487,14 @@ func (p *Permission) isRoleHit(userId string) bool {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, role := range p.Roles {
|
for _, role := range p.Roles {
|
||||||
|
if role == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
for _, targetRole := range targetRoles {
|
for _, targetRole := range targetRoles {
|
||||||
if targetRole.GetId() == role {
|
if role == targetRole.GetId() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -284,24 +284,28 @@ func removeGroupingPolicies(permission *Permission) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CasbinRequest = []interface{}
|
func Enforce(permission *Permission, request []string, permissionIds ...string) (bool, error) {
|
||||||
|
|
||||||
func Enforce(permission *Permission, request *CasbinRequest, permissionIds ...string) (bool, error) {
|
|
||||||
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return enforcer.Enforce(*request...)
|
// type transformation
|
||||||
|
interfaceRequest := util.StringToInterfaceArray(request)
|
||||||
|
|
||||||
|
return enforcer.Enforce(interfaceRequest...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BatchEnforce(permission *Permission, requests *[]CasbinRequest, permissionIds ...string) ([]bool, error) {
|
func BatchEnforce(permission *Permission, requests [][]string, permissionIds ...string) ([]bool, error) {
|
||||||
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return enforcer.BatchEnforce(*requests)
|
// type transformation
|
||||||
|
interfaceRequests := util.StringToInterfaceArray2d(requests)
|
||||||
|
|
||||||
|
return enforcer.BatchEnforce(interfaceRequests)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) ([]string, error) {
|
func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) ([]string, error) {
|
||||||
|
@@ -37,7 +37,7 @@ type Provider struct {
|
|||||||
SubType string `xorm:"varchar(100)" json:"subType"`
|
SubType string `xorm:"varchar(100)" json:"subType"`
|
||||||
Method string `xorm:"varchar(100)" json:"method"`
|
Method string `xorm:"varchar(100)" json:"method"`
|
||||||
ClientId string `xorm:"varchar(200)" json:"clientId"`
|
ClientId string `xorm:"varchar(200)" json:"clientId"`
|
||||||
ClientSecret string `xorm:"varchar(2000)" json:"clientSecret"`
|
ClientSecret string `xorm:"varchar(3000)" json:"clientSecret"`
|
||||||
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
|
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
|
||||||
ClientSecret2 string `xorm:"varchar(500)" json:"clientSecret2"`
|
ClientSecret2 string `xorm:"varchar(500)" json:"clientSecret2"`
|
||||||
Cert string `xorm:"varchar(100)" json:"cert"`
|
Cert string `xorm:"varchar(100)" json:"cert"`
|
||||||
@@ -52,7 +52,7 @@ type Provider struct {
|
|||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
DisableSsl bool `json:"disableSsl"` // If the provider type is WeChat, DisableSsl means EnableQRCode
|
DisableSsl bool `json:"disableSsl"` // If the provider type is WeChat, DisableSsl means EnableQRCode
|
||||||
Title string `xorm:"varchar(100)" json:"title"`
|
Title string `xorm:"varchar(100)" json:"title"`
|
||||||
Content string `xorm:"varchar(1000)" json:"content"` // If provider type is WeChat, Content means QRCode string by Base64 encoding
|
Content string `xorm:"varchar(2000)" json:"content"` // If provider type is WeChat, Content means QRCode string by Base64 encoding
|
||||||
Receiver string `xorm:"varchar(100)" json:"receiver"`
|
Receiver string `xorm:"varchar(100)" json:"receiver"`
|
||||||
|
|
||||||
RegionId string `xorm:"varchar(100)" json:"regionId"`
|
RegionId string `xorm:"varchar(100)" json:"regionId"`
|
||||||
@@ -417,7 +417,7 @@ func FromProviderToIdpInfo(ctx *context.Context, provider *Provider) *idp.Provid
|
|||||||
providerInfo.ClientId = provider.ClientId2
|
providerInfo.ClientId = provider.ClientId2
|
||||||
providerInfo.ClientSecret = provider.ClientSecret2
|
providerInfo.ClientSecret = provider.ClientSecret2
|
||||||
}
|
}
|
||||||
} else if provider.Type == "AzureAD" || provider.Type == "ADFS" || provider.Type == "Okta" {
|
} else if provider.Type == "AzureAD" || provider.Type == "AzureADB2C" || provider.Type == "ADFS" || provider.Type == "Okta" {
|
||||||
providerInfo.HostUrl = provider.Domain
|
providerInfo.HostUrl = provider.Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -271,6 +271,9 @@ func getRolesByUserInternal(userId string) ([]*Role, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return roles, err
|
return roles, err
|
||||||
}
|
}
|
||||||
|
if user == nil {
|
||||||
|
return nil, fmt.Errorf("The user: %s doesn't exist", userId)
|
||||||
|
}
|
||||||
|
|
||||||
query := ormer.Engine.Alias("r").Where("r.users like ?", fmt.Sprintf("%%%s%%", userId))
|
query := ormer.Engine.Alias("r").Where("r.users like ?", fmt.Sprintf("%%%s%%", userId))
|
||||||
for _, group := range user.Groups {
|
for _, group := range user.Groups {
|
||||||
|
@@ -28,10 +28,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/RobotsAndPencils/go-saml"
|
|
||||||
"github.com/beevik/etree"
|
"github.com/beevik/etree"
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
saml "github.com/russellhaering/gosaml2"
|
||||||
dsig "github.com/russellhaering/goxmldsig"
|
dsig "github.com/russellhaering/goxmldsig"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -283,15 +283,15 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var authnRequest saml.AuthnRequest
|
var authnRequest saml.AuthNRequest
|
||||||
err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
|
err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", method, fmt.Errorf("err: Failed to unmarshal AuthnRequest, please check the SAML request. %s", err.Error())
|
return "", "", method, fmt.Errorf("err: Failed to unmarshal AuthnRequest, please check the SAML request. %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify samlRequest
|
// verify samlRequest
|
||||||
if isValid := application.IsRedirectUriValid(authnRequest.Issuer.Url); !isValid {
|
if isValid := application.IsRedirectUriValid(authnRequest.Issuer); !isValid {
|
||||||
return "", "", method, fmt.Errorf("err: Issuer URI: %s doesn't exist in the allowed Redirect URI list", authnRequest.Issuer.Url)
|
return "", "", method, fmt.Errorf("err: Issuer URI: %s doesn't exist in the allowed Redirect URI list", authnRequest.Issuer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get certificate string
|
// get certificate string
|
||||||
@@ -317,7 +317,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
|||||||
|
|
||||||
_, originBackend := getOriginFromHost(host)
|
_, originBackend := getOriginFromHost(host)
|
||||||
// build signedResponse
|
// build signedResponse
|
||||||
samlResponse, _ := NewSamlResponse(application, user, originBackend, certificate, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer.Url, authnRequest.ID, application.RedirectUris)
|
samlResponse, _ := NewSamlResponse(application, user, originBackend, certificate, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer, authnRequest.ID, application.RedirectUris)
|
||||||
randomKeyStore := &X509Key{
|
randomKeyStore := &X509Key{
|
||||||
PrivateKey: cert.PrivateKey,
|
PrivateKey: cert.PrivateKey,
|
||||||
X509Certificate: certificate,
|
X509Certificate: certificate,
|
||||||
|
@@ -15,9 +15,10 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/proxy"
|
"github.com/casdoor/casdoor/proxy"
|
||||||
)
|
)
|
||||||
@@ -41,18 +42,24 @@ func (c *HttpSmsClient) SendMessage(param map[string]string, targetPhoneNumber .
|
|||||||
phoneNumber := targetPhoneNumber[0]
|
phoneNumber := targetPhoneNumber[0]
|
||||||
content := param["code"]
|
content := param["code"]
|
||||||
|
|
||||||
req, err := http.NewRequest(c.method, c.endpoint, bytes.NewBufferString(content))
|
var req *http.Request
|
||||||
if err != nil {
|
var err error
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.method == "POST" {
|
if c.method == "POST" {
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
formValues := url.Values{}
|
||||||
req.PostForm = map[string][]string{
|
formValues.Set("phoneNumber", phoneNumber)
|
||||||
"phoneNumber": targetPhoneNumber,
|
formValues.Set(c.paramName, content)
|
||||||
c.paramName: {content},
|
req, err = http.NewRequest(c.method, c.endpoint, strings.NewReader(formValues.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
} else if c.method == "GET" {
|
} else if c.method == "GET" {
|
||||||
|
req, err = http.NewRequest(c.method, c.endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
q := req.URL.Query()
|
q := req.URL.Query()
|
||||||
q.Add("phoneNumber", phoneNumber)
|
q.Add("phoneNumber", phoneNumber)
|
||||||
q.Add(c.paramName, content)
|
q.Add(c.paramName, content)
|
@@ -72,6 +72,10 @@ func GetTruncatedPath(provider *Provider, fullFilePath string, limit int) string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
||||||
|
if provider.Domain != "" && !strings.HasPrefix(provider.Domain, "http://") && !strings.HasPrefix(provider.Domain, "https://") {
|
||||||
|
provider.Domain = fmt.Sprintf("https://%s", provider.Domain)
|
||||||
|
}
|
||||||
|
|
||||||
escapedPath := util.UrlJoin(provider.PathPrefix, fullFilePath)
|
escapedPath := util.UrlJoin(provider.PathPrefix, fullFilePath)
|
||||||
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
||||||
|
|
||||||
@@ -79,9 +83,6 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
|
|||||||
if provider.Type != "Local File System" {
|
if provider.Type != "Local File System" {
|
||||||
// provider.Domain = "https://cdn.casbin.com/casdoor/"
|
// provider.Domain = "https://cdn.casbin.com/casdoor/"
|
||||||
host = util.GetUrlHost(provider.Domain)
|
host = util.GetUrlHost(provider.Domain)
|
||||||
if !strings.HasPrefix(host, "http://") && !strings.HasPrefix(host, "https://") {
|
|
||||||
host = fmt.Sprintf("https://%s", host)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// provider.Domain = "http://localhost:8000" or "https://door.casdoor.com"
|
// provider.Domain = "http://localhost:8000" or "https://door.casdoor.com"
|
||||||
host = util.UrlJoin(provider.Domain, "/files")
|
host = util.UrlJoin(provider.Domain, "/files")
|
||||||
@@ -90,9 +91,12 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
|
|||||||
host = util.UrlJoin(host, provider.Bucket)
|
host = util.UrlJoin(host, provider.Bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileUrl := util.UrlJoin(host, escapePath(objectKey))
|
fileUrl := ""
|
||||||
|
if host != "" {
|
||||||
|
fileUrl = util.UrlJoin(host, escapePath(objectKey))
|
||||||
|
}
|
||||||
|
|
||||||
if hasTimestamp {
|
if fileUrl != "" && hasTimestamp {
|
||||||
fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
|
fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +116,10 @@ func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface,
|
|||||||
|
|
||||||
if provider.Domain == "" {
|
if provider.Domain == "" {
|
||||||
provider.Domain = storageProvider.GetEndpoint()
|
provider.Domain = storageProvider.GetEndpoint()
|
||||||
UpdateProvider(provider.GetId(), provider)
|
_, err := UpdateProvider(provider.GetId(), provider)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return storageProvider, nil
|
return storageProvider, nil
|
||||||
@@ -126,7 +133,12 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
|
|||||||
|
|
||||||
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
|
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
|
||||||
|
|
||||||
_, err = storageProvider.Put(objectKey, fileBuffer)
|
objectKeyRefined := objectKey
|
||||||
|
if provider.Type == "Google Cloud Storage" {
|
||||||
|
objectKeyRefined = strings.TrimPrefix(objectKeyRefined, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = storageProvider.Put(objectKeyRefined, fileBuffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
@@ -117,6 +117,7 @@ type User struct {
|
|||||||
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
|
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
|
||||||
Apple string `xorm:"apple varchar(100)" json:"apple"`
|
Apple string `xorm:"apple varchar(100)" json:"apple"`
|
||||||
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
|
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
|
||||||
|
AzureADB2c string `xorm:"azureadb2c varchar(100)" json:"azureadb2c"`
|
||||||
Slack string `xorm:"slack varchar(100)" json:"slack"`
|
Slack string `xorm:"slack varchar(100)" json:"slack"`
|
||||||
Steam string `xorm:"steam varchar(100)" json:"steam"`
|
Steam string `xorm:"steam varchar(100)" json:"steam"`
|
||||||
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
|
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
|
||||||
@@ -622,7 +623,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
|||||||
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
||||||
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret",
|
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret",
|
||||||
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
||||||
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
||||||
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
||||||
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
|
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
|
||||||
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
|
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
|
||||||
@@ -885,6 +886,18 @@ func (user *User) GetId() string {
|
|||||||
return fmt.Sprintf("%s/%s", user.Owner, user.Name)
|
return fmt.Sprintf("%s/%s", user.Owner, user.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) GetFriendlyName() string {
|
||||||
|
if user.FirstName != "" && user.LastName != "" {
|
||||||
|
return fmt.Sprintf("%s, %s", user.FirstName, user.LastName)
|
||||||
|
} else if user.DisplayName != "" {
|
||||||
|
return user.DisplayName
|
||||||
|
} else if user.Name != "" {
|
||||||
|
return user.Name
|
||||||
|
} else {
|
||||||
|
return user.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func isUserIdGlobalAdmin(userId string) bool {
|
func isUserIdGlobalAdmin(userId string) bool {
|
||||||
return strings.HasPrefix(userId, "built-in/") || strings.HasPrefix(userId, "app/")
|
return strings.HasPrefix(userId, "built-in/") || strings.HasPrefix(userId, "app/")
|
||||||
}
|
}
|
||||||
@@ -1021,7 +1034,10 @@ func GenerateIdForNewUser(application *Application) (string, error) {
|
|||||||
|
|
||||||
lastUserId := -1
|
lastUserId := -1
|
||||||
if lastUser != nil {
|
if lastUser != nil {
|
||||||
lastUserId = util.ParseInt(lastUser.Id)
|
lastUserId, err = util.ParseIntWithError(lastUser.Id)
|
||||||
|
if err != nil {
|
||||||
|
return util.GenerateId(), nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res := strconv.Itoa(lastUserId + 1)
|
res := strconv.Itoa(lastUserId + 1)
|
||||||
|
@@ -89,7 +89,10 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
|
|||||||
}
|
}
|
||||||
|
|
||||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||||
content := fmt.Sprintf(provider.Content, code)
|
content := strings.Replace(provider.Content, "%s", code, 1)
|
||||||
|
if user != nil {
|
||||||
|
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
|
||||||
|
}
|
||||||
|
|
||||||
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil {
|
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@@ -73,6 +73,12 @@ func initAPI() {
|
|||||||
beego.Router("/api/get-default-application", &controllers.ApiController{}, "GET:GetDefaultApplication")
|
beego.Router("/api/get-default-application", &controllers.ApiController{}, "GET:GetDefaultApplication")
|
||||||
beego.Router("/api/get-organization-names", &controllers.ApiController{}, "GET:GetOrganizationNames")
|
beego.Router("/api/get-organization-names", &controllers.ApiController{}, "GET:GetOrganizationNames")
|
||||||
|
|
||||||
|
beego.Router("/api/get-groups", &controllers.ApiController{}, "GET:GetGroups")
|
||||||
|
beego.Router("/api/get-group", &controllers.ApiController{}, "GET:GetGroup")
|
||||||
|
beego.Router("/api/update-group", &controllers.ApiController{}, "POST:UpdateGroup")
|
||||||
|
beego.Router("/api/add-group", &controllers.ApiController{}, "POST:AddGroup")
|
||||||
|
beego.Router("/api/delete-group", &controllers.ApiController{}, "POST:DeleteGroup")
|
||||||
|
|
||||||
beego.Router("/api/get-global-users", &controllers.ApiController{}, "GET:GetGlobalUsers")
|
beego.Router("/api/get-global-users", &controllers.ApiController{}, "GET:GetGlobalUsers")
|
||||||
beego.Router("/api/get-users", &controllers.ApiController{}, "GET:GetUsers")
|
beego.Router("/api/get-users", &controllers.ApiController{}, "GET:GetUsers")
|
||||||
beego.Router("/api/get-sorted-users", &controllers.ApiController{}, "GET:GetSortedUsers")
|
beego.Router("/api/get-sorted-users", &controllers.ApiController{}, "GET:GetSortedUsers")
|
||||||
@@ -85,11 +91,41 @@ func initAPI() {
|
|||||||
beego.Router("/api/upload-users", &controllers.ApiController{}, "POST:UploadUsers")
|
beego.Router("/api/upload-users", &controllers.ApiController{}, "POST:UploadUsers")
|
||||||
beego.Router("/api/remove-user-from-group", &controllers.ApiController{}, "POST:RemoveUserFromGroup")
|
beego.Router("/api/remove-user-from-group", &controllers.ApiController{}, "POST:RemoveUserFromGroup")
|
||||||
|
|
||||||
beego.Router("/api/get-groups", &controllers.ApiController{}, "GET:GetGroups")
|
beego.Router("/api/get-invitations", &controllers.ApiController{}, "GET:GetInvitations")
|
||||||
beego.Router("/api/get-group", &controllers.ApiController{}, "GET:GetGroup")
|
beego.Router("/api/get-invitation", &controllers.ApiController{}, "GET:GetInvitation")
|
||||||
beego.Router("/api/update-group", &controllers.ApiController{}, "POST:UpdateGroup")
|
beego.Router("/api/update-invitation", &controllers.ApiController{}, "POST:UpdateInvitation")
|
||||||
beego.Router("/api/add-group", &controllers.ApiController{}, "POST:AddGroup")
|
beego.Router("/api/add-invitation", &controllers.ApiController{}, "POST:AddInvitation")
|
||||||
beego.Router("/api/delete-group", &controllers.ApiController{}, "POST:DeleteGroup")
|
beego.Router("/api/delete-invitation", &controllers.ApiController{}, "POST:DeleteInvitation")
|
||||||
|
beego.Router("/api/verify-invitation", &controllers.ApiController{}, "GET:VerifyInvitation")
|
||||||
|
|
||||||
|
beego.Router("/api/get-applications", &controllers.ApiController{}, "GET:GetApplications")
|
||||||
|
beego.Router("/api/get-application", &controllers.ApiController{}, "GET:GetApplication")
|
||||||
|
beego.Router("/api/get-user-application", &controllers.ApiController{}, "GET:GetUserApplication")
|
||||||
|
beego.Router("/api/get-organization-applications", &controllers.ApiController{}, "GET:GetOrganizationApplications")
|
||||||
|
beego.Router("/api/update-application", &controllers.ApiController{}, "POST:UpdateApplication")
|
||||||
|
beego.Router("/api/add-application", &controllers.ApiController{}, "POST:AddApplication")
|
||||||
|
beego.Router("/api/delete-application", &controllers.ApiController{}, "POST:DeleteApplication")
|
||||||
|
|
||||||
|
beego.Router("/api/get-providers", &controllers.ApiController{}, "GET:GetProviders")
|
||||||
|
beego.Router("/api/get-provider", &controllers.ApiController{}, "GET:GetProvider")
|
||||||
|
beego.Router("/api/get-global-providers", &controllers.ApiController{}, "GET:GetGlobalProviders")
|
||||||
|
beego.Router("/api/update-provider", &controllers.ApiController{}, "POST:UpdateProvider")
|
||||||
|
beego.Router("/api/add-provider", &controllers.ApiController{}, "POST:AddProvider")
|
||||||
|
beego.Router("/api/delete-provider", &controllers.ApiController{}, "POST:DeleteProvider")
|
||||||
|
|
||||||
|
beego.Router("/api/get-resources", &controllers.ApiController{}, "GET:GetResources")
|
||||||
|
beego.Router("/api/get-resource", &controllers.ApiController{}, "GET:GetResource")
|
||||||
|
beego.Router("/api/update-resource", &controllers.ApiController{}, "POST:UpdateResource")
|
||||||
|
beego.Router("/api/add-resource", &controllers.ApiController{}, "POST:AddResource")
|
||||||
|
beego.Router("/api/delete-resource", &controllers.ApiController{}, "POST:DeleteResource")
|
||||||
|
beego.Router("/api/upload-resource", &controllers.ApiController{}, "POST:UploadResource")
|
||||||
|
|
||||||
|
beego.Router("/api/get-certs", &controllers.ApiController{}, "GET:GetCerts")
|
||||||
|
beego.Router("/api/get-global-certs", &controllers.ApiController{}, "GET:GetGlobalCerts")
|
||||||
|
beego.Router("/api/get-cert", &controllers.ApiController{}, "GET:GetCert")
|
||||||
|
beego.Router("/api/update-cert", &controllers.ApiController{}, "POST:UpdateCert")
|
||||||
|
beego.Router("/api/add-cert", &controllers.ApiController{}, "POST:AddCert")
|
||||||
|
beego.Router("/api/delete-cert", &controllers.ApiController{}, "POST:DeleteCert")
|
||||||
|
|
||||||
beego.Router("/api/get-roles", &controllers.ApiController{}, "GET:GetRoles")
|
beego.Router("/api/get-roles", &controllers.ApiController{}, "GET:GetRoles")
|
||||||
beego.Router("/api/get-role", &controllers.ApiController{}, "GET:GetRole")
|
beego.Router("/api/get-role", &controllers.ApiController{}, "GET:GetRole")
|
||||||
@@ -107,12 +143,6 @@ func initAPI() {
|
|||||||
beego.Router("/api/delete-permission", &controllers.ApiController{}, "POST:DeletePermission")
|
beego.Router("/api/delete-permission", &controllers.ApiController{}, "POST:DeletePermission")
|
||||||
beego.Router("/api/upload-permissions", &controllers.ApiController{}, "POST:UploadPermissions")
|
beego.Router("/api/upload-permissions", &controllers.ApiController{}, "POST:UploadPermissions")
|
||||||
|
|
||||||
beego.Router("/api/enforce", &controllers.ApiController{}, "POST:Enforce")
|
|
||||||
beego.Router("/api/batch-enforce", &controllers.ApiController{}, "POST:BatchEnforce")
|
|
||||||
beego.Router("/api/get-all-objects", &controllers.ApiController{}, "GET:GetAllObjects")
|
|
||||||
beego.Router("/api/get-all-actions", &controllers.ApiController{}, "GET:GetAllActions")
|
|
||||||
beego.Router("/api/get-all-roles", &controllers.ApiController{}, "GET:GetAllRoles")
|
|
||||||
|
|
||||||
beego.Router("/api/get-models", &controllers.ApiController{}, "GET:GetModels")
|
beego.Router("/api/get-models", &controllers.ApiController{}, "GET:GetModels")
|
||||||
beego.Router("/api/get-model", &controllers.ApiController{}, "GET:GetModel")
|
beego.Router("/api/get-model", &controllers.ApiController{}, "GET:GetModel")
|
||||||
beego.Router("/api/update-model", &controllers.ApiController{}, "POST:UpdateModel")
|
beego.Router("/api/update-model", &controllers.ApiController{}, "POST:UpdateModel")
|
||||||
@@ -135,53 +165,11 @@ func initAPI() {
|
|||||||
beego.Router("/api/add-enforcer", &controllers.ApiController{}, "POST:AddEnforcer")
|
beego.Router("/api/add-enforcer", &controllers.ApiController{}, "POST:AddEnforcer")
|
||||||
beego.Router("/api/delete-enforcer", &controllers.ApiController{}, "POST:DeleteEnforcer")
|
beego.Router("/api/delete-enforcer", &controllers.ApiController{}, "POST:DeleteEnforcer")
|
||||||
|
|
||||||
beego.Router("/api/set-password", &controllers.ApiController{}, "POST:SetPassword")
|
beego.Router("/api/enforce", &controllers.ApiController{}, "POST:Enforce")
|
||||||
beego.Router("/api/check-user-password", &controllers.ApiController{}, "POST:CheckUserPassword")
|
beego.Router("/api/batch-enforce", &controllers.ApiController{}, "POST:BatchEnforce")
|
||||||
beego.Router("/api/get-email-and-phone", &controllers.ApiController{}, "GET:GetEmailAndPhone")
|
beego.Router("/api/get-all-objects", &controllers.ApiController{}, "GET:GetAllObjects")
|
||||||
beego.Router("/api/send-verification-code", &controllers.ApiController{}, "POST:SendVerificationCode")
|
beego.Router("/api/get-all-actions", &controllers.ApiController{}, "GET:GetAllActions")
|
||||||
beego.Router("/api/verify-code", &controllers.ApiController{}, "POST:VerifyCode")
|
beego.Router("/api/get-all-roles", &controllers.ApiController{}, "GET:GetAllRoles")
|
||||||
beego.Router("/api/verify-captcha", &controllers.ApiController{}, "POST:VerifyCaptcha")
|
|
||||||
beego.Router("/api/reset-email-or-phone", &controllers.ApiController{}, "POST:ResetEmailOrPhone")
|
|
||||||
beego.Router("/api/get-captcha", &controllers.ApiController{}, "GET:GetCaptcha")
|
|
||||||
|
|
||||||
beego.Router("/api/get-ldap-users", &controllers.ApiController{}, "GET:GetLdapUsers")
|
|
||||||
beego.Router("/api/get-ldaps", &controllers.ApiController{}, "GET:GetLdaps")
|
|
||||||
beego.Router("/api/get-ldap", &controllers.ApiController{}, "GET:GetLdap")
|
|
||||||
beego.Router("/api/add-ldap", &controllers.ApiController{}, "POST:AddLdap")
|
|
||||||
beego.Router("/api/update-ldap", &controllers.ApiController{}, "POST:UpdateLdap")
|
|
||||||
beego.Router("/api/delete-ldap", &controllers.ApiController{}, "POST:DeleteLdap")
|
|
||||||
beego.Router("/api/sync-ldap-users", &controllers.ApiController{}, "POST:SyncLdapUsers")
|
|
||||||
|
|
||||||
beego.Router("/api/get-providers", &controllers.ApiController{}, "GET:GetProviders")
|
|
||||||
beego.Router("/api/get-provider", &controllers.ApiController{}, "GET:GetProvider")
|
|
||||||
beego.Router("/api/get-global-providers", &controllers.ApiController{}, "GET:GetGlobalProviders")
|
|
||||||
beego.Router("/api/update-provider", &controllers.ApiController{}, "POST:UpdateProvider")
|
|
||||||
beego.Router("/api/add-provider", &controllers.ApiController{}, "POST:AddProvider")
|
|
||||||
beego.Router("/api/delete-provider", &controllers.ApiController{}, "POST:DeleteProvider")
|
|
||||||
|
|
||||||
beego.Router("/api/get-applications", &controllers.ApiController{}, "GET:GetApplications")
|
|
||||||
beego.Router("/api/get-application", &controllers.ApiController{}, "GET:GetApplication")
|
|
||||||
beego.Router("/api/get-user-application", &controllers.ApiController{}, "GET:GetUserApplication")
|
|
||||||
beego.Router("/api/get-organization-applications", &controllers.ApiController{}, "GET:GetOrganizationApplications")
|
|
||||||
beego.Router("/api/update-application", &controllers.ApiController{}, "POST:UpdateApplication")
|
|
||||||
beego.Router("/api/add-application", &controllers.ApiController{}, "POST:AddApplication")
|
|
||||||
beego.Router("/api/delete-application", &controllers.ApiController{}, "POST:DeleteApplication")
|
|
||||||
|
|
||||||
beego.Router("/api/get-resources", &controllers.ApiController{}, "GET:GetResources")
|
|
||||||
beego.Router("/api/get-resource", &controllers.ApiController{}, "GET:GetResource")
|
|
||||||
beego.Router("/api/update-resource", &controllers.ApiController{}, "POST:UpdateResource")
|
|
||||||
beego.Router("/api/add-resource", &controllers.ApiController{}, "POST:AddResource")
|
|
||||||
beego.Router("/api/delete-resource", &controllers.ApiController{}, "POST:DeleteResource")
|
|
||||||
beego.Router("/api/upload-resource", &controllers.ApiController{}, "POST:UploadResource")
|
|
||||||
|
|
||||||
beego.Router("/api/get-tokens", &controllers.ApiController{}, "GET:GetTokens")
|
|
||||||
beego.Router("/api/get-token", &controllers.ApiController{}, "GET:GetToken")
|
|
||||||
beego.Router("/api/update-token", &controllers.ApiController{}, "POST:UpdateToken")
|
|
||||||
beego.Router("/api/add-token", &controllers.ApiController{}, "POST:AddToken")
|
|
||||||
beego.Router("/api/delete-token", &controllers.ApiController{}, "POST:DeleteToken")
|
|
||||||
beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken")
|
|
||||||
beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
|
|
||||||
beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken")
|
|
||||||
|
|
||||||
beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions")
|
beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions")
|
||||||
beego.Router("/api/get-session", &controllers.ApiController{}, "GET:GetSingleSession")
|
beego.Router("/api/get-session", &controllers.ApiController{}, "GET:GetSingleSession")
|
||||||
@@ -190,43 +178,11 @@ func initAPI() {
|
|||||||
beego.Router("/api/delete-session", &controllers.ApiController{}, "POST:DeleteSession")
|
beego.Router("/api/delete-session", &controllers.ApiController{}, "POST:DeleteSession")
|
||||||
beego.Router("/api/is-session-duplicated", &controllers.ApiController{}, "GET:IsSessionDuplicated")
|
beego.Router("/api/is-session-duplicated", &controllers.ApiController{}, "GET:IsSessionDuplicated")
|
||||||
|
|
||||||
beego.Router("/api/get-webhooks", &controllers.ApiController{}, "GET:GetWebhooks")
|
beego.Router("/api/get-tokens", &controllers.ApiController{}, "GET:GetTokens")
|
||||||
beego.Router("/api/get-webhook", &controllers.ApiController{}, "GET:GetWebhook")
|
beego.Router("/api/get-token", &controllers.ApiController{}, "GET:GetToken")
|
||||||
beego.Router("/api/update-webhook", &controllers.ApiController{}, "POST:UpdateWebhook")
|
beego.Router("/api/update-token", &controllers.ApiController{}, "POST:UpdateToken")
|
||||||
beego.Router("/api/add-webhook", &controllers.ApiController{}, "POST:AddWebhook")
|
beego.Router("/api/add-token", &controllers.ApiController{}, "POST:AddToken")
|
||||||
beego.Router("/api/delete-webhook", &controllers.ApiController{}, "POST:DeleteWebhook")
|
beego.Router("/api/delete-token", &controllers.ApiController{}, "POST:DeleteToken")
|
||||||
|
|
||||||
beego.Router("/api/get-syncers", &controllers.ApiController{}, "GET:GetSyncers")
|
|
||||||
beego.Router("/api/get-syncer", &controllers.ApiController{}, "GET:GetSyncer")
|
|
||||||
beego.Router("/api/update-syncer", &controllers.ApiController{}, "POST:UpdateSyncer")
|
|
||||||
beego.Router("/api/add-syncer", &controllers.ApiController{}, "POST:AddSyncer")
|
|
||||||
beego.Router("/api/delete-syncer", &controllers.ApiController{}, "POST:DeleteSyncer")
|
|
||||||
beego.Router("/api/run-syncer", &controllers.ApiController{}, "GET:RunSyncer")
|
|
||||||
|
|
||||||
beego.Router("/api/get-certs", &controllers.ApiController{}, "GET:GetCerts")
|
|
||||||
beego.Router("/api/get-global-certs", &controllers.ApiController{}, "GET:GetGlobalCerts")
|
|
||||||
beego.Router("/api/get-cert", &controllers.ApiController{}, "GET:GetCert")
|
|
||||||
beego.Router("/api/update-cert", &controllers.ApiController{}, "POST:UpdateCert")
|
|
||||||
beego.Router("/api/add-cert", &controllers.ApiController{}, "POST:AddCert")
|
|
||||||
beego.Router("/api/delete-cert", &controllers.ApiController{}, "POST:DeleteCert")
|
|
||||||
|
|
||||||
beego.Router("/api/get-subscriptions", &controllers.ApiController{}, "GET:GetSubscriptions")
|
|
||||||
beego.Router("/api/get-subscription", &controllers.ApiController{}, "GET:GetSubscription")
|
|
||||||
beego.Router("/api/update-subscription", &controllers.ApiController{}, "POST:UpdateSubscription")
|
|
||||||
beego.Router("/api/add-subscription", &controllers.ApiController{}, "POST:AddSubscription")
|
|
||||||
beego.Router("/api/delete-subscription", &controllers.ApiController{}, "POST:DeleteSubscription")
|
|
||||||
|
|
||||||
beego.Router("/api/get-plans", &controllers.ApiController{}, "GET:GetPlans")
|
|
||||||
beego.Router("/api/get-plan", &controllers.ApiController{}, "GET:GetPlan")
|
|
||||||
beego.Router("/api/update-plan", &controllers.ApiController{}, "POST:UpdatePlan")
|
|
||||||
beego.Router("/api/add-plan", &controllers.ApiController{}, "POST:AddPlan")
|
|
||||||
beego.Router("/api/delete-plan", &controllers.ApiController{}, "POST:DeletePlan")
|
|
||||||
|
|
||||||
beego.Router("/api/get-pricings", &controllers.ApiController{}, "GET:GetPricings")
|
|
||||||
beego.Router("/api/get-pricing", &controllers.ApiController{}, "GET:GetPricing")
|
|
||||||
beego.Router("/api/update-pricing", &controllers.ApiController{}, "POST:UpdatePricing")
|
|
||||||
beego.Router("/api/add-pricing", &controllers.ApiController{}, "POST:AddPricing")
|
|
||||||
beego.Router("/api/delete-pricing", &controllers.ApiController{}, "POST:DeletePricing")
|
|
||||||
|
|
||||||
beego.Router("/api/get-products", &controllers.ApiController{}, "GET:GetProducts")
|
beego.Router("/api/get-products", &controllers.ApiController{}, "GET:GetProducts")
|
||||||
beego.Router("/api/get-product", &controllers.ApiController{}, "GET:GetProduct")
|
beego.Router("/api/get-product", &controllers.ApiController{}, "GET:GetProduct")
|
||||||
@@ -244,6 +200,64 @@ func initAPI() {
|
|||||||
beego.Router("/api/notify-payment/?:owner/?:payment", &controllers.ApiController{}, "POST:NotifyPayment")
|
beego.Router("/api/notify-payment/?:owner/?:payment", &controllers.ApiController{}, "POST:NotifyPayment")
|
||||||
beego.Router("/api/invoice-payment", &controllers.ApiController{}, "POST:InvoicePayment")
|
beego.Router("/api/invoice-payment", &controllers.ApiController{}, "POST:InvoicePayment")
|
||||||
|
|
||||||
|
beego.Router("/api/get-plans", &controllers.ApiController{}, "GET:GetPlans")
|
||||||
|
beego.Router("/api/get-plan", &controllers.ApiController{}, "GET:GetPlan")
|
||||||
|
beego.Router("/api/update-plan", &controllers.ApiController{}, "POST:UpdatePlan")
|
||||||
|
beego.Router("/api/add-plan", &controllers.ApiController{}, "POST:AddPlan")
|
||||||
|
beego.Router("/api/delete-plan", &controllers.ApiController{}, "POST:DeletePlan")
|
||||||
|
|
||||||
|
beego.Router("/api/get-pricings", &controllers.ApiController{}, "GET:GetPricings")
|
||||||
|
beego.Router("/api/get-pricing", &controllers.ApiController{}, "GET:GetPricing")
|
||||||
|
beego.Router("/api/update-pricing", &controllers.ApiController{}, "POST:UpdatePricing")
|
||||||
|
beego.Router("/api/add-pricing", &controllers.ApiController{}, "POST:AddPricing")
|
||||||
|
beego.Router("/api/delete-pricing", &controllers.ApiController{}, "POST:DeletePricing")
|
||||||
|
|
||||||
|
beego.Router("/api/get-subscriptions", &controllers.ApiController{}, "GET:GetSubscriptions")
|
||||||
|
beego.Router("/api/get-subscription", &controllers.ApiController{}, "GET:GetSubscription")
|
||||||
|
beego.Router("/api/update-subscription", &controllers.ApiController{}, "POST:UpdateSubscription")
|
||||||
|
beego.Router("/api/add-subscription", &controllers.ApiController{}, "POST:AddSubscription")
|
||||||
|
beego.Router("/api/delete-subscription", &controllers.ApiController{}, "POST:DeleteSubscription")
|
||||||
|
|
||||||
|
beego.Router("/api/get-system-info", &controllers.ApiController{}, "GET:GetSystemInfo")
|
||||||
|
beego.Router("/api/get-version-info", &controllers.ApiController{}, "GET:GetVersionInfo")
|
||||||
|
beego.Router("/api/health", &controllers.ApiController{}, "GET:Health")
|
||||||
|
beego.Router("/api/get-prometheus-info", &controllers.ApiController{}, "GET:GetPrometheusInfo")
|
||||||
|
beego.Handler("/api/metrics", promhttp.Handler())
|
||||||
|
|
||||||
|
beego.Router("/api/get-syncers", &controllers.ApiController{}, "GET:GetSyncers")
|
||||||
|
beego.Router("/api/get-syncer", &controllers.ApiController{}, "GET:GetSyncer")
|
||||||
|
beego.Router("/api/update-syncer", &controllers.ApiController{}, "POST:UpdateSyncer")
|
||||||
|
beego.Router("/api/add-syncer", &controllers.ApiController{}, "POST:AddSyncer")
|
||||||
|
beego.Router("/api/delete-syncer", &controllers.ApiController{}, "POST:DeleteSyncer")
|
||||||
|
beego.Router("/api/run-syncer", &controllers.ApiController{}, "GET:RunSyncer")
|
||||||
|
|
||||||
|
beego.Router("/api/get-webhooks", &controllers.ApiController{}, "GET:GetWebhooks")
|
||||||
|
beego.Router("/api/get-webhook", &controllers.ApiController{}, "GET:GetWebhook")
|
||||||
|
beego.Router("/api/update-webhook", &controllers.ApiController{}, "POST:UpdateWebhook")
|
||||||
|
beego.Router("/api/add-webhook", &controllers.ApiController{}, "POST:AddWebhook")
|
||||||
|
beego.Router("/api/delete-webhook", &controllers.ApiController{}, "POST:DeleteWebhook")
|
||||||
|
|
||||||
|
beego.Router("/api/set-password", &controllers.ApiController{}, "POST:SetPassword")
|
||||||
|
beego.Router("/api/check-user-password", &controllers.ApiController{}, "POST:CheckUserPassword")
|
||||||
|
beego.Router("/api/get-email-and-phone", &controllers.ApiController{}, "GET:GetEmailAndPhone")
|
||||||
|
beego.Router("/api/send-verification-code", &controllers.ApiController{}, "POST:SendVerificationCode")
|
||||||
|
beego.Router("/api/verify-code", &controllers.ApiController{}, "POST:VerifyCode")
|
||||||
|
beego.Router("/api/verify-captcha", &controllers.ApiController{}, "POST:VerifyCaptcha")
|
||||||
|
beego.Router("/api/reset-email-or-phone", &controllers.ApiController{}, "POST:ResetEmailOrPhone")
|
||||||
|
beego.Router("/api/get-captcha", &controllers.ApiController{}, "GET:GetCaptcha")
|
||||||
|
|
||||||
|
beego.Router("/api/get-ldap-users", &controllers.ApiController{}, "GET:GetLdapUsers")
|
||||||
|
beego.Router("/api/get-ldaps", &controllers.ApiController{}, "GET:GetLdaps")
|
||||||
|
beego.Router("/api/get-ldap", &controllers.ApiController{}, "GET:GetLdap")
|
||||||
|
beego.Router("/api/add-ldap", &controllers.ApiController{}, "POST:AddLdap")
|
||||||
|
beego.Router("/api/update-ldap", &controllers.ApiController{}, "POST:UpdateLdap")
|
||||||
|
beego.Router("/api/delete-ldap", &controllers.ApiController{}, "POST:DeleteLdap")
|
||||||
|
beego.Router("/api/sync-ldap-users", &controllers.ApiController{}, "POST:SyncLdapUsers")
|
||||||
|
|
||||||
|
beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken")
|
||||||
|
beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
|
||||||
|
beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken")
|
||||||
|
|
||||||
beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail")
|
beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail")
|
||||||
beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms")
|
beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms")
|
||||||
beego.Router("/api/send-notification", &controllers.ApiController{}, "POST:SendNotification")
|
beego.Router("/api/send-notification", &controllers.ApiController{}, "POST:SendNotification")
|
||||||
@@ -259,13 +273,6 @@ func initAPI() {
|
|||||||
beego.Router("/api/delete-mfa", &controllers.ApiController{}, "POST:DeleteMfa")
|
beego.Router("/api/delete-mfa", &controllers.ApiController{}, "POST:DeleteMfa")
|
||||||
beego.Router("/api/set-preferred-mfa", &controllers.ApiController{}, "POST:SetPreferredMfa")
|
beego.Router("/api/set-preferred-mfa", &controllers.ApiController{}, "POST:SetPreferredMfa")
|
||||||
|
|
||||||
beego.Router("/api/get-system-info", &controllers.ApiController{}, "GET:GetSystemInfo")
|
|
||||||
beego.Router("/api/get-version-info", &controllers.ApiController{}, "GET:GetVersionInfo")
|
|
||||||
beego.Router("/api/health", &controllers.ApiController{}, "GET:Health")
|
|
||||||
beego.Router("/api/get-prometheus-info", &controllers.ApiController{}, "GET:GetPrometheusInfo")
|
|
||||||
|
|
||||||
beego.Handler("/api/metrics", promhttp.Handler())
|
|
||||||
|
|
||||||
beego.Router("/.well-known/openid-configuration", &controllers.RootController{}, "GET:GetOidcDiscovery")
|
beego.Router("/.well-known/openid-configuration", &controllers.RootController{}, "GET:GetOidcDiscovery")
|
||||||
beego.Router("/.well-known/jwks", &controllers.RootController{}, "*:GetJwks")
|
beego.Router("/.well-known/jwks", &controllers.RootController{}, "*:GetJwks")
|
||||||
|
|
||||||
|
@@ -22,12 +22,13 @@ import (
|
|||||||
|
|
||||||
func NewAwsS3StorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
func NewAwsS3StorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
||||||
sp := s3.New(&s3.Config{
|
sp := s3.New(&s3.Config{
|
||||||
AccessID: clientId,
|
AccessID: clientId,
|
||||||
AccessKey: clientSecret,
|
AccessKey: clientSecret,
|
||||||
Region: region,
|
Region: region,
|
||||||
Bucket: bucket,
|
Bucket: bucket,
|
||||||
Endpoint: endpoint,
|
Endpoint: endpoint,
|
||||||
ACL: awss3.BucketCannedACLPublicRead,
|
S3Endpoint: endpoint,
|
||||||
|
ACL: awss3.BucketCannedACLPublicRead,
|
||||||
})
|
})
|
||||||
|
|
||||||
return sp
|
return sp
|
||||||
|
@@ -19,13 +19,15 @@ import (
|
|||||||
"github.com/casdoor/oss/googlecloud"
|
"github.com/casdoor/oss/googlecloud"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewGoogleCloudStorageProvider(clientId string, clientSecret string, bucket string, endpoint string) oss.StorageInterface {
|
func NewGoogleCloudStorageProvider(clientSecret string, bucket string, endpoint string) oss.StorageInterface {
|
||||||
sp, _ := googlecloud.New(&googlecloud.Config{
|
sp, err := googlecloud.New(&googlecloud.Config{
|
||||||
AccessID: clientId,
|
ServiceAccountJson: clientSecret,
|
||||||
AccessKey: clientSecret,
|
Bucket: bucket,
|
||||||
Bucket: bucket,
|
Endpoint: endpoint,
|
||||||
Endpoint: endpoint,
|
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
return sp
|
return sp
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,7 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
|
|||||||
case "Qiniu Cloud Kodo":
|
case "Qiniu Cloud Kodo":
|
||||||
return NewQiniuCloudKodoStorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
return NewQiniuCloudKodoStorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
||||||
case "Google Cloud Storage":
|
case "Google Cloud Storage":
|
||||||
return NewGoogleCloudStorageProvider(clientId, clientSecret, bucket, endpoint)
|
return NewGoogleCloudStorageProvider(clientSecret, bucket, endpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
10
util/path.go
10
util/path.go
@@ -72,7 +72,15 @@ func GetUrlPath(urlString string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetUrlHost(urlString string) string {
|
func GetUrlHost(urlString string) string {
|
||||||
u, _ := url.Parse(urlString)
|
if urlString == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(urlString)
|
||||||
|
if err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s://%s", u.Scheme, u.Host)
|
return fmt.Sprintf("%s://%s", u.Scheme, u.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -45,6 +45,19 @@ func ParseInt(s string) int {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseIntWithError(s string) (int, error) {
|
||||||
|
if s == "" {
|
||||||
|
return 0, fmt.Errorf("ParseIntWithError() error, empty string")
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ParseFloat(s string) float64 {
|
func ParseFloat(s string) float64 {
|
||||||
f, err := strconv.ParseFloat(s, 64)
|
f, err := strconv.ParseFloat(s, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -309,3 +322,23 @@ func GetUsernameFromEmail(email string) string {
|
|||||||
return tokens[0]
|
return tokens[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StringToInterfaceArray(array []string) []interface{} {
|
||||||
|
var interfaceArray []interface{}
|
||||||
|
for _, v := range array {
|
||||||
|
interfaceArray = append(interfaceArray, v)
|
||||||
|
}
|
||||||
|
return interfaceArray
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringToInterfaceArray2d(arrays [][]string) [][]interface{} {
|
||||||
|
var interfaceArrays [][]interface{}
|
||||||
|
for _, req := range arrays {
|
||||||
|
var interfaceArray []interface{}
|
||||||
|
for _, r := range req {
|
||||||
|
interfaceArray = append(interfaceArray, r)
|
||||||
|
}
|
||||||
|
interfaceArrays = append(interfaceArrays, interfaceArray)
|
||||||
|
}
|
||||||
|
return interfaceArrays
|
||||||
|
}
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"i18n-iso-countries": "^7.0.0",
|
"i18n-iso-countries": "^7.0.0",
|
||||||
"i18next": "^19.8.9",
|
"i18next": "^19.8.9",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
"libphonenumber-js": "^1.10.19",
|
"libphonenumber-js": "^1.10.19",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"qrcode.react": "^3.1.0",
|
"qrcode.react": "^3.1.0",
|
||||||
|
118
web/src/App.js
118
web/src/App.js
@@ -15,62 +15,64 @@
|
|||||||
import React, {Component} from "react";
|
import React, {Component} from "react";
|
||||||
import "./App.less";
|
import "./App.less";
|
||||||
import {Helmet} from "react-helmet";
|
import {Helmet} from "react-helmet";
|
||||||
import Dashboard from "./basic/Dashboard";
|
|
||||||
import ShortcutsPage from "./basic/ShortcutsPage";
|
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
||||||
import {AppstoreTwoTone, BarsOutlined, DeploymentUnitOutlined, DollarTwoTone, DownOutlined, GithubOutlined, HomeTwoTone, InfoCircleFilled, LockTwoTone, LogoutOutlined, SafetyCertificateTwoTone, SettingOutlined, SettingTwoTone, ShareAltOutlined, WalletTwoTone} from "@ant-design/icons";
|
import {AppstoreTwoTone, BarsOutlined, DeploymentUnitOutlined, DollarTwoTone, DownOutlined, GithubOutlined, HomeTwoTone, InfoCircleFilled, LockTwoTone, LogoutOutlined, SafetyCertificateTwoTone, SettingOutlined, SettingTwoTone, ShareAltOutlined, WalletTwoTone} from "@ant-design/icons";
|
||||||
import {Alert, Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result, Tooltip} from "antd";
|
import {Alert, Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result, Tooltip} from "antd";
|
||||||
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
|
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
|
||||||
|
import AccountPage from "./account/AccountPage";
|
||||||
|
import Dashboard from "./basic/Dashboard";
|
||||||
|
import ShortcutsPage from "./basic/ShortcutsPage";
|
||||||
|
import AppListPage from "./basic/AppListPage";
|
||||||
import OrganizationListPage from "./OrganizationListPage";
|
import OrganizationListPage from "./OrganizationListPage";
|
||||||
import OrganizationEditPage from "./OrganizationEditPage";
|
import OrganizationEditPage from "./OrganizationEditPage";
|
||||||
|
import GroupEditPage from "./GroupEdit";
|
||||||
|
import GroupListPage from "./GroupList";
|
||||||
|
import GroupTreePage from "./GroupTreePage";
|
||||||
import UserListPage from "./UserListPage";
|
import UserListPage from "./UserListPage";
|
||||||
import UserEditPage from "./UserEditPage";
|
import UserEditPage from "./UserEditPage";
|
||||||
|
import InvitationListPage from "./InvitationListPage";
|
||||||
|
import InvitationEditPage from "./InvitationEditPage";
|
||||||
|
import ApplicationListPage from "./ApplicationListPage";
|
||||||
|
import ApplicationEditPage from "./ApplicationEditPage";
|
||||||
|
import ProviderListPage from "./ProviderListPage";
|
||||||
|
import ProviderEditPage from "./ProviderEditPage";
|
||||||
|
import ResourceListPage from "./ResourceListPage";
|
||||||
|
import CertListPage from "./CertListPage";
|
||||||
|
import CertEditPage from "./CertEditPage";
|
||||||
import RoleListPage from "./RoleListPage";
|
import RoleListPage from "./RoleListPage";
|
||||||
import RoleEditPage from "./RoleEditPage";
|
import RoleEditPage from "./RoleEditPage";
|
||||||
import PermissionListPage from "./PermissionListPage";
|
import PermissionListPage from "./PermissionListPage";
|
||||||
import PermissionEditPage from "./PermissionEditPage";
|
import PermissionEditPage from "./PermissionEditPage";
|
||||||
|
import ModelListPage from "./ModelListPage";
|
||||||
|
import ModelEditPage from "./ModelEditPage";
|
||||||
|
import AdapterListPage from "./AdapterListPage";
|
||||||
|
import AdapterEditPage from "./AdapterEditPage";
|
||||||
import EnforcerEditPage from "./EnforcerEditPage";
|
import EnforcerEditPage from "./EnforcerEditPage";
|
||||||
import EnforcerListPage from "./EnforcerListPage";
|
import EnforcerListPage from "./EnforcerListPage";
|
||||||
import GroupTreePage from "./GroupTreePage";
|
import SessionListPage from "./SessionListPage";
|
||||||
import GroupEditPage from "./GroupEdit";
|
|
||||||
import GroupListPage from "./GroupList";
|
|
||||||
import ProviderListPage from "./ProviderListPage";
|
|
||||||
import ProviderEditPage from "./ProviderEditPage";
|
|
||||||
import ApplicationListPage from "./ApplicationListPage";
|
|
||||||
import ApplicationEditPage from "./ApplicationEditPage";
|
|
||||||
import ResourceListPage from "./ResourceListPage";
|
|
||||||
import LdapEditPage from "./LdapEditPage";
|
|
||||||
import LdapSyncPage from "./LdapSyncPage";
|
|
||||||
import TokenListPage from "./TokenListPage";
|
import TokenListPage from "./TokenListPage";
|
||||||
import TokenEditPage from "./TokenEditPage";
|
import TokenEditPage from "./TokenEditPage";
|
||||||
import WebhookListPage from "./WebhookListPage";
|
|
||||||
import WebhookEditPage from "./WebhookEditPage";
|
|
||||||
import SyncerListPage from "./SyncerListPage";
|
|
||||||
import SyncerEditPage from "./SyncerEditPage";
|
|
||||||
import CertListPage from "./CertListPage";
|
|
||||||
import CertEditPage from "./CertEditPage";
|
|
||||||
import SubscriptionListPage from "./SubscriptionListPage";
|
|
||||||
import SubscriptionEditPage from "./SubscriptionEditPage";
|
|
||||||
import PricingListPage from "./PricingListPage";
|
|
||||||
import PricingEditPage from "./PricingEditPage";
|
|
||||||
import PlanListPage from "./PlanListPage";
|
|
||||||
import PlanEditPage from "./PlanEditPage";
|
|
||||||
import ProductListPage from "./ProductListPage";
|
import ProductListPage from "./ProductListPage";
|
||||||
import ProductEditPage from "./ProductEditPage";
|
import ProductEditPage from "./ProductEditPage";
|
||||||
import ProductBuyPage from "./ProductBuyPage";
|
import ProductBuyPage from "./ProductBuyPage";
|
||||||
import PaymentListPage from "./PaymentListPage";
|
import PaymentListPage from "./PaymentListPage";
|
||||||
import PaymentEditPage from "./PaymentEditPage";
|
import PaymentEditPage from "./PaymentEditPage";
|
||||||
import PaymentResultPage from "./PaymentResultPage";
|
import PaymentResultPage from "./PaymentResultPage";
|
||||||
import ModelListPage from "./ModelListPage";
|
import PricingListPage from "./PricingListPage";
|
||||||
import ModelEditPage from "./ModelEditPage";
|
import PricingEditPage from "./PricingEditPage";
|
||||||
import AdapterListPage from "./AdapterListPage";
|
import PlanListPage from "./PlanListPage";
|
||||||
import AdapterEditPage from "./AdapterEditPage";
|
import PlanEditPage from "./PlanEditPage";
|
||||||
import SessionListPage from "./SessionListPage";
|
import SubscriptionListPage from "./SubscriptionListPage";
|
||||||
import MfaSetupPage from "./auth/MfaSetupPage";
|
import SubscriptionEditPage from "./SubscriptionEditPage";
|
||||||
import SystemInfo from "./SystemInfo";
|
import SystemInfo from "./SystemInfo";
|
||||||
import AccountPage from "./account/AccountPage";
|
import SyncerListPage from "./SyncerListPage";
|
||||||
import AppListPage from "./basic/AppListPage";
|
import SyncerEditPage from "./SyncerEditPage";
|
||||||
|
import WebhookListPage from "./WebhookListPage";
|
||||||
|
import WebhookEditPage from "./WebhookEditPage";
|
||||||
|
import LdapEditPage from "./LdapEditPage";
|
||||||
|
import LdapSyncPage from "./LdapSyncPage";
|
||||||
|
import MfaSetupPage from "./auth/MfaSetupPage";
|
||||||
import CustomGithubCorner from "./common/CustomGithubCorner";
|
import CustomGithubCorner from "./common/CustomGithubCorner";
|
||||||
import * as Conf from "./Conf";
|
import * as Conf from "./Conf";
|
||||||
|
|
||||||
@@ -153,7 +155,7 @@ class App extends Component {
|
|||||||
});
|
});
|
||||||
if (uri === "/" || uri.includes("/shortcuts") || uri.includes("/apps")) {
|
if (uri === "/" || uri.includes("/shortcuts") || uri.includes("/apps")) {
|
||||||
this.setState({selectedMenuKey: "/home"});
|
this.setState({selectedMenuKey: "/home"});
|
||||||
} else if (uri.includes("/organizations") || uri.includes("/trees") || uri.includes("/users") || uri.includes("/groups")) {
|
} else if (uri.includes("/organizations") || uri.includes("/trees") || uri.includes("/groups") || uri.includes("/users") || uri.includes("/invitations")) {
|
||||||
this.setState({selectedMenuKey: "/orgs"});
|
this.setState({selectedMenuKey: "/orgs"});
|
||||||
} else if (uri.includes("/applications") || uri.includes("/providers") || uri.includes("/resources") || uri.includes("/certs")) {
|
} else if (uri.includes("/applications") || uri.includes("/providers") || uri.includes("/resources") || uri.includes("/certs")) {
|
||||||
this.setState({selectedMenuKey: "/identity"});
|
this.setState({selectedMenuKey: "/identity"});
|
||||||
@@ -434,6 +436,7 @@ class App extends Component {
|
|||||||
Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>, "/organizations"),
|
Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>, "/organizations"),
|
||||||
Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>, "/groups"),
|
Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>, "/groups"),
|
||||||
Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>, "/users"),
|
Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>, "/users"),
|
||||||
|
Setting.getItem(<Link to="/invitations">{i18next.t("general:Invitations")}</Link>, "/invitations"),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone />, [
|
res.push(Setting.getItem(<Link style={{color: "black"}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone />, [
|
||||||
@@ -514,48 +517,49 @@ class App extends Component {
|
|||||||
<Route exact path="/groups/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/groups/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={this.state.account} {...props} />} />
|
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={this.state.account} {...props} />} />
|
||||||
|
<Route exact path="/invitations" render={(props) => this.renderLoginIfNotLoggedIn(<InvitationListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/invitations/:organizationName/:invitationName" render={(props) => this.renderLoginIfNotLoggedIn(<InvitationEditPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/applications/:organizationName/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/providers/:organizationName/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/certs" render={(props) => this.renderLoginIfNotLoggedIn(<CertListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/certs/:organizationName/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/roles" render={(props) => this.renderLoginIfNotLoggedIn(<RoleListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/roles" render={(props) => this.renderLoginIfNotLoggedIn(<RoleListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/roles/:organizationName/:roleName" render={(props) => this.renderLoginIfNotLoggedIn(<RoleEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/roles/:organizationName/:roleName" render={(props) => this.renderLoginIfNotLoggedIn(<RoleEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/permissions" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/permissions" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/permissions/:organizationName/:permissionName" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/permissions/:organizationName/:permissionName" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/models" render={(props) => this.renderLoginIfNotLoggedIn(<ModelListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/models" render={(props) => this.renderLoginIfNotLoggedIn(<ModelListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/models/:organizationName/:modelName" render={(props) => this.renderLoginIfNotLoggedIn(<ModelEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/models/:organizationName/:modelName" render={(props) => this.renderLoginIfNotLoggedIn(<ModelEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/enforcers" render={(props) => this.renderLoginIfNotLoggedIn(<EnforcerListPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/enforcers/:organizationName/:enforcerName" render={(props) => this.renderLoginIfNotLoggedIn(<EnforcerEditPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/adapters" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/adapters" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/enforcers" render={(props) => this.renderLoginIfNotLoggedIn(<EnforcerListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/providers/:organizationName/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/enforcers/:organizationName/:enforcerName" render={(props) => this.renderLoginIfNotLoggedIn(<EnforcerEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/applications/:organizationName/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} />
|
|
||||||
{/* <Route exact path="/resources/:resourceName" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceEditPage account={this.state.account} {...props} />)}/>*/}
|
|
||||||
<Route exact path="/ldap/:organizationName/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/ldap/sync/:organizationName/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapSyncPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/tokens" render={(props) => this.renderLoginIfNotLoggedIn(<TokenListPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/sessions" render={(props) => this.renderLoginIfNotLoggedIn(<SessionListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/sessions" render={(props) => this.renderLoginIfNotLoggedIn(<SessionListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/tokens" render={(props) => this.renderLoginIfNotLoggedIn(<TokenListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/tokens/:tokenName" render={(props) => this.renderLoginIfNotLoggedIn(<TokenEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/tokens/:tokenName" render={(props) => this.renderLoginIfNotLoggedIn(<TokenEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/webhooks" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookListPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/webhooks/:webhookName" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookEditPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/syncers" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerListPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/syncers/:syncerName" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerEditPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/certs" render={(props) => this.renderLoginIfNotLoggedIn(<CertListPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/certs/:organizationName/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/plans" render={(props) => this.renderLoginIfNotLoggedIn(<PlanListPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/plans/:organizationName/:planName" render={(props) => this.renderLoginIfNotLoggedIn(<PlanEditPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/pricings" render={(props) => this.renderLoginIfNotLoggedIn(<PricingListPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/pricings/:organizationName/:pricingName" render={(props) => this.renderLoginIfNotLoggedIn(<PricingEditPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/subscriptions" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionListPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/subscriptions/:organizationName/:subscriptionName" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionEditPage account={this.state.account} {...props} />)} />
|
|
||||||
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/products/:organizationName/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/products/:organizationName/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/products/:organizationName/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)} />
|
<Route exact path="/products/:organizationName/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/payments/:organizationName/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/payments/:organizationName/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/payments/:organizationName/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
|
<Route exact path="/payments/:organizationName/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/plans" render={(props) => this.renderLoginIfNotLoggedIn(<PlanListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/plans/:organizationName/:planName" render={(props) => this.renderLoginIfNotLoggedIn(<PlanEditPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/pricings" render={(props) => this.renderLoginIfNotLoggedIn(<PricingListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/pricings/:organizationName/:pricingName" render={(props) => this.renderLoginIfNotLoggedIn(<PricingEditPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/subscriptions" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/subscriptions/:organizationName/:subscriptionName" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionEditPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/syncers" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/syncers/:syncerName" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerEditPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/webhooks" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/webhooks/:webhookName" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookEditPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/ldap/:organizationName/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/ldap/sync/:organizationName/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapSyncPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/mfa/setup" render={(props) => this.renderLoginIfNotLoggedIn(<MfaSetupPage account={this.state.account} onfinish={() => this.setState({requiredEnableMfa: false})} {...props} />)} />
|
<Route exact path="/mfa/setup" render={(props) => this.renderLoginIfNotLoggedIn(<MfaSetupPage account={this.state.account} onfinish={() => this.setState({requiredEnableMfa: false})} {...props} />)} />
|
||||||
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
|
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
|
||||||
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
|
|
||||||
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
|
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
|
||||||
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
|
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Button, Card, Col, ConfigProvider, Input, List, Popover, Radio, Result, Row, Select, Space, Switch, Upload} from "antd";
|
import {Button, Card, Col, ConfigProvider, Input, InputNumber, List, Popover, Radio, Result, Row, Select, Space, Switch, Upload} from "antd";
|
||||||
import {CopyOutlined, LinkOutlined, UploadOutlined} from "@ant-design/icons";
|
import {CopyOutlined, LinkOutlined, UploadOutlined} from "@ant-design/icons";
|
||||||
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||||
import * as CertBackend from "./backend/CertBackend";
|
import * as CertBackend from "./backend/CertBackend";
|
||||||
@@ -27,6 +27,7 @@ import LoginPage from "./auth/LoginPage";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import UrlTable from "./table/UrlTable";
|
import UrlTable from "./table/UrlTable";
|
||||||
import ProviderTable from "./table/ProviderTable";
|
import ProviderTable from "./table/ProviderTable";
|
||||||
|
import SigninTable from "./table/SigninTable";
|
||||||
import SignupTable from "./table/SignupTable";
|
import SignupTable from "./table/SignupTable";
|
||||||
import SamlAttributeTable from "./table/SamlAttributeTable";
|
import SamlAttributeTable from "./table/SamlAttributeTable";
|
||||||
import PromptPage from "./auth/PromptPage";
|
import PromptPage from "./auth/PromptPage";
|
||||||
@@ -199,7 +200,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseApplicationField(key, value) {
|
parseApplicationField(key, value) {
|
||||||
if (["expireInHours", "refreshExpireInHours", "offset"].includes(key)) {
|
if (["offset"].includes(key)) {
|
||||||
value = Setting.myParseInt(value);
|
value = Setting.myParseInt(value);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@@ -394,8 +395,8 @@ class ApplicationEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("application:Token expire"), i18next.t("application:Token expire - Tooltip"))} :
|
{Setting.getLabel(i18next.t("application:Token expire"), i18next.t("application:Token expire - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input style={{width: "150px"}} value={this.state.application.expireInHours} suffix="Hours" onChange={e => {
|
<InputNumber style={{width: "150px"}} value={this.state.application.expireInHours} min={1} step={1} precision={0} addonAfter="Hours" onChange={value => {
|
||||||
this.updateApplicationField("expireInHours", e.target.value);
|
this.updateApplicationField("expireInHours", value);
|
||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@@ -404,18 +405,28 @@ class ApplicationEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("application:Refresh token expire"), i18next.t("application:Refresh token expire - Tooltip"))} :
|
{Setting.getLabel(i18next.t("application:Refresh token expire"), i18next.t("application:Refresh token expire - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input style={{width: "150px"}} value={this.state.application.refreshExpireInHours} suffix="Hours" onChange={e => {
|
<InputNumber style={{width: "150px"}} value={this.state.application.refreshExpireInHours} min={1} step={1} precision={0} addonAfter="Hours" onChange={value => {
|
||||||
this.updateApplicationField("refreshExpireInHours", e.target.value);
|
this.updateApplicationField("refreshExpireInHours", value);
|
||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("application:Enable password"), i18next.t("application:Enable password - Tooltip"))} :
|
{Setting.getLabel(i18next.t("application:Failed signin limit"), i18next.t("application:Failed signin limit - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={1} >
|
<Col span={22} >
|
||||||
<Switch checked={this.state.application.enablePassword} onChange={checked => {
|
<InputNumber style={{width: "150px"}} value={this.state.application.failedSigninLimit} min={1} step={1} precision={0} addonAfter="Times" onChange={value => {
|
||||||
this.updateApplicationField("enablePassword", checked);
|
this.updateApplicationField("failedSigninLimit", value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("application:Failed signin frozen time"), i18next.t("application:Failed signin frozen time - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<InputNumber style={{width: "150px"}} value={this.state.application.failedSigninfrozenTime} min={1} step={1} precision={0} addonAfter="Minutes" onChange={value => {
|
||||||
|
this.updateApplicationField("failedSigninfrozenTime", value);
|
||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@@ -449,26 +460,6 @@ class ApplicationEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
|
||||||
{Setting.getLabel(i18next.t("application:Enable code signin"), i18next.t("application:Enable code signin - Tooltip"))} :
|
|
||||||
</Col>
|
|
||||||
<Col span={1} >
|
|
||||||
<Switch checked={this.state.application.enableCodeSignin} onChange={checked => {
|
|
||||||
this.updateApplicationField("enableCodeSignin", checked);
|
|
||||||
}} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{marginTop: "20px"}} >
|
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
|
||||||
{Setting.getLabel(i18next.t("application:Enable WebAuthn signin"), i18next.t("application:Enable WebAuthn signin - Tooltip"))} :
|
|
||||||
</Col>
|
|
||||||
<Col span={1} >
|
|
||||||
<Switch checked={this.state.application.enableWebAuthn} onChange={checked => {
|
|
||||||
this.updateApplicationField("enableWebAuthn", checked);
|
|
||||||
}} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||||
{Setting.getLabel(i18next.t("application:Enable Email linking"), i18next.t("application:Enable Email linking - Tooltip"))} :
|
{Setting.getLabel(i18next.t("application:Enable Email linking"), i18next.t("application:Enable Email linking - Tooltip"))} :
|
||||||
@@ -479,6 +470,20 @@ class ApplicationEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("application:Signin methods"), i18next.t("application:Signin methods - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<SigninTable
|
||||||
|
title={i18next.t("application:Signin methods")}
|
||||||
|
table={this.state.application.signinMethods}
|
||||||
|
onUpdateTable={(value) => {
|
||||||
|
this.updateApplicationField("signinMethods", value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("application:Org choice mode"), i18next.t("application:Org choice mode - Tooltip"))} :
|
{Setting.getLabel(i18next.t("application:Org choice mode"), i18next.t("application:Org choice mode - Tooltip"))} :
|
||||||
@@ -676,7 +681,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
<br />
|
<br />
|
||||||
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
||||||
copy(`${window.location.origin}/api/saml/metadata?application=admin/${encodeURIComponent(this.state.applicationName)}`);
|
copy(`${window.location.origin}/api/saml/metadata?application=admin/${encodeURIComponent(this.state.applicationName)}`);
|
||||||
Setting.showMessage("success", i18next.t("application:SAML metadata URL copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18next.t("application:Copy SAML metadata URL")}
|
{i18next.t("application:Copy SAML metadata URL")}
|
||||||
@@ -885,7 +890,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
<Space>
|
<Space>
|
||||||
<Button icon={<CopyOutlined />} onClick={() => {
|
<Button icon={<CopyOutlined />} onClick={() => {
|
||||||
copy(item.code);
|
copy(item.code);
|
||||||
Setting.showMessage("success", i18next.t("application:Invitation code copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}
|
}
|
||||||
}>
|
}>
|
||||||
{i18next.t("general:Copy")}
|
{i18next.t("general:Copy")}
|
||||||
@@ -930,7 +935,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
|
|
||||||
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${redirectUri}&scope=read&state=casdoor`;
|
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${redirectUri}&scope=read&state=casdoor`;
|
||||||
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
||||||
if (!this.state.application.enablePassword) {
|
if (!Setting.isPasswordEnabled(this.state.application)) {
|
||||||
signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize");
|
signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -939,7 +944,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
<Col span={previewGrid}>
|
<Col span={previewGrid}>
|
||||||
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
||||||
copy(`${window.location.origin}${signUpUrl}`);
|
copy(`${window.location.origin}${signUpUrl}`);
|
||||||
Setting.showMessage("success", i18next.t("application:Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18next.t("application:Copy signup page URL")}
|
{i18next.t("application:Copy signup page URL")}
|
||||||
@@ -954,7 +959,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
}}>
|
}}>
|
||||||
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", overflow: "auto"}}>
|
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", overflow: "auto"}}>
|
||||||
{
|
{
|
||||||
this.state.application.enablePassword ? (
|
Setting.isPasswordEnabled(this.state.application) ? (
|
||||||
<div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "auto"}}>
|
<div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "auto"}}>
|
||||||
<SignupPage application={this.state.application} preview = "auto" />
|
<SignupPage application={this.state.application} preview = "auto" />
|
||||||
</div>
|
</div>
|
||||||
@@ -971,7 +976,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
<Col span={previewGrid}>
|
<Col span={previewGrid}>
|
||||||
<Button style={{marginBottom: "10px", marginTop: Setting.isMobile() ? "15px" : "0"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
<Button style={{marginBottom: "10px", marginTop: Setting.isMobile() ? "15px" : "0"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
||||||
copy(`${window.location.origin}${signInUrl}`);
|
copy(`${window.location.origin}${signInUrl}`);
|
||||||
Setting.showMessage("success", i18next.t("application:Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18next.t("application:Copy signin page URL")}
|
{i18next.t("application:Copy signin page URL")}
|
||||||
@@ -1004,7 +1009,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
<Col span={previewGrid}>
|
<Col span={previewGrid}>
|
||||||
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
||||||
copy(`${window.location.origin}${promptUrl}`);
|
copy(`${window.location.origin}${promptUrl}`);
|
||||||
Setting.showMessage("success", i18next.t("application:Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18next.t("application:Copy prompt page URL")}
|
{i18next.t("application:Copy prompt page URL")}
|
||||||
@@ -1029,6 +1034,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
submitApplicationEdit(exitAfterSave) {
|
submitApplicationEdit(exitAfterSave) {
|
||||||
const application = Setting.deepCopy(this.state.application);
|
const application = Setting.deepCopy(this.state.application);
|
||||||
application.providers = application.providers?.filter(provider => this.state.providers.map(provider => provider.name).includes(provider.name));
|
application.providers = application.providers?.filter(provider => this.state.providers.map(provider => provider.name).includes(provider.name));
|
||||||
|
application.signinMethods = application.signinMethods?.filter(signinMethod => ["Password", "Verification code", "WebAuthn"].includes(signinMethod.name));
|
||||||
|
|
||||||
ApplicationBackend.updateApplication("admin", this.state.applicationName, application)
|
ApplicationBackend.updateApplication("admin", this.state.applicationName, application)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
@@ -46,6 +46,11 @@ class ApplicationListPage extends BaseListPage {
|
|||||||
providers: [
|
providers: [
|
||||||
{name: "provider_captcha_default", canSignUp: false, canSignIn: false, canUnlink: false, prompted: false, signupGroup: "", rule: ""},
|
{name: "provider_captcha_default", canSignUp: false, canSignIn: false, canUnlink: false, prompted: false, signupGroup: "", rule: ""},
|
||||||
],
|
],
|
||||||
|
SigninMethods: [
|
||||||
|
{name: "Password", displayName: "Password", rule: "None"},
|
||||||
|
{name: "Verification code", displayName: "Verification code", rule: "All"},
|
||||||
|
{name: "WebAuthn", displayName: "WebAuthn", rule: "None"},
|
||||||
|
],
|
||||||
signupItems: [
|
signupItems: [
|
||||||
{name: "ID", visible: false, required: true, rule: "Random"},
|
{name: "ID", visible: false, required: true, rule: "Random"},
|
||||||
{name: "Username", visible: true, required: true, rule: "None"},
|
{name: "Username", visible: true, required: true, rule: "None"},
|
||||||
|
@@ -230,7 +230,7 @@ class CertEditPage extends React.Component {
|
|||||||
<Col span={editorWidth} >
|
<Col span={editorWidth} >
|
||||||
<Button style={{marginRight: "10px", marginBottom: "10px"}} disabled={this.state.cert.certificate === ""} onClick={() => {
|
<Button style={{marginRight: "10px", marginBottom: "10px"}} disabled={this.state.cert.certificate === ""} onClick={() => {
|
||||||
copy(this.state.cert.certificate);
|
copy(this.state.cert.certificate);
|
||||||
Setting.showMessage("success", i18next.t("cert:Certificate copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18next.t("cert:Copy certificate")}
|
{i18next.t("cert:Copy certificate")}
|
||||||
@@ -253,7 +253,7 @@ class CertEditPage extends React.Component {
|
|||||||
<Col span={editorWidth} >
|
<Col span={editorWidth} >
|
||||||
<Button style={{marginRight: "10px", marginBottom: "10px"}} disabled={this.state.cert.privateKey === ""} onClick={() => {
|
<Button style={{marginRight: "10px", marginBottom: "10px"}} disabled={this.state.cert.privateKey === ""} onClick={() => {
|
||||||
copy(this.state.cert.privateKey);
|
copy(this.state.cert.privateKey);
|
||||||
Setting.showMessage("success", i18next.t("cert:Private key copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18next.t("cert:Copy private key")}
|
{i18next.t("cert:Copy private key")}
|
||||||
|
282
web/src/InvitationEditPage.js
Normal file
282
web/src/InvitationEditPage.js
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
|
||||||
|
import * as InvitationBackend from "./backend/InvitationBackend";
|
||||||
|
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||||
|
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||||
|
import * as Setting from "./Setting";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
|
const {Option} = Select;
|
||||||
|
|
||||||
|
class InvitationEditPage extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
classes: props,
|
||||||
|
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||||
|
invitationName: props.match.params.invitationName,
|
||||||
|
invitation: null,
|
||||||
|
organizations: [],
|
||||||
|
applications: [],
|
||||||
|
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
UNSAFE_componentWillMount() {
|
||||||
|
this.getInvitation();
|
||||||
|
this.getOrganizations();
|
||||||
|
this.getApplicationsByOrganization(this.state.organizationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
getInvitation() {
|
||||||
|
InvitationBackend.getInvitation(this.state.organizationName, this.state.invitationName)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data === null) {
|
||||||
|
this.props.history.push("/404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
invitation: res.data,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrganizations() {
|
||||||
|
OrganizationBackend.getOrganizations("admin")
|
||||||
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
organizations: res.data || [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getApplicationsByOrganization(organizationName) {
|
||||||
|
ApplicationBackend.getApplicationsByOrganization("admin", organizationName)
|
||||||
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
applications: res.data || [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
parseInvitationField(key, value) {
|
||||||
|
if ([""].includes(key)) {
|
||||||
|
value = Setting.myParseInt(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInvitationField(key, value) {
|
||||||
|
value = this.parseInvitationField(key, value);
|
||||||
|
|
||||||
|
const invitation = this.state.invitation;
|
||||||
|
invitation[key] = value;
|
||||||
|
this.setState({
|
||||||
|
invitation: invitation,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInvitation() {
|
||||||
|
const isCreatedByPlan = this.state.invitation.tag === "auto_created_invitation_for_plan";
|
||||||
|
return (
|
||||||
|
<Card size="small" title={
|
||||||
|
<div>
|
||||||
|
{this.state.mode === "add" ? i18next.t("invitation:New Invitation") : i18next.t("invitation:Edit Invitation")}
|
||||||
|
<Button onClick={() => this.submitInvitationEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||||
|
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitInvitationEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||||
|
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteInvitation()}>{i18next.t("general:Cancel")}</Button> : null}
|
||||||
|
</div>
|
||||||
|
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
||||||
|
<Row style={{marginTop: "10px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account) || isCreatedByPlan} value={this.state.invitation.owner} onChange={(value => {this.updateInvitationField("owner", value);})}>
|
||||||
|
{
|
||||||
|
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.invitation.name} disabled={isCreatedByPlan} onChange={e => {
|
||||||
|
this.updateInvitationField("name", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Display name"), i18next.t("general:Display name - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.invitation.displayName} onChange={e => {
|
||||||
|
this.updateInvitationField("displayName", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("invitation:Code"), i18next.t("invitation:Code - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.invitation.code} onChange={e => {
|
||||||
|
this.updateInvitationField("code", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("invitation:Quota"), i18next.t("invitation:Quota - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<InputNumber min={0} value={this.state.invitation.quota} onChange={value => {
|
||||||
|
this.updateInvitationField("quota", value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("invitation:Used count"), i18next.t("invitation:Used count - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<InputNumber min={0} max={this.state.invitation.quota} value={this.state.invitation.usedCount} onChange={value => {
|
||||||
|
this.updateInvitationField("usedCount", value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Application"), i18next.t("general:Application - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select virtual={false} style={{width: "100%"}} value={this.state.invitation.application}
|
||||||
|
onChange={(value => {this.updateInvitationField("application", value);})}
|
||||||
|
options={this.state.applications.map((application) => Setting.getOption(application.name, application.name))
|
||||||
|
} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("signup:Username"), i18next.t("signup:Username - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.invitation.username} onChange={e => {
|
||||||
|
this.updateInvitationField("username", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Email"), i18next.t("general:Email - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.invitation.email} onChange={e => {
|
||||||
|
this.updateInvitationField("email", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Phone"), i18next.t("general:Phone - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.invitation.phone} onChange={e => {
|
||||||
|
this.updateInvitationField("phone", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:State"), i18next.t("general:State - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select virtual={false} style={{width: "100%"}} value={this.state.invitation.state} onChange={(value => {
|
||||||
|
this.updateInvitationField("state", value);
|
||||||
|
})}
|
||||||
|
options={[
|
||||||
|
{value: "Active", name: i18next.t("subscription:Active")},
|
||||||
|
{value: "Suspended", name: i18next.t("subscription:Suspended")},
|
||||||
|
].map((item) => Setting.getOption(item.name, item.value))}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
submitInvitationEdit(exitAfterSave) {
|
||||||
|
const invitation = Setting.deepCopy(this.state.invitation);
|
||||||
|
InvitationBackend.updateInvitation(this.state.organizationName, this.state.invitationName, invitation)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
|
this.setState({
|
||||||
|
invitationName: this.state.invitation.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exitAfterSave) {
|
||||||
|
this.props.history.push("/invitations");
|
||||||
|
} else {
|
||||||
|
this.props.history.push(`/invitations/${this.state.invitation.owner}/${this.state.invitation.name}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
|
this.updateInvitationField("name", this.state.invitationName);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteInvitation() {
|
||||||
|
InvitationBackend.deleteInvitation(this.state.invitation)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.props.history.push("/invitations");
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
this.state.invitation !== null ? this.renderInvitation() : null
|
||||||
|
}
|
||||||
|
<div style={{marginTop: "20px", marginLeft: "40px"}}>
|
||||||
|
<Button size="large" onClick={() => this.submitInvitationEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||||
|
<Button style={{marginLeft: "20px"}} type="primary" size="large" onClick={() => this.submitInvitationEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||||
|
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} size="large" onClick={() => this.deleteInvitation()}>{i18next.t("general:Cancel")}</Button> : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InvitationEditPage;
|
310
web/src/InvitationListPage.js
Normal file
310
web/src/InvitationListPage.js
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {Link} from "react-router-dom";
|
||||||
|
import {Button, Table} from "antd";
|
||||||
|
import {MinusCircleOutlined, SyncOutlined} from "@ant-design/icons";
|
||||||
|
import moment from "moment";
|
||||||
|
import * as Setting from "./Setting";
|
||||||
|
import * as InvitationBackend from "./backend/InvitationBackend";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import BaseListPage from "./BaseListPage";
|
||||||
|
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||||
|
import copy from "copy-to-clipboard";
|
||||||
|
|
||||||
|
class InvitationListPage extends BaseListPage {
|
||||||
|
newInvitation() {
|
||||||
|
const randomName = Setting.getRandomName();
|
||||||
|
const owner = Setting.getRequestOrganization(this.props.account);
|
||||||
|
return {
|
||||||
|
owner: owner,
|
||||||
|
name: `invitation_${randomName}`,
|
||||||
|
createdTime: moment().format(),
|
||||||
|
updatedTime: moment().format(),
|
||||||
|
displayName: `New Invitation - ${randomName}`,
|
||||||
|
code: Math.random().toString(36).slice(-10),
|
||||||
|
quota: 1,
|
||||||
|
usedCount: 0,
|
||||||
|
application: "",
|
||||||
|
username: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
signupGroup: "",
|
||||||
|
state: "Active",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addInvitation() {
|
||||||
|
const newInvitation = this.newInvitation();
|
||||||
|
InvitationBackend.addInvitation(newInvitation)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.props.history.push({pathname: `/invitations/${newInvitation.owner}/${newInvitation.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteInvitation(i) {
|
||||||
|
InvitationBackend.deleteInvitation(this.state.data[i])
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
|
this.setState({
|
||||||
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTable(invitations) {
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Name"),
|
||||||
|
dataIndex: "name",
|
||||||
|
key: "name",
|
||||||
|
width: "140px",
|
||||||
|
fixed: "left",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("name"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return (
|
||||||
|
<Link to={`/invitations/${record.owner}/${text}`}>
|
||||||
|
{text}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Organization"),
|
||||||
|
dataIndex: "owner",
|
||||||
|
key: "owner",
|
||||||
|
width: "150px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("owner"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return (
|
||||||
|
<Link to={`/organizations/${text}`}>
|
||||||
|
{text}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: i18next.t("general:Created time"),
|
||||||
|
// dataIndex: "createdTime",
|
||||||
|
// key: "createdTime",
|
||||||
|
// width: "160px",
|
||||||
|
// sorter: true,
|
||||||
|
// render: (text, record, index) => {
|
||||||
|
// return Setting.getFormattedDate(text);
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Updated time"),
|
||||||
|
dataIndex: "updatedTime",
|
||||||
|
key: "updatedTime",
|
||||||
|
width: "160px",
|
||||||
|
sorter: true,
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return Setting.getFormattedDate(text);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Display name"),
|
||||||
|
dataIndex: "displayName",
|
||||||
|
key: "displayName",
|
||||||
|
width: "170px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("displayName"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("invitation:Code"),
|
||||||
|
dataIndex: "code",
|
||||||
|
key: "code",
|
||||||
|
width: "160px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("code"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("invitation:Quota"),
|
||||||
|
dataIndex: "quota",
|
||||||
|
key: "quota",
|
||||||
|
width: "120px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("quota"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("invitation:Used count"),
|
||||||
|
dataIndex: "usedCount",
|
||||||
|
key: "usedCount",
|
||||||
|
width: "130px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("usedCount"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Application"),
|
||||||
|
dataIndex: "application",
|
||||||
|
key: "application",
|
||||||
|
width: "170px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("application"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return (
|
||||||
|
<Link to={`/applications/${record.owner}/${text}`}>
|
||||||
|
{text}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Email"),
|
||||||
|
dataIndex: "email",
|
||||||
|
key: "email",
|
||||||
|
width: "160px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("email"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return (
|
||||||
|
<a href={`mailto:${text}`}>
|
||||||
|
{text}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Phone"),
|
||||||
|
dataIndex: "phone",
|
||||||
|
key: "phone",
|
||||||
|
width: "120px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("phone"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:State"),
|
||||||
|
dataIndex: "state",
|
||||||
|
key: "state",
|
||||||
|
width: "120px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("state"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
switch (text) {
|
||||||
|
case "Active":
|
||||||
|
return Setting.getTag("success", i18next.t("subscription:Active"), <SyncOutlined spin />);
|
||||||
|
case "Suspended":
|
||||||
|
return Setting.getTag("default", i18next.t("subscription:Suspended"), <MinusCircleOutlined />);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Action"),
|
||||||
|
dataIndex: "",
|
||||||
|
key: "op",
|
||||||
|
width: "350px",
|
||||||
|
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => {
|
||||||
|
copy(`${window.location.origin}/login/${record.owner}?invitation_code=${record.code}`);
|
||||||
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
|
}}>
|
||||||
|
{i18next.t("application:Copy signup page URL")}
|
||||||
|
</Button>
|
||||||
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/invitations/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||||
|
<PopconfirmModal
|
||||||
|
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||||
|
onConfirm={() => this.deleteInvitation(index)}
|
||||||
|
>
|
||||||
|
</PopconfirmModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const paginationProps = {
|
||||||
|
total: this.state.pagination.total,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Table scroll={{x: "max-content"}} columns={columns} dataSource={invitations} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
|
||||||
|
title={() => (
|
||||||
|
<div>
|
||||||
|
{i18next.t("general:Invitations")}
|
||||||
|
<Button type="primary" size="small" onClick={this.addInvitation.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
loading={this.state.loading}
|
||||||
|
onChange={this.handleTableChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch = (params = {}) => {
|
||||||
|
let field = params.searchedColumn, value = params.searchText;
|
||||||
|
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||||
|
if (params.type !== undefined && params.type !== null) {
|
||||||
|
field = "type";
|
||||||
|
value = params.type;
|
||||||
|
}
|
||||||
|
this.setState({loading: true});
|
||||||
|
InvitationBackend.getInvitations(Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.setState({
|
||||||
|
data: res.data,
|
||||||
|
pagination: {
|
||||||
|
...params.pagination,
|
||||||
|
total: res.data2,
|
||||||
|
},
|
||||||
|
searchText: params.searchText,
|
||||||
|
searchedColumn: params.searchedColumn,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (Setting.isResponseDenied(res)) {
|
||||||
|
this.setState({
|
||||||
|
isAuthorized: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InvitationListPage;
|
@@ -32,7 +32,6 @@ class PricingEditPage extends React.Component {
|
|||||||
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||||
pricingName: props.match.params.pricingName,
|
pricingName: props.match.params.pricingName,
|
||||||
organizations: [],
|
organizations: [],
|
||||||
application: null,
|
|
||||||
applications: [],
|
applications: [],
|
||||||
pricing: null,
|
pricing: null,
|
||||||
plans: [],
|
plans: [],
|
||||||
@@ -287,7 +286,7 @@ class PricingEditPage extends React.Component {
|
|||||||
<Col>
|
<Col>
|
||||||
<Button style={{marginBottom: "10px", marginTop: Setting.isMobile() ? "15px" : "0"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
<Button style={{marginBottom: "10px", marginTop: Setting.isMobile() ? "15px" : "0"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
||||||
copy(`${window.location.origin}${pricingUrl}`);
|
copy(`${window.location.origin}${pricingUrl}`);
|
||||||
Setting.showMessage("success", i18next.t("pricing:pricing page URL copied to clipboard successfully, please paste it into the incognito window or another browser"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18next.t("pricing:Copy pricing page URL")}
|
{i18next.t("pricing:Copy pricing page URL")}
|
||||||
|
@@ -29,6 +29,14 @@ import {CaptchaPreview} from "./common/CaptchaPreview";
|
|||||||
import {CountryCodeSelect} from "./common/select/CountryCodeSelect";
|
import {CountryCodeSelect} from "./common/select/CountryCodeSelect";
|
||||||
import * as Web3Auth from "./auth/Web3Auth";
|
import * as Web3Auth from "./auth/Web3Auth";
|
||||||
|
|
||||||
|
import {Controlled as CodeMirror} from "react-codemirror2";
|
||||||
|
import "codemirror/lib/codemirror.css";
|
||||||
|
|
||||||
|
require("codemirror/theme/material-darker.css");
|
||||||
|
require("codemirror/mode/htmlmixed/htmlmixed");
|
||||||
|
require("codemirror/mode/xml/xml");
|
||||||
|
require("codemirror/mode/css/css");
|
||||||
|
|
||||||
const {Option} = Select;
|
const {Option} = Select;
|
||||||
const {TextArea} = Input;
|
const {TextArea} = Input;
|
||||||
|
|
||||||
@@ -197,6 +205,12 @@ class ProviderEditPage extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
||||||
}
|
}
|
||||||
|
case "Storage":
|
||||||
|
if (provider.type === "Google Cloud Storage") {
|
||||||
|
return Setting.getLabel(i18next.t("provider:Service account JSON"), i18next.t("provider:Service account JSON - Tooltip"));
|
||||||
|
} else {
|
||||||
|
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
||||||
|
}
|
||||||
case "Email":
|
case "Email":
|
||||||
if (provider.type === "Azure ACS") {
|
if (provider.type === "Azure ACS") {
|
||||||
return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip"));
|
return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip"));
|
||||||
@@ -305,6 +319,9 @@ class ProviderEditPage extends React.Component {
|
|||||||
} else if (provider.type === "Infoflow") {
|
} else if (provider.type === "Infoflow") {
|
||||||
text = i18next.t("provider:Agent ID");
|
text = i18next.t("provider:Agent ID");
|
||||||
tooltip = i18next.t("provider:Agent ID - Tooltip");
|
tooltip = i18next.t("provider:Agent ID - Tooltip");
|
||||||
|
} else if (provider.type === "AzureADB2C") {
|
||||||
|
text = i18next.t("provider:User flow");
|
||||||
|
tooltip = i18next.t("provider:User flow - Tooltip");
|
||||||
}
|
}
|
||||||
} else if (provider.category === "SMS") {
|
} else if (provider.category === "SMS") {
|
||||||
if (provider.type === "Twilio SMS" || provider.type === "Azure ACS") {
|
if (provider.type === "Twilio SMS" || provider.type === "Azure ACS") {
|
||||||
@@ -471,7 +488,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
this.updateProviderField("port", 465);
|
this.updateProviderField("port", 465);
|
||||||
this.updateProviderField("disableSsl", false);
|
this.updateProviderField("disableSsl", false);
|
||||||
this.updateProviderField("title", "Casdoor Verification Code");
|
this.updateProviderField("title", "Casdoor Verification Code");
|
||||||
this.updateProviderField("content", "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes.");
|
this.updateProviderField("content", Setting.getDefaultHtmlEmailContent());
|
||||||
this.updateProviderField("receiver", this.props.account.email);
|
this.updateProviderField("receiver", this.props.account.email);
|
||||||
} else if (value === "SMS") {
|
} else if (value === "SMS") {
|
||||||
this.updateProviderField("type", "Twilio SMS");
|
this.updateProviderField("type", "Twilio SMS");
|
||||||
@@ -522,9 +539,12 @@ class ProviderEditPage extends React.Component {
|
|||||||
this.updateProviderField("customTokenUrl", "https://door.casdoor.com/api/login/oauth/access_token");
|
this.updateProviderField("customTokenUrl", "https://door.casdoor.com/api/login/oauth/access_token");
|
||||||
this.updateProviderField("customUserInfoUrl", "https://door.casdoor.com/api/userinfo");
|
this.updateProviderField("customUserInfoUrl", "https://door.casdoor.com/api/userinfo");
|
||||||
} else if (value === "Custom HTTP SMS") {
|
} else if (value === "Custom HTTP SMS") {
|
||||||
this.updateProviderField("endpoint", "https://example.com/send-custom-http");
|
this.updateProviderField("endpoint", "https://example.com/send-custom-http-sms");
|
||||||
this.updateProviderField("method", "GET");
|
this.updateProviderField("method", "GET");
|
||||||
this.updateProviderField("title", "code");
|
this.updateProviderField("title", "code");
|
||||||
|
} else if (value === "Custom HTTP Email") {
|
||||||
|
this.updateProviderField("endpoint", "https://example.com/send-custom-http-email");
|
||||||
|
this.updateProviderField("method", "POST");
|
||||||
} else if (value === "Custom HTTP") {
|
} else if (value === "Custom HTTP") {
|
||||||
this.updateProviderField("method", "GET");
|
this.updateProviderField("method", "GET");
|
||||||
this.updateProviderField("title", "");
|
this.updateProviderField("title", "");
|
||||||
@@ -676,6 +696,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
(this.state.provider.category === "Notification" && (this.state.provider.type === "Google Chat" || this.state.provider.type === "Custom HTTP")) ? null : (
|
(this.state.provider.category === "Notification" && (this.state.provider.type === "Google Chat" || this.state.provider.type === "Custom HTTP")) ? null : (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{
|
{
|
||||||
|
(this.state.provider.category === "Storage" && this.state.provider.type === "Google Cloud Storage") ||
|
||||||
(this.state.provider.category === "Email" && this.state.provider.type === "Azure ACS") ||
|
(this.state.provider.category === "Email" && this.state.provider.type === "Azure ACS") ||
|
||||||
(this.state.provider.category === "Notification" && (this.state.provider.type === "Line" || this.state.provider.type === "Telegram" || this.state.provider.type === "Bark" || this.state.provider.type === "Discord" || this.state.provider.type === "Slack" || this.state.provider.type === "Pushbullet" || this.state.provider.type === "Pushover" || this.state.provider.type === "Lark" || this.state.provider.type === "Microsoft Teams")) ? null : (
|
(this.state.provider.category === "Notification" && (this.state.provider.type === "Line" || this.state.provider.type === "Telegram" || this.state.provider.type === "Bark" || this.state.provider.type === "Discord" || this.state.provider.type === "Slack" || this.state.provider.type === "Pushbullet" || this.state.provider.type === "Pushover" || this.state.provider.type === "Lark" || this.state.provider.type === "Microsoft Teams")) ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
@@ -748,7 +769,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
|
this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "AzureADB2C" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={2}>
|
<Col style={{marginTop: "5px"}} span={2}>
|
||||||
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
||||||
@@ -761,7 +782,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{this.state.provider.category === "Storage" || this.state.provider.type === "Custom HTTP SMS" ? (
|
{this.state.provider.category === "Storage" || ["Custom HTTP SMS", "Custom HTTP Email"].includes(this.state.provider.type) ? (
|
||||||
<div>
|
<div>
|
||||||
{["Local File System"].includes(this.state.provider.type) ? null : (
|
{["Local File System"].includes(this.state.provider.type) ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
@@ -953,22 +974,47 @@ class ProviderEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("provider:Email content"), i18next.t("provider:Email content - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:Email content"), i18next.t("provider:Email content - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<TextArea autoSize={{minRows: 3, maxRows: 100}} value={this.state.provider.content} onChange={e => {
|
<Row style={{marginTop: "20px"}} >
|
||||||
this.updateProviderField("content", e.target.value);
|
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => this.updateProviderField("content", "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes.")} >
|
||||||
}} />
|
{i18next.t("provider:Reset to Default Text")}
|
||||||
|
</Button>
|
||||||
|
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary" onClick={() => this.updateProviderField("content", Setting.getDefaultHtmlEmailContent())} >
|
||||||
|
{i18next.t("provider:Reset to Default HTML")}
|
||||||
|
</Button>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col span={Setting.isMobile() ? 22 : 11}>
|
||||||
|
<div style={{height: "300px", margin: "10px"}}>
|
||||||
|
<CodeMirror
|
||||||
|
value={this.state.provider.content}
|
||||||
|
options={{mode: "htmlmixed", theme: "material-darker"}}
|
||||||
|
onBeforeChange={(editor, data, value) => {
|
||||||
|
this.updateProviderField("content", value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
<Col span={1} />
|
||||||
|
<Col span={Setting.isMobile() ? 22 : 11}>
|
||||||
|
<div style={{margin: "10px"}}>
|
||||||
|
<div dangerouslySetInnerHTML={{__html: this.state.provider.content.replace("%s", "123456").replace("%{user.friendlyName}", Setting.getFriendlyUserName(this.props.account))}} />
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}}>
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("provider:Test Email"), i18next.t("provider:Test Email - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:Test Email"), i18next.t("provider:Test Email - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={4} >
|
<Col span={4}>
|
||||||
<Input value={this.state.provider.receiver} placeholder = {i18next.t("user:Input your email")} onChange={e => {
|
<Input value={this.state.provider.receiver} placeholder={i18next.t("user:Input your email")}
|
||||||
this.updateProviderField("receiver", e.target.value);
|
onChange={e => {
|
||||||
}} />
|
this.updateProviderField("receiver", e.target.value);
|
||||||
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
{["Azure ACS"].includes(this.state.provider.type) ? null : (
|
{["Azure ACS"].includes(this.state.provider.type) ? null : (
|
||||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary" onClick={() => ProviderEditTestEmail.connectSmtpServer(this.state.provider)} >
|
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => ProviderEditTestEmail.connectSmtpServer(this.state.provider)} >
|
||||||
{i18next.t("provider:Test SMTP Connection")}
|
{i18next.t("provider:Test SMTP Connection")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -1010,7 +1056,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
!["Custom HTTP SMS"].includes(this.state.provider.type) ? null : (
|
!["Custom HTTP SMS", "Custom HTTP Email"].includes(this.state.provider.type) ? null : (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={2}>
|
<Col style={{marginTop: "5px"}} span={2}>
|
||||||
@@ -1150,7 +1196,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
<Col span={1}>
|
<Col span={1}>
|
||||||
<Button type="primary" onClick={() => {
|
<Button type="primary" onClick={() => {
|
||||||
copy(`${authConfig.serverUrl}/api/acs`);
|
copy(`${authConfig.serverUrl}/api/acs`);
|
||||||
Setting.showMessage("success", i18next.t("provider:Link copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}}>
|
}}>
|
||||||
{i18next.t("provider:Copy")}
|
{i18next.t("provider:Copy")}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -1166,7 +1212,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
<Col span={1}>
|
<Col span={1}>
|
||||||
<Button type="primary" onClick={() => {
|
<Button type="primary" onClick={() => {
|
||||||
copy(`${authConfig.serverUrl}/api/acs`);
|
copy(`${authConfig.serverUrl}/api/acs`);
|
||||||
Setting.showMessage("success", i18next.t("provider:Link copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}}>
|
}}>
|
||||||
{i18next.t("provider:Copy")}
|
{i18next.t("provider:Copy")}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -1195,7 +1241,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
(this.state.provider.type === "Alipay") ? (
|
(this.state.provider.type === "Alipay") ? (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("general:Root Cert"), i18next.t("general:Root Cert - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Root cert"), i18next.t("general:Root cert - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} style={{width: "100%"}} value={this.state.provider.metadata} onChange={(value => {this.updateProviderField("metadata", value);})}>
|
<Select virtual={false} style={{width: "100%"}} value={this.state.provider.metadata} onChange={(value => {this.updateProviderField("metadata", value);})}>
|
||||||
|
@@ -248,7 +248,7 @@ class ResourceListPage extends BaseListPage {
|
|||||||
<div>
|
<div>
|
||||||
<Button onClick={() => {
|
<Button onClick={() => {
|
||||||
copy(record.url);
|
copy(record.url);
|
||||||
Setting.showMessage("success", i18next.t("provider:Link copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18next.t("resource:Copy Link")}
|
{i18next.t("resource:Copy Link")}
|
||||||
|
@@ -169,6 +169,10 @@ export const OtherProviderInfo = {
|
|||||||
logo: `${StaticBaseUrl}/img/social_azure.png`,
|
logo: `${StaticBaseUrl}/img/social_azure.png`,
|
||||||
url: "https://learn.microsoft.com/zh-cn/azure/communication-services",
|
url: "https://learn.microsoft.com/zh-cn/azure/communication-services",
|
||||||
},
|
},
|
||||||
|
"Custom HTTP Email": {
|
||||||
|
logo: `${StaticBaseUrl}/img/social_default.png`,
|
||||||
|
url: "https://casdoor.org/docs/provider/email/overview",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Storage: {
|
Storage: {
|
||||||
"Local File System": {
|
"Local File System": {
|
||||||
@@ -920,7 +924,8 @@ export function getProviderTypeOptions(category) {
|
|||||||
{id: "Casdoor", name: "Casdoor"},
|
{id: "Casdoor", name: "Casdoor"},
|
||||||
{id: "Infoflow", name: "Infoflow"},
|
{id: "Infoflow", name: "Infoflow"},
|
||||||
{id: "Apple", name: "Apple"},
|
{id: "Apple", name: "Apple"},
|
||||||
{id: "AzureAD", name: "AzureAD"},
|
{id: "AzureAD", name: "Azure AD"},
|
||||||
|
{id: "AzureADB2C", name: "Azure AD B2C"},
|
||||||
{id: "Slack", name: "Slack"},
|
{id: "Slack", name: "Slack"},
|
||||||
{id: "Steam", name: "Steam"},
|
{id: "Steam", name: "Steam"},
|
||||||
{id: "Bilibili", name: "Bilibili"},
|
{id: "Bilibili", name: "Bilibili"},
|
||||||
@@ -985,6 +990,7 @@ export function getProviderTypeOptions(category) {
|
|||||||
{id: "SUBMAIL", name: "SUBMAIL"},
|
{id: "SUBMAIL", name: "SUBMAIL"},
|
||||||
{id: "Mailtrap", name: "Mailtrap"},
|
{id: "Mailtrap", name: "Mailtrap"},
|
||||||
{id: "Azure ACS", name: "Azure ACS"},
|
{id: "Azure ACS", name: "Azure ACS"},
|
||||||
|
{id: "Custom HTTP Email", name: "Custom HTTP Email"},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
} else if (category === "SMS") {
|
} else if (category === "SMS") {
|
||||||
@@ -1125,11 +1131,35 @@ export function renderLogo(application) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isPasswordEnabled(application) {
|
||||||
|
if (application) {
|
||||||
|
return application.signinMethods.filter(item => item.name === "Password").length > 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCodeSigninEnabled(application) {
|
||||||
|
if (application) {
|
||||||
|
return application.signinMethods.filter(item => item.name === "Verification code").length > 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWebAuthnEnabled(application) {
|
||||||
|
if (application) {
|
||||||
|
return application.signinMethods.filter(item => item.name === "WebAuthn").length > 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getLoginLink(application) {
|
export function getLoginLink(application) {
|
||||||
let url;
|
let url;
|
||||||
if (application === null) {
|
if (application === null) {
|
||||||
url = null;
|
url = null;
|
||||||
} else if (!application.enablePassword && window.location.pathname.includes("/auto-signup/oauth/authorize")) {
|
} else if (!isPasswordEnabled(application) && window.location.pathname.includes("/auto-signup/oauth/authorize")) {
|
||||||
url = window.location.href.replace("/auto-signup/oauth/authorize", "/login/oauth/authorize");
|
url = window.location.href.replace("/auto-signup/oauth/authorize", "/login/oauth/authorize");
|
||||||
} else if (authConfig.appName === application.name) {
|
} else if (authConfig.appName === application.name) {
|
||||||
url = "/login";
|
url = "/login";
|
||||||
@@ -1185,7 +1215,7 @@ export function renderSignupLink(application, text) {
|
|||||||
let url;
|
let url;
|
||||||
if (application === null) {
|
if (application === null) {
|
||||||
url = null;
|
url = null;
|
||||||
} else if (!application.enablePassword && window.location.pathname.includes("/login/oauth/authorize")) {
|
} else if (!isPasswordEnabled(application) && window.location.pathname.includes("/login/oauth/authorize")) {
|
||||||
url = window.location.href.replace("/login/oauth/authorize", "/auto-signup/oauth/authorize");
|
url = window.location.href.replace("/login/oauth/authorize", "/auto-signup/oauth/authorize");
|
||||||
} else if (authConfig.appName === application.name) {
|
} else if (authConfig.appName === application.name) {
|
||||||
url = "/signup";
|
url = "/signup";
|
||||||
@@ -1399,3 +1429,53 @@ export function getCurrencySymbol(currency) {
|
|||||||
return currency;
|
return currency;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFriendlyUserName(account) {
|
||||||
|
if (account.firstName !== "" && account.lastName !== "") {
|
||||||
|
return `${account.firstName}, ${account.lastName}`;
|
||||||
|
} else if (account.displayName !== "") {
|
||||||
|
return account.displayName;
|
||||||
|
} else if (account.name !== "") {
|
||||||
|
return account.name;
|
||||||
|
} else {
|
||||||
|
return account.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDefaultHtmlEmailContent() {
|
||||||
|
return `<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Verification Code Email</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; }
|
||||||
|
.email-container { width: 600px; margin: 0 auto; }
|
||||||
|
.header { text-align: center; }
|
||||||
|
.code { font-size: 24px; margin: 20px 0; text-align: center; }
|
||||||
|
.footer { font-size: 12px; text-align: center; margin-top: 50px; }
|
||||||
|
.footer a { color: #000; text-decoration: none; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="email-container">
|
||||||
|
<div class="header">
|
||||||
|
<h3>Casbin Organization</h3>
|
||||||
|
<img src="https://cdn.casbin.org/img/casdoor-logo_1185x256.png" alt="Casdoor Logo" width="300">
|
||||||
|
</div>
|
||||||
|
<p><strong>%{user.friendlyName}</strong>, here is your verification code</p>
|
||||||
|
<p>Use this code for your transaction. It's valid for 5 minutes</p>
|
||||||
|
<div class="code">
|
||||||
|
%s
|
||||||
|
</div>
|
||||||
|
<p>Thanks</p>
|
||||||
|
<p>Casbin Team</p>
|
||||||
|
<hr>
|
||||||
|
<div class="footer">
|
||||||
|
<p>Casdoor is a brand operated by Casbin organization. For more info please refer to <a href="https://casdoor.org">https://casdoor.org</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
}
|
||||||
|
@@ -280,12 +280,12 @@ class SubscriptionEditPage extends React.Component {
|
|||||||
this.updateSubscriptionField("state", value);
|
this.updateSubscriptionField("state", value);
|
||||||
})}
|
})}
|
||||||
options={[
|
options={[
|
||||||
{value: "Pending", name: i18next.t("permission:Pending")},
|
{value: "Pending", name: i18next.t("subscription:Pending")},
|
||||||
{value: "Active", name: i18next.t("permission:Active")},
|
{value: "Active", name: i18next.t("subscription:Active")},
|
||||||
{value: "Upcoming", name: i18next.t("permission:Upcoming")},
|
{value: "Upcoming", name: i18next.t("subscription:Upcoming")},
|
||||||
{value: "Expired", name: i18next.t("permission:Expired")},
|
{value: "Expired", name: i18next.t("subscription:Expired")},
|
||||||
{value: "Error", name: i18next.t("permission:Error")},
|
{value: "Error", name: i18next.t("subscription:Error")},
|
||||||
{value: "Suspended", name: i18next.t("permission:Suspended")},
|
{value: "Suspended", name: i18next.t("subscription:Suspended")},
|
||||||
].map((item) => Setting.getOption(item.name, item.value))}
|
].map((item) => Setting.getOption(item.name, item.value))}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
@@ -201,17 +201,17 @@ class SubscriptionListPage extends BaseListPage {
|
|||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
switch (text) {
|
switch (text) {
|
||||||
case "Pending":
|
case "Pending":
|
||||||
return Setting.getTag("processing", i18next.t("permission:Pending"), <ExclamationCircleOutlined />);
|
return Setting.getTag("processing", i18next.t("subscription:Pending"), <ExclamationCircleOutlined />);
|
||||||
case "Active":
|
case "Active":
|
||||||
return Setting.getTag("success", i18next.t("permission:Active"), <SyncOutlined spin />);
|
return Setting.getTag("success", i18next.t("subscription:Active"), <SyncOutlined spin />);
|
||||||
case "Upcoming":
|
case "Upcoming":
|
||||||
return Setting.getTag("warning", i18next.t("permission:Upcoming"), <ClockCircleOutlined />);
|
return Setting.getTag("warning", i18next.t("subscription:Upcoming"), <ClockCircleOutlined />);
|
||||||
case "Expired":
|
case "Expired":
|
||||||
return Setting.getTag("warning", i18next.t("permission:Expired"), <ClockCircleOutlined />);
|
return Setting.getTag("warning", i18next.t("subscription:Expired"), <ClockCircleOutlined />);
|
||||||
case "Error":
|
case "Error":
|
||||||
return Setting.getTag("error", i18next.t("permission:Error"), <CloseCircleOutlined />);
|
return Setting.getTag("error", i18next.t("subscription:Error"), <CloseCircleOutlined />);
|
||||||
case "Suspended":
|
case "Suspended":
|
||||||
return Setting.getTag("default", i18next.t("permission:Suspended"), <MinusCircleOutlined />);
|
return Setting.getTag("default", i18next.t("subscription:Suspended"), <MinusCircleOutlined />);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,10 @@ import {Button, Card, Col, Input, Row} from "antd";
|
|||||||
import * as TokenBackend from "./backend/TokenBackend";
|
import * as TokenBackend from "./backend/TokenBackend";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import copy from "copy-to-clipboard";
|
||||||
|
import {jwtDecode} from "jwt-decode";
|
||||||
|
|
||||||
|
const {TextArea} = Input;
|
||||||
|
|
||||||
class TokenEditPage extends React.Component {
|
class TokenEditPage extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -69,7 +73,20 @@ class TokenEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parseAccessToken(accessToken) {
|
||||||
|
try {
|
||||||
|
const parsedHeader = JSON.stringify(jwtDecode(accessToken, {header: true}), null, 2);
|
||||||
|
const parsedPayload = JSON.stringify(jwtDecode(accessToken), null, 2);
|
||||||
|
const res = parsedHeader + "." + parsedPayload;
|
||||||
|
return res;
|
||||||
|
} catch (error) {
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
renderToken() {
|
renderToken() {
|
||||||
|
const editorWidth = Setting.isMobile() ? 22 : 9;
|
||||||
|
const parsedResult = this.parseAccessToken(this.state.token.accessToken);
|
||||||
return (
|
return (
|
||||||
<Card size="small" title={
|
<Card size="small" title={
|
||||||
<div>
|
<div>
|
||||||
@@ -81,7 +98,7 @@ class TokenEditPage extends React.Component {
|
|||||||
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
||||||
<Row style={{marginTop: "10px"}} >
|
<Row style={{marginTop: "10px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{i18next.t("general:Name")}:
|
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input value={this.state.token.name} onChange={e => {
|
<Input value={this.state.token.name} onChange={e => {
|
||||||
@@ -91,7 +108,7 @@ class TokenEditPage extends React.Component {
|
|||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{i18next.t("general:Application")}:
|
{Setting.getLabel(i18next.t("general:Application"), i18next.t("general:Application - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input value={this.state.token.application} onChange={e => {
|
<Input value={this.state.token.application} onChange={e => {
|
||||||
@@ -101,7 +118,7 @@ class TokenEditPage extends React.Component {
|
|||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{i18next.t("general:Organization")}:
|
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input disabled={!Setting.isAdminUser(this.props.account)} value={this.state.token.organization} onChange={e => {
|
<Input disabled={!Setting.isAdminUser(this.props.account)} value={this.state.token.organization} onChange={e => {
|
||||||
@@ -111,7 +128,7 @@ class TokenEditPage extends React.Component {
|
|||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{i18next.t("general:User")}:
|
{Setting.getLabel(i18next.t("general:User"), i18next.t("general:User - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input value={this.state.token.user} onChange={e => {
|
<Input value={this.state.token.user} onChange={e => {
|
||||||
@@ -121,7 +138,7 @@ class TokenEditPage extends React.Component {
|
|||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{i18next.t("token:Authorization code")}:
|
{Setting.getLabel(i18next.t("token:Authorization code"), i18next.t("token:Authorization code - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input value={this.state.token.code} onChange={e => {
|
<Input value={this.state.token.code} onChange={e => {
|
||||||
@@ -131,17 +148,7 @@ class TokenEditPage extends React.Component {
|
|||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{i18next.t("token:Access token")}:
|
{Setting.getLabel(i18next.t("token:Expires in"), i18next.t("token:Expires in - Tooltip"))} :
|
||||||
</Col>
|
|
||||||
<Col span={22} >
|
|
||||||
<Input value={this.state.token.accessToken} onChange={e => {
|
|
||||||
this.updateTokenField("accessToken", e.target.value);
|
|
||||||
}} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{marginTop: "20px"}} >
|
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
|
||||||
{i18next.t("token:Expires in")}:
|
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input value={this.state.token.expiresIn} onChange={e => {
|
<Input value={this.state.token.expiresIn} onChange={e => {
|
||||||
@@ -151,7 +158,7 @@ class TokenEditPage extends React.Component {
|
|||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{i18next.t("provider:Scope")}:
|
{Setting.getLabel(i18next.t("provider:Scope"), i18next.t("provider:Scope - Tooltip"))}
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input value={this.state.token.scope} onChange={e => {
|
<Input value={this.state.token.scope} onChange={e => {
|
||||||
@@ -161,7 +168,7 @@ class TokenEditPage extends React.Component {
|
|||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{i18next.t("token:Token type")}:
|
{Setting.getLabel(i18next.t("token:Token type"), i18next.t("token:Token type - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input value={this.state.token.tokenType} onChange={e => {
|
<Input value={this.state.token.tokenType} onChange={e => {
|
||||||
@@ -169,6 +176,37 @@ class TokenEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("token:Access token"), i18next.t("token:Access token - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={editorWidth} >
|
||||||
|
<Button type="primary" style={{marginRight: "10px", marginBottom: "10px"}} disabled={this.state.token.accessToken === ""} onClick={() => {
|
||||||
|
copy(this.state.token.accessToken);
|
||||||
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18next.t("token:Copy access token")}
|
||||||
|
</Button>
|
||||||
|
<TextArea autoSize={{minRows: 10, maxRows: 200}} value={this.state.token.accessToken} onChange={e => {
|
||||||
|
this.updateTokenField("accessToken", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
<Col span={1} />
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("token:Parsed result"), i18next.t("token:Parsed result - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={editorWidth} >
|
||||||
|
<Button type="primary" style={{marginRight: "10px", marginBottom: "10px"}} disabled={!parsedResult.includes("\"alg\":")} onClick={() => {
|
||||||
|
copy(parsedResult);
|
||||||
|
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18next.t("token:Copy parsed result")}
|
||||||
|
</Button>
|
||||||
|
<TextArea autoSize={{minRows: 10, maxRows: 200}} value={parsedResult} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -325,6 +325,10 @@ class UserListPage extends BaseListPage {
|
|||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("tag"),
|
...this.getColumnSearchProps("tag"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
|
if (this.state.organization?.tags?.length === 0) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
const tagMap = {};
|
const tagMap = {};
|
||||||
this.state.organization?.tags?.map((tag, index) => {
|
this.state.organization?.tags?.map((tag, index) => {
|
||||||
const tokens = tag.split("|");
|
const tokens = tag.split("|");
|
||||||
|
32
web/src/auth/AzureADB2CLoginButton.js
Normal file
32
web/src/auth/AzureADB2CLoginButton.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import {createButton} from "react-social-login-buttons";
|
||||||
|
import {StaticBaseUrl} from "../Setting";
|
||||||
|
|
||||||
|
function Icon({width = 24, height = 24, color}) {
|
||||||
|
return <img src={`${StaticBaseUrl}/buttons/azuread.svg`} alt="Sign in with Azure AD B2C" style={{width: 24, height: 24}} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
text: "Sign in with Azure AD B2C",
|
||||||
|
icon: Icon,
|
||||||
|
iconFormat: name => `fa fa-${name}`,
|
||||||
|
style: {background: "#ffffff", color: "#000000"},
|
||||||
|
activeStyle: {background: "#ededee"},
|
||||||
|
};
|
||||||
|
|
||||||
|
const AzureADB2CLoginButton = createButton(config);
|
||||||
|
|
||||||
|
export default AzureADB2CLoginButton;
|
@@ -16,11 +16,11 @@ import {createButton} from "react-social-login-buttons";
|
|||||||
import {StaticBaseUrl} from "../Setting";
|
import {StaticBaseUrl} from "../Setting";
|
||||||
|
|
||||||
function Icon({width = 24, height = 24, color}) {
|
function Icon({width = 24, height = 24, color}) {
|
||||||
return <img src={`${StaticBaseUrl}/buttons/azuread.svg`} alt="Sign in with AzureAD" style={{width: 24, height: 24}} />;
|
return <img src={`${StaticBaseUrl}/buttons/azuread.svg`} alt="Sign in with Azure AD" style={{width: 24, height: 24}} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
text: "Sign in with AzureAD",
|
text: "Sign in with Azure AD",
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
iconFormat: name => `fa fa-${name}`,
|
iconFormat: name => `fa fa-${name}`,
|
||||||
style: {background: "#ffffff", color: "#000000"},
|
style: {background: "#ffffff", color: "#000000"},
|
||||||
|
@@ -201,19 +201,33 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDefaultLoginMethod(application) {
|
getDefaultLoginMethod(application) {
|
||||||
if (application?.enablePassword) {
|
if (application?.signinMethods.length > 0) {
|
||||||
return "password";
|
switch (application?.signinMethods[0].name) {
|
||||||
}
|
case "Password": return "password";
|
||||||
if (application?.enableCodeSignin) {
|
case "Verification code": {
|
||||||
return "verificationCode";
|
switch (application?.signinMethods[0].rule) {
|
||||||
}
|
case "All": return "verificationCode"; // All
|
||||||
if (application?.enableWebAuthn) {
|
case "Email only": return "verificationCodeEmail";
|
||||||
return "webAuthn";
|
case "Phone only": return "verificationCodePhone";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "WebAuthn": return "webAuthn";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "password";
|
return "password";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPlaceholder() {
|
||||||
|
switch (this.state.loginMethod) {
|
||||||
|
case "verificationCode": return i18next.t("login:Email or phone");
|
||||||
|
case "verificationCodeEmail": return i18next.t("login:Email");
|
||||||
|
case "verificationCodePhone": return i18next.t("login:Phone");
|
||||||
|
default: return i18next.t("login:username, Email or phone");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onUpdateAccount(account) {
|
onUpdateAccount(account) {
|
||||||
this.props.onUpdateAccount(account);
|
this.props.onUpdateAccount(account);
|
||||||
}
|
}
|
||||||
@@ -487,7 +501,7 @@ class LoginPage extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const showForm = application.enablePassword || application.enableCodeSignin || application.enableWebAuthn;
|
const showForm = Setting.isPasswordEnabled(application) || Setting.isCodeSigninEnabled(application) || Setting.isWebAuthnEnabled(application);
|
||||||
if (showForm) {
|
if (showForm) {
|
||||||
let loginWidth = 320;
|
let loginWidth = 320;
|
||||||
if (Setting.getLanguage() === "fr") {
|
if (Setting.getLanguage() === "fr") {
|
||||||
@@ -546,7 +560,13 @@ class LoginPage extends React.Component {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: i18next.t("login:Please input your Email or Phone!"),
|
message: () => {
|
||||||
|
switch (this.state.loginMethod) {
|
||||||
|
case "verificationCodeEmail": return i18next.t("login:Please input your Email!");
|
||||||
|
case "verificationCodePhone": return i18next.t("login:Please input your Phone!");
|
||||||
|
default: return i18next.t("login:Please input your Email or Phone!");
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
validator: (_, value) => {
|
validator: (_, value) => {
|
||||||
@@ -561,6 +581,19 @@ class LoginPage extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
this.setState({validEmail: false});
|
this.setState({validEmail: false});
|
||||||
}
|
}
|
||||||
|
} else if (this.state.loginMethod === "verificationCodeEmail") {
|
||||||
|
if (!Setting.isValidEmail(value)) {
|
||||||
|
this.setState({validEmail: false});
|
||||||
|
this.setState({validEmailOrPhone: false});
|
||||||
|
return Promise.reject(i18next.t("login:The input is not valid Email!"));
|
||||||
|
} else {
|
||||||
|
this.setState({validEmail: true});
|
||||||
|
}
|
||||||
|
} else if (this.state.loginMethod === "verificationCodePhone") {
|
||||||
|
if (!Setting.isValidPhone(value)) {
|
||||||
|
this.setState({validEmailOrPhone: false});
|
||||||
|
return Promise.reject(i18next.t("login:The input is not valid phone number!"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({validEmailOrPhone: true});
|
this.setState({validEmailOrPhone: true});
|
||||||
@@ -572,7 +605,7 @@ class LoginPage extends React.Component {
|
|||||||
<Input
|
<Input
|
||||||
id="input"
|
id="input"
|
||||||
prefix={<UserOutlined className="site-form-item-icon" />}
|
prefix={<UserOutlined className="site-form-item-icon" />}
|
||||||
placeholder={(this.state.loginMethod === "verificationCode") ? i18next.t("login:Email or phone") : i18next.t("login:username, Email or phone")}
|
placeholder={this.getPlaceholder()}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
this.setState({
|
this.setState({
|
||||||
username: e.target.value,
|
username: e.target.value,
|
||||||
@@ -842,12 +875,12 @@ class LoginPage extends React.Component {
|
|||||||
prefix={<LockOutlined className="site-form-item-icon" />}
|
prefix={<LockOutlined className="site-form-item-icon" />}
|
||||||
type="password"
|
type="password"
|
||||||
placeholder={i18next.t("general:Password")}
|
placeholder={i18next.t("general:Password")}
|
||||||
disabled={!application.enablePassword}
|
disabled={!Setting.isPasswordEnabled(application)}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
} else if (this.state.loginMethod === "verificationCode") {
|
} else if (this.state.loginMethod?.includes("verificationCode")) {
|
||||||
return (
|
return (
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -871,9 +904,26 @@ class LoginPage extends React.Component {
|
|||||||
renderMethodChoiceBox() {
|
renderMethodChoiceBox() {
|
||||||
const application = this.getApplicationObj();
|
const application = this.getApplicationObj();
|
||||||
const items = [];
|
const items = [];
|
||||||
application.enablePassword ? items.push({label: i18next.t("general:Password"), key: "password"}) : null;
|
|
||||||
application.enableCodeSignin ? items.push({label: i18next.t("login:Verification code"), key: "verificationCode"}) : null;
|
const generateItemKey = (name, rule) => {
|
||||||
application.enableWebAuthn ? items.push({label: i18next.t("login:WebAuthn"), key: "webAuthn"}) : null;
|
return `${name}-${rule}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const itemsMap = new Map([
|
||||||
|
[generateItemKey("Password", "None"), {label: i18next.t("general:Password"), key: "password"}],
|
||||||
|
[generateItemKey("Verification code", "All"), {label: i18next.t("login:Verification code"), key: "verificationCode"}],
|
||||||
|
[generateItemKey("Verification code", "Email only"), {label: i18next.t("login:Verification code"), key: "verificationCodeEmail"}],
|
||||||
|
[generateItemKey("Verification code", "Phone only"), {label: i18next.t("login:Verification code"), key: "verificationCodePhone"}],
|
||||||
|
[generateItemKey("WebAuthn", "None"), {label: i18next.t("login:WebAuthn"), key: "webAuthn"}],
|
||||||
|
]);
|
||||||
|
|
||||||
|
application?.signinMethods.forEach((signinMethod) => {
|
||||||
|
const item = itemsMap.get(generateItemKey(signinMethod.name, signinMethod.rule));
|
||||||
|
if (item) {
|
||||||
|
const label = signinMethod.name === signinMethod.displayName ? item.label : signinMethod.displayName;
|
||||||
|
items.push({label: label, key: item.key});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (items.length > 1) {
|
if (items.length > 1) {
|
||||||
return (
|
return (
|
||||||
@@ -1003,7 +1053,7 @@ class LoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const visibleOAuthProviderItems = (application.providers === null) ? [] : application.providers.filter(providerItem => this.isProviderVisible(providerItem));
|
const visibleOAuthProviderItems = (application.providers === null) ? [] : application.providers.filter(providerItem => this.isProviderVisible(providerItem));
|
||||||
if (this.props.preview !== "auto" && !application.enablePassword && !application.enableCodeSignin && !application.enableWebAuthn && visibleOAuthProviderItems.length === 1) {
|
if (this.props.preview !== "auto" && !Setting.isPasswordEnabled(application) && !Setting.isCodeSigninEnabled(application) && !Setting.isWebAuthnEnabled(application) && visibleOAuthProviderItems.length === 1) {
|
||||||
Setting.goToLink(Provider.getAuthUrl(application, visibleOAuthProviderItems[0].provider, "signup"));
|
Setting.goToLink(Provider.getAuthUrl(application, visibleOAuthProviderItems[0].provider, "signup"));
|
||||||
return (
|
return (
|
||||||
<div style={{display: "flex", justifyContent: "center", alignItems: "center", width: "100%"}}>
|
<div style={{display: "flex", justifyContent: "center", alignItems: "center", width: "100%"}}>
|
||||||
|
@@ -100,6 +100,10 @@ const authInfo = {
|
|||||||
scope: "user.read",
|
scope: "user.read",
|
||||||
endpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
endpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
||||||
},
|
},
|
||||||
|
AzureADB2C: {
|
||||||
|
scope: "openid",
|
||||||
|
endpoint: "https://tenant.b2clogin.com/tenant.onmicrosoft.com/userflow/oauth2/v2.0/authorize",
|
||||||
|
},
|
||||||
Slack: {
|
Slack: {
|
||||||
scope: "users:read",
|
scope: "users:read",
|
||||||
endpoint: "https://slack.com/oauth/authorize",
|
endpoint: "https://slack.com/oauth/authorize",
|
||||||
@@ -406,6 +410,8 @@ export function getAuthUrl(application, provider, method) {
|
|||||||
|| provider.type === "Twitch" || provider.type === "Typetalk" || provider.type === "Uber" || provider.type === "VK" || provider.type === "Wepay"
|
|| provider.type === "Twitch" || provider.type === "Typetalk" || provider.type === "Uber" || provider.type === "VK" || provider.type === "Wepay"
|
||||||
|| provider.type === "Xero" || provider.type === "Yahoo" || provider.type === "Yammer" || provider.type === "Yandex" || provider.type === "Zoom") {
|
|| provider.type === "Xero" || provider.type === "Yahoo" || provider.type === "Yammer" || provider.type === "Yandex" || provider.type === "Zoom") {
|
||||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
|
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
|
||||||
|
} else if (provider.type === "AzureADB2C") {
|
||||||
|
return `https://${provider.domain}.b2clogin.com/${provider.domain}.onmicrosoft.com/${provider.appId}/oauth2/v2.0/authorize?client_id=${provider.clientId}&nonce=defaultNonce&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${scope}&response_type=code&state=${state}&prompt=login`;
|
||||||
} else if (provider.type === "DingTalk") {
|
} else if (provider.type === "DingTalk") {
|
||||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&prompt=consent&state=${state}`;
|
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&prompt=consent&state=${state}`;
|
||||||
} else if (provider.type === "WeChat") {
|
} else if (provider.type === "WeChat") {
|
||||||
|
@@ -35,6 +35,7 @@ import AlipayLoginButton from "./AlipayLoginButton";
|
|||||||
import InfoflowLoginButton from "./InfoflowLoginButton";
|
import InfoflowLoginButton from "./InfoflowLoginButton";
|
||||||
import AppleLoginButton from "./AppleLoginButton";
|
import AppleLoginButton from "./AppleLoginButton";
|
||||||
import AzureADLoginButton from "./AzureADLoginButton";
|
import AzureADLoginButton from "./AzureADLoginButton";
|
||||||
|
import AzureADB2CLoginButton from "./AzureADB2CLoginButton";
|
||||||
import SlackLoginButton from "./SlackLoginButton";
|
import SlackLoginButton from "./SlackLoginButton";
|
||||||
import SteamLoginButton from "./SteamLoginButton";
|
import SteamLoginButton from "./SteamLoginButton";
|
||||||
import BilibiliLoginButton from "./BilibiliLoginButton";
|
import BilibiliLoginButton from "./BilibiliLoginButton";
|
||||||
@@ -85,6 +86,8 @@ function getSigninButton(provider) {
|
|||||||
return <AppleLoginButton text={text} align={"center"} />;
|
return <AppleLoginButton text={text} align={"center"} />;
|
||||||
} else if (provider.type === "AzureAD") {
|
} else if (provider.type === "AzureAD") {
|
||||||
return <AzureADLoginButton text={text} align={"center"} />;
|
return <AzureADLoginButton text={text} align={"center"} />;
|
||||||
|
} else if (provider.type === "AzureADB2C") {
|
||||||
|
return <AzureADB2CLoginButton text={text} align={"center"} />;
|
||||||
} else if (provider.type === "Slack") {
|
} else if (provider.type === "Slack") {
|
||||||
return <SlackLoginButton text={text} align={"center"} />;
|
return <SlackLoginButton text={text} align={"center"} />;
|
||||||
} else if (provider.type === "Steam") {
|
} else if (provider.type === "Steam") {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user