mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-14 08:03:23 +08:00
Compare commits
136 Commits
Author | SHA1 | Date | |
---|---|---|---|
b8db07db4d | |||
a681c267b3 | |||
5fb6ea0ab4 | |||
0f6b7984d4 | |||
ba9d6e5d78 | |||
a4524e9996 | |||
b469928780 | |||
dc6fe13f75 | |||
8227762988 | |||
d92b072ed0 | |||
1161310f81 | |||
48ba5f91ed | |||
53df2c2704 | |||
78066da208 | |||
60096468fe | |||
39d6bc10f7 | |||
177f2f2f11 | |||
79b393afee | |||
5bb12a30d4 | |||
fdb68bf9c8 | |||
37748850c8 | |||
8968396ae5 | |||
f5395f15f9 | |||
73e44df867 | |||
0b575ccf84 | |||
9b7f465a47 | |||
b1fe28fb83 | |||
530d054adb | |||
a2b9f9baaf | |||
a2d20fcb63 | |||
b118a3bb76 | |||
280867d0cb | |||
30fa2f7d81 | |||
518288691d | |||
ffa54247cd | |||
0199ad9aaa | |||
b9d171718f | |||
e841d0ba8e | |||
e5a9594f90 | |||
c542929835 | |||
86dea71efd | |||
9e536850fd | |||
fddd4a12b8 | |||
2d6fae32be | |||
741cff99df | |||
cad9c28e92 | |||
524cf4dda5 | |||
077a1cb8b7 | |||
00efdf1d03 | |||
aa543f1abb | |||
1d1d3049bd | |||
4f497d44a5 | |||
369de36987 | |||
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 | |||
dc06eb9948 | |||
b068202e74 | |||
cb16567c7b | |||
4eb725d47a | |||
ce72a172b0 | |||
5521962e0c | |||
37b8b09cc0 | |||
482eb61168 | |||
8819a8697b | |||
85cb68eb66 | |||
b25b5f0249 | |||
947dcf6e75 | |||
113c27db73 | |||
badfe34755 | |||
a5f9f61381 | |||
2ce8c93ead | |||
da41ac7275 | |||
fd0c70a827 | |||
c4a6f07672 | |||
a67f541171 | |||
192968bac8 | |||
23d4488b64 | |||
23f4684e1d | |||
1a91e7b0f9 | |||
811999b6cc | |||
7786018051 | |||
6c72f86d03 | |||
5b151f4ec4 | |||
e9b7d1266f | |||
2d4998228c | |||
d3ed6c348b | |||
a22e05dcc1 | |||
0ac2b69f5a | |||
d090e9c860 | |||
8ebb158765 | |||
ea2f053630 | |||
988b14c6b5 | |||
a9e72ac3cb | |||
498cd02d49 | |||
a389842f59 | |||
6c69daa666 | |||
53c89bbe89 | |||
9442aa9f7a | |||
8a195715d0 | |||
b985bab3f3 | |||
477a090aa0 | |||
e082cf10e0 |
48
.github/workflows/build.yml
vendored
48
.github/workflows/build.yml
vendored
@ -1,6 +1,6 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
on: [ push, pull_request ]
|
||||
|
||||
jobs:
|
||||
|
||||
@ -35,7 +35,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: ./web/yarn.lock
|
||||
- run: yarn install && CI=false yarn run build
|
||||
@ -101,7 +101,7 @@ jobs:
|
||||
working-directory: ./
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: ./web/yarn.lock
|
||||
- run: yarn install
|
||||
@ -127,7 +127,7 @@ jobs:
|
||||
release-and-push:
|
||||
name: Release And Push
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push'
|
||||
needs: [ frontend, backend, linter, e2e ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
@ -137,7 +137,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- name: Fetch Previous version
|
||||
id: get-previous-tag
|
||||
@ -167,10 +167,8 @@ jobs:
|
||||
elif [ ${old_array[1]} != ${new_array[1]} ]
|
||||
then
|
||||
echo ::set-output name=push::'true'
|
||||
|
||||
else
|
||||
echo ::set-output name=push::'false'
|
||||
|
||||
fi
|
||||
|
||||
- name: Set up QEMU
|
||||
@ -184,14 +182,14 @@ jobs:
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Push to Docker Hub
|
||||
uses: docker/build-push-action@v3
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
with:
|
||||
context: .
|
||||
target: STANDARD
|
||||
@ -201,10 +199,40 @@ jobs:
|
||||
|
||||
- name: Push All In One Version to Docker Hub
|
||||
uses: docker/build-push-action@v3
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||
with:
|
||||
context: .
|
||||
target: ALLINONE
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: casbin/casdoor-all-in-one:${{steps.get-current-tag.outputs.tag }},casbin/casdoor-all-in-one:latest
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
if: steps.should_push.outputs.push=='true'
|
||||
with:
|
||||
repository: casdoor/casdoor-helm
|
||||
ref: 'master'
|
||||
token: ${{ secrets.GH_BOT_TOKEN }}
|
||||
|
||||
- name: Update Helm Chart
|
||||
if: steps.should_push.outputs.push=='true'
|
||||
run: |
|
||||
# Set the appVersion and version of the chart to the current tag
|
||||
sed -i "s/appVersion: .*/appVersion: ${{steps.get-current-tag.outputs.tag }}/g" ./charts/casdoor/Chart.yaml
|
||||
sed -i "s/version: .*/version: ${{steps.get-current-tag.outputs.tag }}/g" ./charts/casdoor/Chart.yaml
|
||||
|
||||
REGISTRY=oci://registry-1.docker.io/casbin
|
||||
cd charts/casdoor
|
||||
helm package .
|
||||
PKG_NAME=$(ls *.tgz)
|
||||
helm repo index . --url $REGISTRY --merge index.yaml
|
||||
helm push $PKG_NAME $REGISTRY
|
||||
rm $PKG_NAME
|
||||
|
||||
# Commit and push the changes back to the repository
|
||||
git config --global user.name "casbin-bot"
|
||||
git config --global user.email "bot@casbin.org"
|
||||
git add Chart.yaml index.yaml
|
||||
git commit -m "chore(helm): bump helm charts appVersion to ${{steps.get-current-tag.outputs.tag }}"
|
||||
git tag ${{steps.get-current-tag.outputs.tag }}
|
||||
git push origin HEAD:master --follow-tags
|
||||
|
2
.github/workflows/sync.yml
vendored
2
.github/workflows/sync.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
synchronize-with-crowdin:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push'
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -18,7 +18,7 @@ bin/
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
.vscode/
|
||||
.vscode/settings.json
|
||||
|
||||
tmp/
|
||||
tmpFiles/
|
||||
@ -31,3 +31,6 @@ commentsRouter*.go
|
||||
# ignore build result
|
||||
casdoor
|
||||
server
|
||||
|
||||
# include helm-chart
|
||||
!manifests/casdoor
|
||||
|
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"debugAdapter": "dlv-dap",
|
||||
"args": ["--createDatabase=true"]
|
||||
}
|
||||
]
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
FROM node:16.18.0 AS FRONT
|
||||
FROM node:18.19.0 AS FRONT
|
||||
WORKDIR /web
|
||||
COPY ./web .
|
||||
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
|
||||
COPY . .
|
||||
RUN ./build.sh
|
||||
|
3
Makefile
3
Makefile
@ -86,6 +86,9 @@ docker-build: ## Build docker image with the manager.
|
||||
docker-push: ## Push docker image with the manager.
|
||||
docker push ${REGISTRY}/${IMG}:${IMG_TAG}
|
||||
|
||||
deps: ## Run dependencies for local development
|
||||
docker compose up -d db
|
||||
|
||||
lint-install: ## Install golangci-lint
|
||||
@# The following installs a specific version of golangci-lint, which is appropriate for a CI server to avoid different results from build to build
|
||||
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.40.1
|
||||
|
17
README.md
17
README.md
@ -1,5 +1,5 @@
|
||||
<h1 align="center" style="border-bottom: none;">📦⚡️ Casdoor</h1>
|
||||
<h3 align="center">A UI-first centralized authentication / Single-Sign-On (SSO) platform based on OAuth 2.0 / OIDC.</h3>
|
||||
<h3 align="center">An open-source UI-first Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML, CAS, LDAP, SCIM, WebAuthn, TOTP, MFA and RADIUS</h3>
|
||||
<p align="center">
|
||||
<a href="#badge">
|
||||
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
|
||||
@ -42,6 +42,20 @@
|
||||
</a>
|
||||
</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
|
||||
|
||||
- Read-only site: https://door.casdoor.com (any modification operation will fail)
|
||||
@ -55,6 +69,7 @@ https://casdoor.org
|
||||
|
||||
- By source code: https://casdoor.org/docs/basic/server-installation
|
||||
- By Docker: https://casdoor.org/docs/basic/try-with-docker
|
||||
- By Kubernetes Helm: https://casdoor.org/docs/basic/try-with-helm
|
||||
|
||||
## How to connect to Casdoor?
|
||||
|
||||
|
@ -92,11 +92,14 @@ p, *, *, GET, /api/get-plan, *, *
|
||||
p, *, *, GET, /api/get-subscription, *, *
|
||||
p, *, *, GET, /api/get-provider, *, *
|
||||
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)
|
||||
// load all rules from string adapter to enforcer's memory
|
||||
err := sa.LoadPolicy(Enforcer.GetModel())
|
||||
err = sa.LoadPolicy(Enforcer.GetModel())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -147,11 +150,11 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
|
||||
|
||||
func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
||||
if method == "POST" {
|
||||
if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/callback" || urlPath == "/api/send-verification-code" || urlPath == "/api/send-email" || urlPath == "/api/verify-captcha" {
|
||||
if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/callback" || urlPath == "/api/send-verification-code" || urlPath == "/api/send-email" || urlPath == "/api/verify-captcha" || urlPath == "/api/verify-code" || urlPath == "/api/check-user-password" || strings.HasPrefix(urlPath, "/api/mfa/") {
|
||||
return true
|
||||
} else if urlPath == "/api/update-user" {
|
||||
// Allow ordinary users to update their own information
|
||||
if subOwner == objOwner && subName == objName && !(subOwner == "built-in" && subName == "admin") {
|
||||
if (subOwner == objOwner && subName == objName || subOwner == "app") && !(subOwner == "built-in" && subName == "admin") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -56,6 +56,17 @@ type Captcha struct {
|
||||
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
|
||||
// @Tag Login API
|
||||
// @Title Signup
|
||||
@ -238,7 +249,7 @@ func (c *ApiController) Signup() {
|
||||
// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri"
|
||||
// @Param state query string false "state"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /logout [get,post]
|
||||
// @router /logout [post]
|
||||
func (c *ApiController) Logout() {
|
||||
// https://openid.net/specs/openid-connect-rpinitiated-1_0-final.html
|
||||
accessToken := c.Input().Get("id_token_hint")
|
||||
@ -282,17 +293,15 @@ func (c *ApiController) Logout() {
|
||||
return
|
||||
}
|
||||
|
||||
affected, application, token, err := object.ExpireTokenByAccessToken(accessToken)
|
||||
_, application, token, err := object.ExpireTokenByAccessToken(accessToken)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !affected {
|
||||
if token == nil {
|
||||
c.ResponseError(c.T("token:Token not found, invalid accessToken"))
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist")), token.Application)
|
||||
return
|
||||
@ -319,7 +328,15 @@ func (c *ApiController) Logout() {
|
||||
return
|
||||
} else {
|
||||
if application.IsRedirectUriValid(redirectUri) {
|
||||
c.Ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?state=%s", strings.TrimRight(redirectUri, "/"), state))
|
||||
redirectUrl := redirectUri
|
||||
if state != "" {
|
||||
if strings.Contains(redirectUri, "?") {
|
||||
redirectUrl = fmt.Sprintf("%s&state=%s", strings.TrimSuffix(redirectUri, "/"), state)
|
||||
} else {
|
||||
redirectUrl = fmt.Sprintf("%s?state=%s", strings.TrimSuffix(redirectUri, "/"), state)
|
||||
}
|
||||
}
|
||||
c.Ctx.Redirect(http.StatusFound, redirectUrl)
|
||||
} else {
|
||||
c.ResponseError(fmt.Sprintf(c.T("token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri))
|
||||
return
|
||||
@ -412,7 +429,7 @@ func (c *ApiController) GetUserinfo() {
|
||||
// @Title UserInfo2
|
||||
// @Tag Account API
|
||||
// @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]
|
||||
func (c *ApiController) GetUserinfo2() {
|
||||
user, ok := c.RequireSignedInUser()
|
||||
@ -420,17 +437,6 @@ func (c *ApiController) GetUserinfo2() {
|
||||
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{
|
||||
Id: user.Id,
|
||||
Name: user.Name,
|
||||
@ -447,7 +453,8 @@ func (c *ApiController) GetUserinfo2() {
|
||||
// GetCaptcha ...
|
||||
// @Tag Login API
|
||||
// @Title GetCaptcha
|
||||
// @router /api/get-captcha [get]
|
||||
// @router /get-captcha [get]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) GetCaptcha() {
|
||||
applicationId := c.Input().Get("applicationId")
|
||||
isCurrentProvider := c.Input().Get("isCurrentProvider")
|
||||
@ -473,7 +480,7 @@ func (c *ApiController) GetCaptcha() {
|
||||
Type: captchaProvider.Type,
|
||||
SubType: captchaProvider.SubType,
|
||||
ClientId: captchaProvider.ClientId,
|
||||
ClientSecret: captchaProvider.ClientSecret,
|
||||
ClientSecret: "***",
|
||||
ClientId2: captchaProvider.ClientId2,
|
||||
ClientSecret2: captchaProvider.ClientSecret2,
|
||||
})
|
||||
|
@ -173,6 +173,12 @@ func (c *ApiController) GetOrganizationApplications() {
|
||||
return
|
||||
}
|
||||
|
||||
applications, err = object.GetAllowedApplications(applications, userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedApplications(applications, userId))
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -154,7 +155,8 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
resp = &Response{Status: "error", Msg: fmt.Sprintf("error: grant_type: %s is not supported in this application", form.Type), Data: ""}
|
||||
} else {
|
||||
scope := c.Input().Get("scope")
|
||||
token, _ := object.GetTokenByUser(application, user, scope, c.Ctx.Request.Host)
|
||||
nonce := c.Input().Get("nonce")
|
||||
token, _ := object.GetTokenByUser(application, user, scope, nonce, c.Ctx.Request.Host)
|
||||
resp = tokenToResponse(token)
|
||||
}
|
||||
} else if form.Type == ResponseTypeSaml { // saml flow
|
||||
@ -220,7 +222,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
// @Param redirectUri query string true "redirect uri"
|
||||
// @Param scope query string true "scope"
|
||||
// @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]
|
||||
func (c *ApiController) GetApplicationLogin() {
|
||||
clientId := c.Input().Get("clientId")
|
||||
@ -331,8 +333,6 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
var user *object.User
|
||||
var msg string
|
||||
|
||||
if authForm.Password == "" {
|
||||
if user, err = object.GetUserByFields(authForm.Organization, authForm.Username); err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
@ -342,7 +342,28 @@ func (c *ApiController) Login() {
|
||||
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)
|
||||
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
|
||||
if verificationCodeType == object.VerifyTypePhone {
|
||||
authForm.CountryCode = user.GetCountryCode(authForm.CountryCode)
|
||||
@ -354,20 +375,21 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
// check result through Email or Phone
|
||||
checkResult := object.CheckSigninCode(user, checkDest, authForm.Code, c.GetAcceptLanguage())
|
||||
if len(checkResult) != 0 {
|
||||
c.ResponseError(fmt.Sprintf("%s - %s", verificationCodeType, checkResult))
|
||||
err = object.CheckSigninCode(user, checkDest, authForm.Code, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(fmt.Sprintf("%s - %s", verificationCodeType, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// disable the verification code
|
||||
err := object.DisableVerificationCode(checkDest)
|
||||
err = object.DisableVerificationCode(checkDest)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
var application *object.Application
|
||||
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
@ -377,16 +399,31 @@ func (c *ApiController) Login() {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||
return
|
||||
}
|
||||
if !application.EnablePassword {
|
||||
if authForm.SigninMethod == "Password" && !application.IsPasswordEnabled() {
|
||||
c.ResponseError(c.T("auth:The login method: login with password is not enabled for the application"))
|
||||
return
|
||||
}
|
||||
if authForm.SigninMethod == "LDAP" && !application.IsLdapEnabled() {
|
||||
c.ResponseError(c.T("auth:The login method: login with LDAP is not enabled for the application"))
|
||||
return
|
||||
}
|
||||
var enableCaptcha bool
|
||||
if enableCaptcha, err = object.CheckToEnableCaptcha(application, authForm.Organization, authForm.Username); err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
} else if enableCaptcha {
|
||||
isHuman, err := captcha.VerifyCaptchaByCaptchaType(authForm.CaptchaType, authForm.CaptchaToken, authForm.ClientSecret)
|
||||
captchaProvider, err := object.GetCaptchaProviderByApplication(util.GetId(application.Owner, application.Name), "false", c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if captchaProvider.Type != "Default" {
|
||||
authForm.ClientSecret = captchaProvider.ClientSecret
|
||||
}
|
||||
|
||||
var isHuman bool
|
||||
isHuman, err = captcha.VerifyCaptchaByCaptchaType(authForm.CaptchaType, authForm.CaptchaToken, authForm.ClientSecret)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -399,13 +436,22 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
password := authForm.Password
|
||||
user, msg = object.CheckUserPassword(authForm.Organization, authForm.Username, password, c.GetAcceptLanguage(), enableCaptcha)
|
||||
isSigninViaLdap := authForm.SigninMethod == "LDAP"
|
||||
var isPasswordWithLdapEnabled bool
|
||||
if authForm.SigninMethod == "Password" {
|
||||
isPasswordWithLdapEnabled = application.IsPasswordWithLdapEnabled()
|
||||
} else {
|
||||
isPasswordWithLdapEnabled = false
|
||||
}
|
||||
user, err = object.CheckUserPassword(authForm.Organization, authForm.Username, password, c.GetAcceptLanguage(), enableCaptcha, isSigninViaLdap, isPasswordWithLdapEnabled)
|
||||
}
|
||||
|
||||
if msg != "" {
|
||||
resp = &Response{Status: "error", Msg: msg}
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
} else {
|
||||
application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
var application *object.Application
|
||||
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -416,7 +462,8 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
|
||||
organization, err := object.GetOrganizationByUser(user)
|
||||
var organization *object.Organization
|
||||
organization, err = object.GetOrganizationByUser(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
}
|
||||
@ -461,12 +508,15 @@ func (c *ApiController) Login() {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||
return
|
||||
}
|
||||
organization, err := object.GetOrganization(util.GetId("admin", application.Organization))
|
||||
|
||||
var organization *object.Organization
|
||||
organization, err = object.GetOrganization(util.GetId("admin", application.Organization))
|
||||
if err != nil {
|
||||
c.ResponseError(c.T(err.Error()))
|
||||
}
|
||||
|
||||
provider, err := object.GetProvider(util.GetId("admin", authForm.Provider))
|
||||
var provider *object.Provider
|
||||
provider, err = object.GetProvider(util.GetId("admin", authForm.Provider))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -488,7 +538,8 @@ func (c *ApiController) Login() {
|
||||
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
||||
// OAuth
|
||||
idpInfo := object.FromProviderToIdpInfo(c.Ctx, provider)
|
||||
idProvider, err := idp.GetIdProvider(idpInfo, authForm.RedirectUri)
|
||||
var idProvider idp.IdProvider
|
||||
idProvider, err = idp.GetIdProvider(idpInfo, authForm.RedirectUri)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -506,7 +557,8 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
// https://github.com/golang/oauth2/issues/123#issuecomment-103715338
|
||||
token, err := idProvider.GetToken(authForm.Code)
|
||||
var token *oauth2.Token
|
||||
token, err = idProvider.GetToken(authForm.Code)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -547,7 +599,12 @@ func (c *ApiController) Login() {
|
||||
if user.IsForbidden {
|
||||
c.ResponseError(c.T("check:The user is forbidden to sign in, please contact the administrator"))
|
||||
}
|
||||
|
||||
// sync info from 3rd-party if possible
|
||||
_, err = object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||
|
||||
record := object.NewRecord(c.Ctx)
|
||||
@ -588,14 +645,16 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
// Handle username conflicts
|
||||
tmpUser, err := object.GetUser(util.GetId(application.Organization, userInfo.Username))
|
||||
var tmpUser *object.User
|
||||
tmpUser, err = object.GetUser(util.GetId(application.Organization, userInfo.Username))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if tmpUser != nil {
|
||||
uid, err := uuid.NewRandom()
|
||||
var uid uuid.UUID
|
||||
uid, err = uuid.NewRandom()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -606,14 +665,16 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
properties := map[string]string{}
|
||||
count, err := object.GetUserCount(application.Organization, "", "", "")
|
||||
var count int64
|
||||
count, err = object.GetUserCount(application.Organization, "", "", "")
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
properties["no"] = strconv.Itoa(int(count + 2))
|
||||
initScore, err := organization.GetInitScore()
|
||||
var initScore int
|
||||
initScore, err = organization.GetInitScore()
|
||||
if err != nil {
|
||||
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
||||
return
|
||||
@ -645,7 +706,8 @@ func (c *ApiController) Login() {
|
||||
Properties: properties,
|
||||
}
|
||||
|
||||
affected, err := object.AddUser(user)
|
||||
var affected bool
|
||||
affected, err = object.AddUser(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -667,7 +729,7 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
// sync info from 3rd-party if possible
|
||||
_, err := object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
|
||||
_, err = object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -703,7 +765,8 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
|
||||
oldUser, err := object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
|
||||
var oldUser *object.User
|
||||
oldUser, err = object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -714,7 +777,8 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := object.GetUser(userId)
|
||||
var user *object.User
|
||||
user, err = object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -727,7 +791,8 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
|
||||
isLinked, err := object.LinkUserAccount(user, provider.Type, userInfo.Id)
|
||||
var isLinked bool
|
||||
isLinked, err = object.LinkUserAccount(user, provider.Type, userInfo.Id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -740,7 +805,8 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
}
|
||||
} else if c.getMfaUserSession() != "" {
|
||||
user, err := object.GetUser(c.getMfaUserSession())
|
||||
var user *object.User
|
||||
user, err = object.GetUser(c.getMfaUserSession())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -773,7 +839,8 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
|
||||
application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
var application *object.Application
|
||||
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -794,7 +861,8 @@ func (c *ApiController) Login() {
|
||||
} else {
|
||||
if c.GetSessionUsername() != "" {
|
||||
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
|
||||
application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
var application *object.Application
|
||||
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -848,9 +916,10 @@ func (c *ApiController) HandleSamlLogin() {
|
||||
}
|
||||
|
||||
// HandleOfficialAccountEvent ...
|
||||
// @Tag HandleOfficialAccountEvent API
|
||||
// @Tag System API
|
||||
// @Title HandleOfficialAccountEvent
|
||||
// @router /api/webhook [POST]
|
||||
// @router /webhook [POST]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) HandleOfficialAccountEvent() {
|
||||
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
@ -878,9 +947,10 @@ func (c *ApiController) HandleOfficialAccountEvent() {
|
||||
}
|
||||
|
||||
// GetWebhookEventType ...
|
||||
// @Tag GetWebhookEventType API
|
||||
// @Tag System API
|
||||
// @Title GetWebhookEventType
|
||||
// @router /api/get-webhook-event [GET]
|
||||
// @router /get-webhook-event [GET]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) GetWebhookEventType() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
@ -900,20 +970,30 @@ func (c *ApiController) GetWebhookEventType() {
|
||||
// @Description Get Login Error Counts
|
||||
// @Param id query string true "The id ( owner/name ) of user"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /api/get-captcha-status [get]
|
||||
// @router /get-captcha-status [get]
|
||||
func (c *ApiController) GetCaptchaStatus() {
|
||||
organization := c.Input().Get("organization")
|
||||
userId := c.Input().Get("user_id")
|
||||
userId := c.Input().Get("userId")
|
||||
user, err := object.GetUserByFields(organization, userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var captchaEnabled bool
|
||||
if user != nil && user.SigninWrongTimes >= object.SigninWrongTimesLimit {
|
||||
captchaEnabled = true
|
||||
captchaEnabled := false
|
||||
if user != nil {
|
||||
var failedSigninLimit int
|
||||
failedSigninLimit, _, err = object.GetFailedSigninConfigByUser(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if user.SigninWrongTimes >= failedSigninLimit {
|
||||
captchaEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
c.ResponseOk(captchaEnabled)
|
||||
}
|
||||
|
||||
@ -921,7 +1001,8 @@ func (c *ApiController) GetCaptchaStatus() {
|
||||
// @Title Callback
|
||||
// @Tag Callback API
|
||||
// @Description Get Login Error Counts
|
||||
// @router /api/Callback [post]
|
||||
// @router /Callback [post]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) Callback() {
|
||||
code := c.GetString("code")
|
||||
state := c.GetString("state")
|
||||
|
@ -16,6 +16,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
@ -23,12 +24,13 @@ import (
|
||||
|
||||
// Enforce
|
||||
// @Title Enforce
|
||||
// @Tag Enforce API
|
||||
// @Tag Enforcer 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 modelId query string false "model id"
|
||||
// @Param resourceId query string false "resource id"
|
||||
// @Param owner query string false "owner"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /enforce [post]
|
||||
func (c *ApiController) Enforce() {
|
||||
@ -36,13 +38,14 @@ func (c *ApiController) Enforce() {
|
||||
modelId := c.Input().Get("modelId")
|
||||
resourceId := c.Input().Get("resourceId")
|
||||
enforcerId := c.Input().Get("enforcerId")
|
||||
owner := c.Input().Get("owner")
|
||||
|
||||
if len(c.Ctx.Input.RequestBody) == 0 {
|
||||
c.ResponseError("The request body should not be empty")
|
||||
return
|
||||
}
|
||||
|
||||
var request object.CasbinRequest
|
||||
var request []string
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@ -56,13 +59,22 @@ func (c *ApiController) Enforce() {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := enforcer.Enforce(request...)
|
||||
res := []bool{}
|
||||
keyRes := []string{}
|
||||
|
||||
// type transformation
|
||||
interfaceRequest := util.StringToInterfaceArray(request)
|
||||
|
||||
enforceResult, err := enforcer.Enforce(interfaceRequest...)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
res = append(res, enforceResult)
|
||||
keyRes = append(keyRes, enforcer.GetModelAndAdapter())
|
||||
|
||||
c.ResponseOk(res, keyRes)
|
||||
return
|
||||
}
|
||||
|
||||
@ -72,22 +84,24 @@ func (c *ApiController) Enforce() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res := []bool{}
|
||||
|
||||
if permission == nil {
|
||||
res = append(res, false)
|
||||
} else {
|
||||
enforceResult, err := object.Enforce(permission, &request)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res = append(res, enforceResult)
|
||||
c.ResponseError(fmt.Sprintf("permission: %s doesn't exist", permissionId))
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -105,48 +119,53 @@ func (c *ApiController) Enforce() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
} else if owner != "" {
|
||||
permissions, err = object.GetPermissions(owner)
|
||||
} else {
|
||||
c.ResponseError(c.T("general:Missing parameter"))
|
||||
return
|
||||
}
|
||||
|
||||
res := []bool{}
|
||||
|
||||
keyRes := []string{}
|
||||
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
||||
for _, permissionIds := range listPermissionIdMap {
|
||||
for key, permissionIds := range listPermissionIdMap {
|
||||
firstPermission, err := object.GetPermission(permissionIds[0])
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
enforceResult, err := object.Enforce(firstPermission, &request, permissionIds...)
|
||||
enforceResult, err := object.Enforce(firstPermission, request, permissionIds...)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res = append(res, enforceResult)
|
||||
keyRes = append(keyRes, key)
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
c.ResponseOk(res, keyRes)
|
||||
}
|
||||
|
||||
// BatchEnforce
|
||||
// @Title BatchEnforce
|
||||
// @Tag Enforce API
|
||||
// @Tag Enforcer 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 modelId query string false "model id"
|
||||
// @Param owner query string false "owner"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /batch-enforce [post]
|
||||
func (c *ApiController) BatchEnforce() {
|
||||
permissionId := c.Input().Get("permissionId")
|
||||
modelId := c.Input().Get("modelId")
|
||||
enforcerId := c.Input().Get("enforcerId")
|
||||
owner := c.Input().Get("owner")
|
||||
|
||||
var requests []object.CasbinRequest
|
||||
var requests [][]string
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@ -160,13 +179,22 @@ func (c *ApiController) BatchEnforce() {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := enforcer.BatchEnforce(requests)
|
||||
res := [][]bool{}
|
||||
keyRes := []string{}
|
||||
|
||||
// type transformation
|
||||
interfaceRequests := util.StringToInterfaceArray2d(requests)
|
||||
|
||||
enforceResult, err := enforcer.BatchEnforce(interfaceRequests)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
res = append(res, enforceResult)
|
||||
keyRes = append(keyRes, enforcer.GetModelAndAdapter())
|
||||
|
||||
c.ResponseOk(res, keyRes)
|
||||
return
|
||||
}
|
||||
|
||||
@ -176,28 +204,24 @@ func (c *ApiController) BatchEnforce() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res := [][]bool{}
|
||||
|
||||
if permission == nil {
|
||||
l := len(requests)
|
||||
resRequest := make([]bool, l)
|
||||
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.ResponseError(fmt.Sprintf("permission: %s doesn't exist", permissionId))
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -209,13 +233,15 @@ func (c *ApiController) BatchEnforce() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
} else if owner != "" {
|
||||
permissions, err = object.GetPermissions(owner)
|
||||
} else {
|
||||
c.ResponseError(c.T("general:Missing parameter"))
|
||||
return
|
||||
}
|
||||
|
||||
res := [][]bool{}
|
||||
|
||||
keyRes := []string{}
|
||||
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
||||
for _, permissionIds := range listPermissionIdMap {
|
||||
firstPermission, err := object.GetPermission(permissionIds[0])
|
||||
@ -224,44 +250,72 @@ func (c *ApiController) BatchEnforce() {
|
||||
return
|
||||
}
|
||||
|
||||
enforceResult, err := object.BatchEnforce(firstPermission, &requests, permissionIds...)
|
||||
enforceResult, err := object.BatchEnforce(firstPermission, requests, permissionIds...)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res = append(res, enforceResult)
|
||||
keyRes = append(keyRes, firstPermission.GetModelAndAdapter())
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
c.ResponseOk(res, keyRes)
|
||||
}
|
||||
|
||||
func (c *ApiController) GetAllObjects() {
|
||||
userId := c.GetSessionUsername()
|
||||
userId := c.Input().Get("userId")
|
||||
if userId == "" {
|
||||
c.ResponseError(c.T("general:Please login first"))
|
||||
userId = c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError(c.T("general:Please login first"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
objects, err := object.GetAllObjects(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetAllObjects(userId))
|
||||
c.ResponseOk(objects)
|
||||
}
|
||||
|
||||
func (c *ApiController) GetAllActions() {
|
||||
userId := c.GetSessionUsername()
|
||||
userId := c.Input().Get("userId")
|
||||
if userId == "" {
|
||||
c.ResponseError(c.T("general:Please login first"))
|
||||
userId = c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError(c.T("general:Please login first"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
actions, err := object.GetAllActions(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetAllActions(userId))
|
||||
c.ResponseOk(actions)
|
||||
}
|
||||
|
||||
func (c *ApiController) GetAllRoles() {
|
||||
userId := c.GetSessionUsername()
|
||||
userId := c.Input().Get("userId")
|
||||
if userId == "" {
|
||||
c.ResponseError(c.T("general:Please login first"))
|
||||
userId = c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError(c.T("general:Please login first"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
roles, err := object.GetAllRoles(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetAllRoles(userId))
|
||||
c.ResponseOk(roles)
|
||||
}
|
||||
|
@ -39,13 +39,13 @@ func (c *ApiController) GetCerts() {
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
maskedCerts, err := object.GetMaskedCerts(object.GetCerts(owner))
|
||||
certs, err := object.GetMaskedCerts(object.GetCerts(owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedCerts)
|
||||
c.ResponseOk(certs)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetCertCount(owner, field, value)
|
||||
@ -65,13 +65,13 @@ func (c *ApiController) GetCerts() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetGlobleCerts
|
||||
// @Title GetGlobleCerts
|
||||
// GetGlobalCerts
|
||||
// @Title GetGlobalCerts
|
||||
// @Tag Cert API
|
||||
// @Description get globle certs
|
||||
// @Success 200 {array} object.Cert The Response object
|
||||
// @router /get-globle-certs [get]
|
||||
func (c *ApiController) GetGlobleCerts() {
|
||||
// @router /get-global-certs [get]
|
||||
func (c *ApiController) GetGlobalCerts() {
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
@ -80,13 +80,13 @@ func (c *ApiController) GetGlobleCerts() {
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
maskedCerts, err := object.GetMaskedCerts(object.GetGlobleCerts())
|
||||
certs, err := object.GetMaskedCerts(object.GetGlobalCerts())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedCerts)
|
||||
c.ResponseOk(certs)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetGlobalCertsCount(field, value)
|
||||
|
@ -71,7 +71,7 @@ func (c *ApiController) GetEnforcers() {
|
||||
// @Tag Enforcer API
|
||||
// @Description get 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]
|
||||
func (c *ApiController) GetEnforcer() {
|
||||
id := c.Input().Get("id")
|
||||
@ -99,7 +99,7 @@ func (c *ApiController) GetEnforcer() {
|
||||
// @Description update enforcer
|
||||
// @Param id query string true "The id ( owner/name ) of enforcer"
|
||||
// @Param enforcer body object true "The enforcer object"
|
||||
// @Success 200 {object} object
|
||||
// @Success 200 {object} object.Enforcer
|
||||
// @router /update-enforcer [post]
|
||||
func (c *ApiController) UpdateEnforcer() {
|
||||
id := c.Input().Get("id")
|
||||
@ -120,7 +120,7 @@ func (c *ApiController) UpdateEnforcer() {
|
||||
// @Tag Enforcer API
|
||||
// @Description add enforcer
|
||||
// @Param enforcer body object true "The enforcer object"
|
||||
// @Success 200 {object} object
|
||||
// @Success 200 {object} object.Enforcer
|
||||
// @router /add-enforcer [post]
|
||||
func (c *ApiController) AddEnforcer() {
|
||||
enforcer := object.Enforcer{}
|
||||
@ -138,8 +138,8 @@ func (c *ApiController) AddEnforcer() {
|
||||
// @Title DeleteEnforcer
|
||||
// @Tag Enforcer API
|
||||
// @Description delete enforcer
|
||||
// @Param body body object.Enforce true "The enforcer object"
|
||||
// @Success 200 {object} object
|
||||
// @Param body body object.Enforcer true "The enforcer object"
|
||||
// @Success 200 {object} object.Enforcer
|
||||
// @router /delete-enforcer [post]
|
||||
func (c *ApiController) DeleteEnforcer() {
|
||||
var enforcer object.Enforcer
|
||||
|
@ -18,7 +18,7 @@ import "github.com/casdoor/casdoor/object"
|
||||
|
||||
// GetDashboard
|
||||
// @Title GetDashboard
|
||||
// @Tag GetDashboard API
|
||||
// @Tag System API
|
||||
// @Description get information of dashboard
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /get-dashboard [get]
|
||||
|
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
|
||||
// @Description get ldap users
|
||||
// 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]
|
||||
func (c *ApiController) GetLdapUsers() {
|
||||
id := c.Input().Get("id")
|
||||
@ -59,6 +59,7 @@ func (c *ApiController) GetLdapUsers() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
//groupsMap, err := conn.GetLdapGroups(ldapServer.BaseDn)
|
||||
//if err != nil {
|
||||
@ -249,7 +250,7 @@ func (c *ApiController) DeleteLdap() {
|
||||
// @Tag Account API
|
||||
// @Description sync ldap users
|
||||
// @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]
|
||||
func (c *ApiController) SyncLdapUsers() {
|
||||
id := c.Input().Get("id")
|
||||
|
@ -26,8 +26,10 @@ type LinkForm struct {
|
||||
}
|
||||
|
||||
// Unlink ...
|
||||
// @router /unlink [post]
|
||||
// @Tag Login API
|
||||
// @Title Unlink
|
||||
// @router /unlink [post]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) Unlink() {
|
||||
user, ok := c.RequireSignedInUser()
|
||||
if !ok {
|
||||
|
@ -73,7 +73,7 @@ func (c *ApiController) MfaSetupInitiate() {
|
||||
// @Description setup verify totp
|
||||
// @param secret form string true "MFA secret"
|
||||
// @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]
|
||||
func (c *ApiController) MfaSetupVerify() {
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
@ -104,7 +104,7 @@ func (c *ApiController) MfaSetupVerify() {
|
||||
// @param owner form string true "owner of user"
|
||||
// @param name form string true "name of user"
|
||||
// @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]
|
||||
func (c *ApiController) MfaSetupEnable() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
@ -143,7 +143,7 @@ func (c *ApiController) MfaSetupEnable() {
|
||||
// @Description: Delete MFA
|
||||
// @param owner form string true "owner 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]
|
||||
func (c *ApiController) DeleteMfa() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
@ -176,7 +176,7 @@ func (c *ApiController) DeleteMfa() {
|
||||
// @param owner form string true "owner of user"
|
||||
// @param name form string true "name of user"
|
||||
// @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]
|
||||
func (c *ApiController) SetPreferredMfa() {
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
|
@ -41,13 +41,12 @@ func (c *ApiController) GetOrganizations() {
|
||||
|
||||
isGlobalAdmin := c.IsGlobalAdmin()
|
||||
if limit == "" || page == "" {
|
||||
var maskedOrganizations []*object.Organization
|
||||
var organizations []*object.Organization
|
||||
var err error
|
||||
|
||||
if isGlobalAdmin {
|
||||
maskedOrganizations, err = object.GetMaskedOrganizations(object.GetOrganizations(owner))
|
||||
organizations, err = object.GetMaskedOrganizations(object.GetOrganizations(owner))
|
||||
} else {
|
||||
maskedOrganizations, err = object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner))
|
||||
organizations, err = object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -55,15 +54,15 @@ func (c *ApiController) GetOrganizations() {
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedOrganizations)
|
||||
c.ResponseOk(organizations)
|
||||
} else {
|
||||
if !isGlobalAdmin {
|
||||
maskedOrganizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner))
|
||||
organizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.ResponseOk(maskedOrganizations)
|
||||
c.ResponseOk(organizations)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetOrganizationCount(owner, field, value)
|
||||
@ -93,13 +92,13 @@ func (c *ApiController) GetOrganizations() {
|
||||
// @router /get-organization [get]
|
||||
func (c *ApiController) GetOrganization() {
|
||||
id := c.Input().Get("id")
|
||||
maskedOrganization, err := object.GetMaskedOrganization(object.GetOrganization(id))
|
||||
organization, err := object.GetMaskedOrganization(object.GetOrganization(id))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedOrganization)
|
||||
c.ResponseOk(organization)
|
||||
}
|
||||
|
||||
// UpdateOrganization ...
|
||||
@ -178,7 +177,7 @@ func (c *ApiController) DeleteOrganization() {
|
||||
// @Tag Organization API
|
||||
// @Description get default application
|
||||
// @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]
|
||||
func (c *ApiController) GetDefaultApplication() {
|
||||
userId := c.GetSessionUsername()
|
||||
@ -190,8 +189,8 @@ func (c *ApiController) GetDefaultApplication() {
|
||||
return
|
||||
}
|
||||
|
||||
maskedApplication := object.GetMaskedApplication(application, userId)
|
||||
c.ResponseOk(maskedApplication)
|
||||
application = object.GetMaskedApplication(application, userId)
|
||||
c.ResponseOk(application)
|
||||
}
|
||||
|
||||
// GetOrganizationNames ...
|
||||
|
@ -163,6 +163,8 @@ func (c *ApiController) BuyProduct() {
|
||||
id := c.Input().Get("id")
|
||||
host := c.Ctx.Request.Host
|
||||
providerName := c.Input().Get("providerName")
|
||||
paymentEnv := c.Input().Get("paymentEnv")
|
||||
|
||||
// buy `pricingName/planName` for `paidUserName`
|
||||
pricingName := c.Input().Get("pricingName")
|
||||
planName := c.Input().Get("planName")
|
||||
@ -187,11 +189,11 @@ func (c *ApiController) BuyProduct() {
|
||||
return
|
||||
}
|
||||
|
||||
payment, err := object.BuyProduct(id, user, providerName, pricingName, planName, host)
|
||||
payment, attachInfo, err := object.BuyProduct(id, user, providerName, pricingName, planName, host, paymentEnv)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(payment)
|
||||
c.ResponseOk(payment, attachInfo)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
|
||||
// GetPrometheusInfo
|
||||
// @Title GetPrometheusInfo
|
||||
// @Tag Prometheus API
|
||||
// @Tag System API
|
||||
// @Description get Prometheus Info
|
||||
// @Success 200 {object} object.PrometheusInfo The Response object
|
||||
// @router /get-prometheus-info [get]
|
||||
|
@ -51,9 +51,14 @@ type NotificationForm struct {
|
||||
// @Param clientId query string true "The clientId of the application"
|
||||
// @Param clientSecret query string true "The clientSecret of the application"
|
||||
// @Param from body controllers.EmailForm true "Details of the email request"
|
||||
// @Success 200 {object} Response object
|
||||
// @router /api/send-email [post]
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /send-email [post]
|
||||
func (c *ApiController) SendEmail() {
|
||||
userId, ok := c.RequireSignedIn()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var emailForm EmailForm
|
||||
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &emailForm)
|
||||
@ -108,8 +113,22 @@ func (c *ApiController) SendEmail() {
|
||||
}
|
||||
|
||||
code := "123456"
|
||||
|
||||
// "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 !strings.HasPrefix(userId, "app/") {
|
||||
var user *object.User
|
||||
user, err = object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
|
||||
}
|
||||
}
|
||||
|
||||
for _, receiver := range emailForm.Receivers {
|
||||
err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender)
|
||||
if err != nil {
|
||||
@ -128,8 +147,8 @@ func (c *ApiController) SendEmail() {
|
||||
// @Param clientId query string true "The clientId of the application"
|
||||
// @Param clientSecret query string true "The clientSecret of the application"
|
||||
// @Param from body controllers.SmsForm true "Details of the sms request"
|
||||
// @Success 200 {object} Response object
|
||||
// @router /api/send-sms [post]
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /send-sms [post]
|
||||
func (c *ApiController) SendSms() {
|
||||
provider, err := c.GetProviderFromContext("SMS")
|
||||
if err != nil {
|
||||
@ -166,8 +185,8 @@ func (c *ApiController) SendSms() {
|
||||
// @Tag Service API
|
||||
// @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"
|
||||
// @Success 200 {object} Response object
|
||||
// @router /api/send-notification [post]
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /send-notification [post]
|
||||
func (c *ApiController) SendNotification() {
|
||||
provider, err := c.GetProviderFromContext("Notification")
|
||||
if err != nil {
|
||||
|
@ -40,13 +40,13 @@ func (c *ApiController) GetSyncers() {
|
||||
organization := c.Input().Get("organization")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
organizationSyncers, err := object.GetOrganizationSyncers(owner, organization)
|
||||
syncers, err := object.GetMaskedSyncers(object.GetOrganizationSyncers(owner, organization))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(organizationSyncers)
|
||||
c.ResponseOk(syncers)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetSyncerCount(owner, organization, field, value)
|
||||
@ -56,7 +56,7 @@ func (c *ApiController) GetSyncers() {
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
syncers, err := object.GetPaginationSyncers(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
syncers, err := object.GetMaskedSyncers(object.GetPaginationSyncers(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -76,7 +76,7 @@ func (c *ApiController) GetSyncers() {
|
||||
func (c *ApiController) GetSyncer() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
syncer, err := object.GetSyncer(id)
|
||||
syncer, err := object.GetMaskedSyncer(object.GetSyncer(id))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
@ -156,12 +156,11 @@ func (c *ApiController) DeleteToken() {
|
||||
// @Success 200 {object} object.TokenWrapper The Response object
|
||||
// @Success 400 {object} object.TokenError The Response object
|
||||
// @Success 401 {object} object.TokenError The Response object
|
||||
// @router api/login/oauth/access_token [post]
|
||||
// @router /login/oauth/access_token [post]
|
||||
func (c *ApiController) GetOAuthToken() {
|
||||
grantType := c.Input().Get("grant_type")
|
||||
refreshToken := c.Input().Get("refresh_token")
|
||||
clientId := c.Input().Get("client_id")
|
||||
clientSecret := c.Input().Get("client_secret")
|
||||
grantType := c.Input().Get("grant_type")
|
||||
code := c.Input().Get("code")
|
||||
verifier := c.Input().Get("code_verifier")
|
||||
scope := c.Input().Get("scope")
|
||||
@ -169,35 +168,61 @@ func (c *ApiController) GetOAuthToken() {
|
||||
password := c.Input().Get("password")
|
||||
tag := c.Input().Get("tag")
|
||||
avatar := c.Input().Get("avatar")
|
||||
refreshToken := c.Input().Get("refresh_token")
|
||||
|
||||
if clientId == "" && clientSecret == "" {
|
||||
clientId, clientSecret, _ = c.Ctx.Request.BasicAuth()
|
||||
}
|
||||
if clientId == "" {
|
||||
// If clientID is empty, try to read data from RequestBody
|
||||
|
||||
if len(c.Ctx.Input.RequestBody) != 0 {
|
||||
// If clientId is empty, try to read data from RequestBody
|
||||
var tokenRequest TokenRequest
|
||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &tokenRequest); err == nil {
|
||||
clientId = tokenRequest.ClientId
|
||||
clientSecret = tokenRequest.ClientSecret
|
||||
grantType = tokenRequest.GrantType
|
||||
refreshToken = tokenRequest.RefreshToken
|
||||
code = tokenRequest.Code
|
||||
verifier = tokenRequest.Verifier
|
||||
scope = tokenRequest.Scope
|
||||
username = tokenRequest.Username
|
||||
password = tokenRequest.Password
|
||||
tag = tokenRequest.Tag
|
||||
avatar = tokenRequest.Avatar
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &tokenRequest)
|
||||
if err == nil {
|
||||
if clientId == "" {
|
||||
clientId = tokenRequest.ClientId
|
||||
}
|
||||
if clientSecret == "" {
|
||||
clientSecret = tokenRequest.ClientSecret
|
||||
}
|
||||
if grantType == "" {
|
||||
grantType = tokenRequest.GrantType
|
||||
}
|
||||
if code == "" {
|
||||
code = tokenRequest.Code
|
||||
}
|
||||
if verifier == "" {
|
||||
verifier = tokenRequest.Verifier
|
||||
}
|
||||
if scope == "" {
|
||||
scope = tokenRequest.Scope
|
||||
}
|
||||
if username == "" {
|
||||
username = tokenRequest.Username
|
||||
}
|
||||
if password == "" {
|
||||
password = tokenRequest.Password
|
||||
}
|
||||
if tag == "" {
|
||||
tag = tokenRequest.Tag
|
||||
}
|
||||
if avatar == "" {
|
||||
avatar = tokenRequest.Avatar
|
||||
}
|
||||
if refreshToken == "" {
|
||||
refreshToken = tokenRequest.RefreshToken
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
host := c.Ctx.Request.Host
|
||||
oAuthtoken, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
|
||||
token, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = oAuthtoken
|
||||
c.Data["json"] = token
|
||||
c.SetTokenErrorHttpStatus()
|
||||
c.ServeJSON()
|
||||
}
|
||||
@ -248,6 +273,7 @@ func (c *ApiController) RefreshToken() {
|
||||
|
||||
// IntrospectToken
|
||||
// @Title IntrospectToken
|
||||
// @Tag Login API
|
||||
// @Description The introspection endpoint is an OAuth 2.0 endpoint that takes a
|
||||
// parameter representing an OAuth 2.0 token and returns a JSON document
|
||||
// representing the meta information surrounding the
|
||||
|
@ -15,10 +15,10 @@
|
||||
package controllers
|
||||
|
||||
type TokenRequest struct {
|
||||
GrantType string `json:"grant_type"`
|
||||
Code string `json:"code"`
|
||||
ClientId string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
GrantType string `json:"grant_type"`
|
||||
Code string `json:"code"`
|
||||
Verifier string `json:"code_verifier"`
|
||||
Scope string `json:"scope"`
|
||||
Username string `json:"username"`
|
||||
|
@ -39,13 +39,13 @@ func (c *ApiController) GetGlobalUsers() {
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGlobalUsers())
|
||||
users, err := object.GetMaskedUsers(object.GetGlobalUsers())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedUsers)
|
||||
c.ResponseOk(users)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetGlobalUserCount(field, value)
|
||||
@ -90,22 +90,22 @@ func (c *ApiController) GetUsers() {
|
||||
|
||||
if limit == "" || page == "" {
|
||||
if groupName != "" {
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(util.GetId(owner, groupName)))
|
||||
users, err := object.GetMaskedUsers(object.GetGroupUsers(util.GetId(owner, groupName)))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.ResponseOk(maskedUsers)
|
||||
c.ResponseOk(users)
|
||||
return
|
||||
}
|
||||
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetUsers(owner))
|
||||
users, err := object.GetMaskedUsers(object.GetUsers(owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedUsers)
|
||||
c.ResponseOk(users)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetUserCount(owner, field, value, groupName)
|
||||
@ -161,7 +161,6 @@ func (c *ApiController) GetUser() {
|
||||
}
|
||||
|
||||
var user *object.User
|
||||
|
||||
if id == "" && owner == "" {
|
||||
switch {
|
||||
case email != "":
|
||||
@ -176,7 +175,8 @@ func (c *ApiController) GetUser() {
|
||||
owner = util.GetOwnerFromId(id)
|
||||
}
|
||||
|
||||
organization, err := object.GetOrganization(util.GetId("admin", owner))
|
||||
var organization *object.Organization
|
||||
organization, err = object.GetOrganization(util.GetId("admin", owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -223,13 +223,13 @@ func (c *ApiController) GetUser() {
|
||||
}
|
||||
|
||||
isAdminOrSelf := c.IsAdminOrSelf(user)
|
||||
maskedUser, err := object.GetMaskedUser(user, isAdminOrSelf)
|
||||
user, err = object.GetMaskedUser(user, isAdminOrSelf)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedUser)
|
||||
c.ResponseOk(user)
|
||||
}
|
||||
|
||||
// UpdateUser
|
||||
@ -476,16 +476,16 @@ func (c *ApiController) SetPassword() {
|
||||
isAdmin := c.IsAdmin()
|
||||
if isAdmin {
|
||||
if oldPassword != "" {
|
||||
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
err = object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if code == "" {
|
||||
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
err = object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -510,6 +510,7 @@ func (c *ApiController) SetPassword() {
|
||||
// @Title CheckUserPassword
|
||||
// @router /check-user-password [post]
|
||||
// @Tag User API
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) CheckUserPassword() {
|
||||
var user object.User
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
||||
@ -518,11 +519,11 @@ func (c *ApiController) CheckUserPassword() {
|
||||
return
|
||||
}
|
||||
|
||||
_, msg := object.CheckUserPassword(user.Owner, user.Name, user.Password, c.GetAcceptLanguage())
|
||||
if msg == "" {
|
||||
c.ResponseOk()
|
||||
_, err = object.CheckUserPassword(user.Owner, user.Name, user.Password, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
} else {
|
||||
c.ResponseError(msg)
|
||||
c.ResponseOk()
|
||||
}
|
||||
}
|
||||
|
||||
@ -540,13 +541,13 @@ func (c *ApiController) GetSortedUsers() {
|
||||
sorter := c.Input().Get("sorter")
|
||||
limit := util.ParseInt(c.Input().Get("limit"))
|
||||
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetSortedUsers(owner, sorter, limit))
|
||||
users, err := object.GetMaskedUsers(object.GetSortedUsers(owner, sorter, limit))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedUsers)
|
||||
c.ResponseOk(users)
|
||||
}
|
||||
|
||||
// GetUserCount
|
||||
@ -580,6 +581,7 @@ func (c *ApiController) GetUserCount() {
|
||||
// @Title AddUserKeys
|
||||
// @router /add-user-keys [post]
|
||||
// @Tag User API
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) AddUserKeys() {
|
||||
var user object.User
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
||||
|
@ -39,6 +39,7 @@ const (
|
||||
// @Title SendVerificationCode
|
||||
// @Tag Verification API
|
||||
// @router /send-verification-code [post]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) SendVerificationCode() {
|
||||
var vform form.VerificationForm
|
||||
err := c.ParseForm(&vform)
|
||||
@ -53,17 +54,34 @@ func (c *ApiController) SendVerificationCode() {
|
||||
return
|
||||
}
|
||||
|
||||
if vform.CaptchaType != "none" {
|
||||
if captchaProvider := captcha.GetCaptchaProvider(vform.CaptchaType); captchaProvider == nil {
|
||||
c.ResponseError(c.T("general:don't support captchaProvider: ") + vform.CaptchaType)
|
||||
return
|
||||
} else if isHuman, err := captchaProvider.VerifyCaptcha(vform.CaptchaToken, vform.ClientSecret); err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
} else if !isHuman {
|
||||
provider, err := object.GetCaptchaProviderByApplication(vform.ApplicationId, "false", c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if provider != nil {
|
||||
if vform.CaptchaType != provider.Type {
|
||||
c.ResponseError(c.T("verification:Turing test failed."))
|
||||
return
|
||||
}
|
||||
|
||||
if provider.Type != "Default" {
|
||||
vform.ClientSecret = provider.ClientSecret
|
||||
}
|
||||
|
||||
if vform.CaptchaType != "none" {
|
||||
if captchaProvider := captcha.GetCaptchaProvider(vform.CaptchaType); captchaProvider == nil {
|
||||
c.ResponseError(c.T("general:don't support captchaProvider: ") + vform.CaptchaType)
|
||||
return
|
||||
} else if isHuman, err := captchaProvider.VerifyCaptcha(vform.CaptchaToken, vform.ClientSecret); err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
} else if !isHuman {
|
||||
c.ResponseError(c.T("verification:Turing test failed."))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
application, err := object.GetApplication(vform.ApplicationId)
|
||||
@ -212,6 +230,7 @@ func (c *ApiController) SendVerificationCode() {
|
||||
// @Title VerifyCaptcha
|
||||
// @Tag Verification API
|
||||
// @router /verify-captcha [post]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) VerifyCaptcha() {
|
||||
var vform form.VerificationForm
|
||||
err := c.ParseForm(&vform)
|
||||
@ -225,6 +244,16 @@ func (c *ApiController) VerifyCaptcha() {
|
||||
return
|
||||
}
|
||||
|
||||
captchaProvider, err := object.GetCaptchaProviderByOwnerName(vform.ApplicationId, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if captchaProvider.Type != "Default" {
|
||||
vform.ClientSecret = captchaProvider.ClientSecret
|
||||
}
|
||||
|
||||
provider := captcha.GetCaptchaProvider(vform.CaptchaType)
|
||||
if provider == nil {
|
||||
c.ResponseError(c.T("verification:Invalid captcha provider."))
|
||||
@ -243,7 +272,8 @@ func (c *ApiController) VerifyCaptcha() {
|
||||
// ResetEmailOrPhone ...
|
||||
// @Tag Account API
|
||||
// @Title ResetEmailOrPhone
|
||||
// @router /api/reset-email-or-phone [post]
|
||||
// @router /reset-email-or-phone [post]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) ResetEmailOrPhone() {
|
||||
user, ok := c.RequireSignedInUser()
|
||||
if !ok {
|
||||
@ -337,7 +367,8 @@ func (c *ApiController) ResetEmailOrPhone() {
|
||||
// VerifyCode
|
||||
// @Tag Verification API
|
||||
// @Title VerifyCode
|
||||
// @router /api/verify-code [post]
|
||||
// @router /verify-code [post]
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) VerifyCode() {
|
||||
var authForm form.AuthForm
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &authForm)
|
||||
|
@ -146,7 +146,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
|
||||
}
|
||||
|
||||
// WebAuthnSigninFinish
|
||||
// @Title WebAuthnSigninBegin
|
||||
// @Title WebAuthnSigninFinish
|
||||
// @Tag Login API
|
||||
// @Description WebAuthn Login Flow 2nd stage
|
||||
// @Param body body protocol.CredentialAssertionResponse true "authenticator assertion Response"
|
||||
@ -154,6 +154,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
|
||||
// @router /webauthn/signin/finish [post]
|
||||
func (c *ApiController) WebAuthnSigninFinish() {
|
||||
responseType := c.Input().Get("responseType")
|
||||
clientId := c.Input().Get("clientId")
|
||||
webauthnObj, err := object.GetWebAuthnObject(c.Ctx.Request.Host)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@ -182,7 +183,13 @@ func (c *ApiController) WebAuthnSigninFinish() {
|
||||
c.SetSessionUsername(userId)
|
||||
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
|
||||
|
||||
application, err := object.GetApplicationByUser(user)
|
||||
var application *object.Application
|
||||
|
||||
if clientId != "" && (responseType == ResponseTypeCode) {
|
||||
application, err = object.GetApplicationByClientId(clientId)
|
||||
} else {
|
||||
application, err = object.GetApplicationByUser(user)
|
||||
}
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
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
|
||||
}
|
||||
|
||||
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" {
|
||||
return NewAzureACSEmailProvider(clientSecret, host)
|
||||
} else if typ == "Custom HTTP Email" {
|
||||
return NewHttpEmailProvider(endpoint, method)
|
||||
} else {
|
||||
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl)
|
||||
}
|
||||
|
15
form/auth.go
15
form/auth.go
@ -14,8 +14,11 @@
|
||||
|
||||
package form
|
||||
|
||||
import "reflect"
|
||||
|
||||
type AuthForm struct {
|
||||
Type string `json:"type"`
|
||||
Type string `json:"type"`
|
||||
SigninMethod string `json:"signinMethod"`
|
||||
|
||||
Organization string `json:"organization"`
|
||||
Username string `json:"username"`
|
||||
@ -59,3 +62,13 @@ type AuthForm struct {
|
||||
Plan string `json:"plan"`
|
||||
Pricing string `json:"pricing"`
|
||||
}
|
||||
|
||||
func GetAuthFormFieldValue(form *AuthForm, fieldName string) (bool, string) {
|
||||
val := reflect.ValueOf(*form)
|
||||
fieldValue := val.FieldByName(fieldName)
|
||||
|
||||
if fieldValue.IsValid() && fieldValue.Kind() == reflect.String {
|
||||
return true, fieldValue.String()
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
27
go.mod
27
go.mod
@ -4,18 +4,16 @@ go 1.16
|
||||
|
||||
require (
|
||||
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/aws/aws-sdk-go v1.45.5
|
||||
github.com/beego/beego v1.12.12
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/casbin/casbin v1.9.1 // indirect
|
||||
github.com/casbin/casbin/v2 v2.77.2
|
||||
github.com/casdoor/go-sms-sender v0.15.0
|
||||
github.com/casdoor/go-sms-sender v0.19.0
|
||||
github.com/casdoor/gomail/v2 v2.0.1
|
||||
github.com/casdoor/notify v0.45.0
|
||||
github.com/casdoor/oss v1.3.0
|
||||
github.com/casdoor/xorm-adapter/v3 v3.0.4
|
||||
github.com/casdoor/oss v1.5.0
|
||||
github.com/casdoor/xorm-adapter/v3 v3.1.0
|
||||
github.com/casvisor/casvisor-go-sdk v1.0.3
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/denisenkom/go-mssqldb v0.9.0
|
||||
@ -23,26 +21,26 @@ require (
|
||||
github.com/elimity-com/scim v0.0.0-20230426070224-941a5eac92f3
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/forestmgy/ldapserver v1.1.0
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5
|
||||
github.com/go-git/go-git/v5 v5.6.0
|
||||
github.com/go-ldap/ldap/v3 v3.3.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/go-mysql-org/go-mysql v1.7.0
|
||||
github.com/go-pay/gopay v1.5.72
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||
github.com/go-webauthn/webauthn v0.6.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/google/uuid v1.3.1
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||
github.com/google/uuid v1.4.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/lestrrat-go/jwx v1.2.21
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
||||
github.com/markbates/goth v1.75.2
|
||||
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/pquerna/otp v1.4.0
|
||||
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/robfig/cron/v3 v3.0.1
|
||||
github.com/russellhaering/gosaml2 v0.9.0
|
||||
@ -61,11 +59,10 @@ require (
|
||||
github.com/xorm-io/core v0.7.4
|
||||
github.com/xorm-io/xorm v1.1.6
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.12.0
|
||||
golang.org/x/net v0.14.0
|
||||
golang.org/x/oauth2 v0.11.0
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
google.golang.org/api v0.138.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/oauth2 v0.13.0
|
||||
google.golang.org/api v0.150.0
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
layeh.com/radius v0.0.0-20221205141417-e7fbddd11d68
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Das Konto für den Anbieter %s und Benutzernamen %s (%s) existiert nicht und es ist nicht erlaubt, ein neues Konto anzumelden. Bitte wenden Sie sich an Ihren IT-Support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Das Konto für den Anbieter %s und Benutzernamen %s (%s) ist bereits mit einem anderen Konto verknüpft: %s (%s)",
|
||||
"The application: %s does not exist": "Die Anwendung: %s existiert nicht",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Nicht autorisierte Operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s und %s stimmen nicht überein"
|
||||
@ -33,11 +37,12 @@
|
||||
"Email is invalid": "E-Mail ist ungültig",
|
||||
"Empty username.": "Leerer Benutzername.",
|
||||
"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",
|
||||
"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",
|
||||
"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 cannot be empty": "Das Telefon darf nicht leer sein",
|
||||
"Phone number is invalid": "Die Telefonnummer ist ungültig",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "La cuenta para el proveedor: %s y el nombre de usuario: %s (%s) no existe y no se permite registrarse como una nueva cuenta, por favor contacte a su soporte de TI",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "La cuenta para proveedor: %s y nombre de usuario: %s (%s) ya está vinculada a otra cuenta: %s (%s)",
|
||||
"The application: %s does not exist": "La aplicación: %s no existe",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"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",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Los servicios %s y %s no coinciden"
|
||||
@ -33,11 +37,12 @@
|
||||
"Email is invalid": "El correo electrónico no es válido",
|
||||
"Empty username.": "Nombre de usuario vacío.",
|
||||
"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",
|
||||
"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",
|
||||
"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 cannot be empty": "Teléfono no puede estar vacío",
|
||||
"Phone number is invalid": "El número de teléfono no es válido",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Le compte pour le fournisseur : %s et le nom d'utilisateur : %s (%s) n'existe pas et n'est pas autorisé à s'inscrire comme nouveau compte, veuillez contacter votre support informatique",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Le compte du fournisseur : %s et le nom d'utilisateur : %s (%s) sont déjà liés à un autre compte : %s (%s)",
|
||||
"The application: %s does not exist": "L'application : %s n'existe pas",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"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",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Les services %s et %s ne correspondent pas"
|
||||
@ -33,11 +37,12 @@
|
||||
"Email is invalid": "L'adresse e-mail est invalide",
|
||||
"Empty username.": "Nom d'utilisateur 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",
|
||||
"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",
|
||||
"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 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",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"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 application: %s does not exist": "Aplikasi: %s tidak ada",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"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",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok"
|
||||
@ -33,11 +37,12 @@
|
||||
"Email is invalid": "Email tidak valid",
|
||||
"Empty username.": "Nama pengguna 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",
|
||||
"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",
|
||||
"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 cannot be empty": "Telepon tidak boleh kosong",
|
||||
"Phone number is invalid": "Nomor telepon tidak valid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "プロバイダー名:%sとユーザー名:%s(%s)のアカウントは存在しません。新しいアカウントとしてサインアップすることはできません。 ITサポートに連絡してください",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "プロバイダのアカウント:%s とユーザー名:%s (%s) は既に別のアカウント:%s (%s) にリンクされています",
|
||||
"The application: %s does not exist": "アプリケーション: %sは存在しません",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません",
|
||||
"The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません",
|
||||
"Unauthorized operation": "不正操作",
|
||||
"Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s",
|
||||
"User's tag: %s is not listed in the application's tags": "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": {
|
||||
"Service %s and %s do not match": "サービス%sと%sは一致しません"
|
||||
@ -33,11 +37,12 @@
|
||||
"Email is invalid": "電子メールは無効です",
|
||||
"Empty username.": "空のユーザー名。",
|
||||
"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のユーザー名またはパスワードが間違っています",
|
||||
"LastName cannot be blank": "姓は空白にできません",
|
||||
"Multiple accounts with same uid, please check your ldap server": "同じuidを持つ複数のアカウントがあります。あなたのLDAPサーバーを確認してください",
|
||||
"Organization does not exist": "組織は存在しません",
|
||||
"Password must have at least 6 characters": "パスワードは少なくとも6つの文字が必要です",
|
||||
"Phone already exists": "電話はすでに存在しています",
|
||||
"Phone cannot be empty": "電話は空っぽにできません",
|
||||
"Phone number is invalid": "電話番号が無効です",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "공급자 계정 %s과 사용자 이름 %s (%s)는 존재하지 않으며 새 계정으로 등록할 수 없습니다. IT 지원팀에 문의하십시오",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "공급자 계정 %s과 사용자 이름 %s(%s)는 이미 다른 계정 %s(%s)에 연결되어 있습니다",
|
||||
"The application: %s does not exist": "해당 애플리케이션(%s)이 존재하지 않습니다",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "어플리케이션에서는 암호를 사용한 로그인 방법이 활성화되어 있지 않습니다",
|
||||
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
|
||||
"Unauthorized operation": "무단 조작",
|
||||
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s",
|
||||
"User's tag: %s is not listed in the application's tags": "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": {
|
||||
"Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"
|
||||
@ -33,11 +37,12 @@
|
||||
"Email is invalid": "이메일이 유효하지 않습니다",
|
||||
"Empty username.": "빈 사용자 이름.",
|
||||
"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 사용자 이름 또는 암호가 잘못되었습니다",
|
||||
"LastName cannot be blank": "성은 비어 있을 수 없습니다",
|
||||
"Multiple accounts with same uid, please check your ldap server": "동일한 UID를 가진 여러 계정이 있습니다. LDAP 서버를 확인해주세요",
|
||||
"Organization does not exist": "조직은 존재하지 않습니다",
|
||||
"Password must have at least 6 characters": "암호는 적어도 6자 이상이어야 합니다",
|
||||
"Phone already exists": "전화기는 이미 존재합니다",
|
||||
"Phone cannot be empty": "전화는 비워 둘 수 없습니다",
|
||||
"Phone number is invalid": "전화번호가 유효하지 않습니다",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Аккаунт для провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован как новый аккаунт. Пожалуйста, обратитесь в службу поддержки IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Аккаунт поставщика: %s и имя пользователя: %s (%s) уже связаны с другим аккаунтом: %s (%s)",
|
||||
"The application: %s does not exist": "Приложение: %s не существует",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",
|
||||
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
|
||||
"Unauthorized operation": "Несанкционированная операция",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s",
|
||||
"User's tag: %s is not listed in the application's tags": "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": {
|
||||
"Service %s and %s do not match": "Сервисы %s и %s не совпадают"
|
||||
@ -33,11 +37,12 @@
|
||||
"Email is invalid": "Адрес электронной почты недействительный",
|
||||
"Empty username.": "Пустое имя пользователя.",
|
||||
"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",
|
||||
"LastName cannot be blank": "Фамилия не может быть пустой",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Множественные учетные записи с тем же UID. Пожалуйста, проверьте свой сервер LDAP",
|
||||
"Organization does not exist": "Организация не существует",
|
||||
"Password must have at least 6 characters": "Пароль должен содержать не менее 6 символов",
|
||||
"Phone already exists": "Телефон уже существует",
|
||||
"Phone cannot be empty": "Телефон не может быть пустым",
|
||||
"Phone number is invalid": "Номер телефона является недействительным",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"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": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
@ -33,11 +37,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 user name or password incorrect",
|
||||
"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",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) không tồn tại và không được phép đăng ký như một tài khoản mới, vui lòng liên hệ với bộ phận hỗ trợ công nghệ thông tin của bạn",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) đã được liên kết với tài khoản khác: %s (%s)",
|
||||
"The application: %s does not exist": "Ứng dụng: %s không tồn tại",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with 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",
|
||||
"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",
|
||||
"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": {
|
||||
"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 +37,12 @@
|
||||
"Email is invalid": "Địa chỉ email không hợp lệ",
|
||||
"Empty username.": "Tên đăng nhập 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",
|
||||
"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",
|
||||
"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 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ệ",
|
||||
|
@ -15,11 +15,15 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许注册新账户, 请联系IT支持",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "提供商账户: %s与用户名: %s (%s)已经与其他账户绑定: %s (%s)",
|
||||
"The application: %s does not exist": "应用%s不存在",
|
||||
"The login method: login with LDAP is not enabled for the application": "该应用禁止采用LDAP登录方式",
|
||||
"The login method: login with SMS is not enabled for the application": "该应用禁止采用短信登录方式",
|
||||
"The login method: login with email is not enabled for the application": "该应用禁止采用邮箱登录方式",
|
||||
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",
|
||||
"The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
|
||||
"Unauthorized operation": "未授权的操作",
|
||||
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s",
|
||||
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中"
|
||||
"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": {
|
||||
"Service %s and %s do not match": "服务%s与%s不匹配"
|
||||
@ -33,11 +37,12 @@
|
||||
"Email is invalid": "无效邮箱",
|
||||
"Empty username.": "用户名不可为空",
|
||||
"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密码错误",
|
||||
"LastName cannot be blank": "姓不可以为空",
|
||||
"Multiple accounts with same uid, please check your ldap server": "多个帐户具有相同的uid,请检查您的 LDAP 服务器",
|
||||
"Organization does not exist": "组织不存在",
|
||||
"Password must have at least 6 characters": "新密码至少为6位",
|
||||
"Phone already exists": "该手机号已存在",
|
||||
"Phone cannot be empty": "手机号不可为空",
|
||||
"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_secret", idp.Config.ClientSecret)
|
||||
payload.Set("redirect_uri", idp.Config.RedirectURL)
|
||||
|
||||
resp, err := idp.Client.PostForm(idp.Config.Endpoint.TokenURL, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -97,10 +99,10 @@ func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
pToken := &AdfsToken{}
|
||||
err = json.Unmarshal(data, pToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to unmarshal token response: %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if pToken.ErrMsg != "" {
|
||||
return nil, fmt.Errorf("pToken.Errmsg = %s", pToken.ErrMsg)
|
||||
return nil, fmt.Errorf(pToken.ErrMsg)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
@ -31,6 +31,7 @@ type UserInfo struct {
|
||||
Phone string
|
||||
CountryCode string
|
||||
AvatarUrl string
|
||||
Extra map[string]string
|
||||
}
|
||||
|
||||
type ProviderInfo struct {
|
||||
@ -90,6 +91,8 @@ func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) (IdProvider, error
|
||||
return NewGitlabIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
case "ADFS":
|
||||
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":
|
||||
return NewBaiduIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
case "Alipay":
|
||||
|
@ -186,15 +186,24 @@ func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
||||
id = wechatUserInfo.Openid
|
||||
}
|
||||
|
||||
extra := make(map[string]string)
|
||||
extra["wechat_unionid"] = wechatUserInfo.Openid
|
||||
// For WeChat, different appId corresponds to different openId
|
||||
extra[BuildWechatOpenIdKey(idp.Config.ClientID)] = wechatUserInfo.Openid
|
||||
userInfo := UserInfo{
|
||||
Id: id,
|
||||
Username: wechatUserInfo.Nickname,
|
||||
DisplayName: wechatUserInfo.Nickname,
|
||||
AvatarUrl: wechatUserInfo.Headimgurl,
|
||||
Extra: extra,
|
||||
}
|
||||
return &userInfo, nil
|
||||
}
|
||||
|
||||
func BuildWechatOpenIdKey(appId string) string {
|
||||
return fmt.Sprintf("wechat_openid_%s", appId)
|
||||
}
|
||||
|
||||
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
|
||||
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", clientId, clientSecret)
|
||||
request, err := http.NewRequest("GET", accessTokenUrl, nil)
|
||||
|
@ -45,6 +45,23 @@
|
||||
"alertType": "None"
|
||||
}
|
||||
],
|
||||
"signinMethods": [
|
||||
{
|
||||
"name": "Password",
|
||||
"displayName": "Password",
|
||||
"rule": "All",
|
||||
},
|
||||
{
|
||||
"name": "Verification code",
|
||||
"displayName": "Verification code",
|
||||
"rule": "All",
|
||||
},
|
||||
{
|
||||
"name": "WebAuthn",
|
||||
"displayName": "WebAuthn",
|
||||
"rule": "None",
|
||||
},
|
||||
],
|
||||
"signupItems": [
|
||||
{
|
||||
"name": "ID",
|
||||
@ -104,7 +121,9 @@
|
||||
}
|
||||
],
|
||||
"redirectUris": [""],
|
||||
"expireInHours": 168
|
||||
"expireInHours": 168,
|
||||
"failedSigninLimit": 5,
|
||||
"failedSigninFrozenTime": 15
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
|
@ -16,6 +16,7 @@ package ldap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"log"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
@ -49,20 +50,20 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
|
||||
|
||||
if r.AuthenticationChoice() == "simple" {
|
||||
bindUsername, bindOrg, err := getNameAndOrgFromDN(string(r.Name()))
|
||||
if err != "" {
|
||||
log.Printf("Bind failed ,ErrMsg=%s", err)
|
||||
if err != nil {
|
||||
log.Printf("getNameAndOrgFromDN() error: %s", err.Error())
|
||||
res.SetResultCode(ldap.LDAPResultInvalidDNSyntax)
|
||||
res.SetDiagnosticMessage("bind failed ErrMsg: " + err)
|
||||
res.SetDiagnosticMessage(fmt.Sprintf("getNameAndOrgFromDN() error: %s", err.Error()))
|
||||
w.Write(res)
|
||||
return
|
||||
}
|
||||
|
||||
bindPassword := string(r.AuthenticationSimple())
|
||||
bindUser, err := object.CheckUserPassword(bindOrg, bindUsername, bindPassword, "en")
|
||||
if err != "" {
|
||||
if err != nil {
|
||||
log.Printf("Bind failed User=%s, Pass=%#v, ErrMsg=%s", string(r.Name()), r.Authentication(), err)
|
||||
res.SetResultCode(ldap.LDAPResultInvalidCredentials)
|
||||
res.SetDiagnosticMessage("invalid credentials ErrMsg: " + err)
|
||||
res.SetDiagnosticMessage("invalid credentials ErrMsg: " + err.Error())
|
||||
w.Write(res)
|
||||
return
|
||||
}
|
||||
@ -78,7 +79,7 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
|
||||
m.Client.OrgName = bindOrg
|
||||
} else {
|
||||
res.SetResultCode(ldap.LDAPResultAuthMethodNotSupported)
|
||||
res.SetDiagnosticMessage("Authentication method not supported,Please use Simple Authentication")
|
||||
res.SetDiagnosticMessage("Authentication method not supported, please use Simple Authentication")
|
||||
}
|
||||
w.Write(res)
|
||||
}
|
||||
@ -113,10 +114,22 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
dn := fmt.Sprintf("cn=%s,%s", user.Name, string(r.BaseObject()))
|
||||
dn := fmt.Sprintf("uid=%s,cn=%s,%s", user.Id, user.Name, string(r.BaseObject()))
|
||||
e := ldap.NewSearchResultEntry(dn)
|
||||
|
||||
for _, attr := range r.Attributes() {
|
||||
uidNumberStr := fmt.Sprintf("%v", hash(user.Name))
|
||||
e.AddAttribute("uidNumber", message.AttributeValue(uidNumberStr))
|
||||
e.AddAttribute("gidNumber", message.AttributeValue(uidNumberStr))
|
||||
e.AddAttribute("homeDirectory", message.AttributeValue("/home/"+user.Name))
|
||||
e.AddAttribute("cn", message.AttributeValue(user.Name))
|
||||
e.AddAttribute("uid", message.AttributeValue(user.Id))
|
||||
attrs := r.Attributes()
|
||||
for _, attr := range attrs {
|
||||
if string(attr) == "*" {
|
||||
attrs = AdditionalLdapAttributes
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, attr := range attrs {
|
||||
e.AddAttribute(message.AttributeDescription(attr), getAttribute(string(attr), user))
|
||||
if string(attr) == "cn" {
|
||||
e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user))
|
||||
@ -127,3 +140,9 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
||||
}
|
||||
w.Write(res)
|
||||
}
|
||||
|
||||
func hash(s string) uint32 {
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(s))
|
||||
return h.Sum32()
|
||||
}
|
||||
|
200
ldap/util.go
200
ldap/util.go
@ -24,9 +24,73 @@ import (
|
||||
"github.com/lor00x/goldap/message"
|
||||
|
||||
ldap "github.com/forestmgy/ldapserver"
|
||||
|
||||
"github.com/xorm-io/builder"
|
||||
)
|
||||
|
||||
func getNameAndOrgFromDN(DN string) (string, string, string) {
|
||||
type AttributeMapper func(user *object.User) message.AttributeValue
|
||||
|
||||
type FieldRelation struct {
|
||||
userField string
|
||||
notSearchable bool
|
||||
hideOnStarOp bool
|
||||
fieldMapper AttributeMapper
|
||||
}
|
||||
|
||||
func (rel FieldRelation) GetField() (string, error) {
|
||||
if rel.notSearchable {
|
||||
return "", fmt.Errorf("attribute %s not supported", rel.userField)
|
||||
}
|
||||
return rel.userField, nil
|
||||
}
|
||||
|
||||
func (rel FieldRelation) GetAttributeValue(user *object.User) message.AttributeValue {
|
||||
return rel.fieldMapper(user)
|
||||
}
|
||||
|
||||
var ldapAttributesMapping = map[string]FieldRelation{
|
||||
"cn": {userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
return message.AttributeValue(user.Name)
|
||||
}},
|
||||
"uid": {userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
return message.AttributeValue(user.Name)
|
||||
}},
|
||||
"displayname": {userField: "displayName", fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
return message.AttributeValue(user.DisplayName)
|
||||
}},
|
||||
"email": {userField: "email", fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
return message.AttributeValue(user.Email)
|
||||
}},
|
||||
"mail": {userField: "email", fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
return message.AttributeValue(user.Email)
|
||||
}},
|
||||
"mobile": {userField: "phone", fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
return message.AttributeValue(user.Phone)
|
||||
}},
|
||||
"title": {userField: "tag", fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
return message.AttributeValue(user.Tag)
|
||||
}},
|
||||
"userPassword": {
|
||||
userField: "userPassword",
|
||||
notSearchable: true,
|
||||
fieldMapper: func(user *object.User) message.AttributeValue {
|
||||
return message.AttributeValue(getUserPasswordWithType(user))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var AdditionalLdapAttributes []message.LDAPString
|
||||
|
||||
func init() {
|
||||
for k, v := range ldapAttributesMapping {
|
||||
if v.hideOnStarOp {
|
||||
continue
|
||||
}
|
||||
AdditionalLdapAttributes = append(AdditionalLdapAttributes, message.LDAPString(k))
|
||||
}
|
||||
}
|
||||
|
||||
func getNameAndOrgFromDN(DN string) (string, string, error) {
|
||||
DNFields := strings.Split(DN, ",")
|
||||
params := make(map[string]string, len(DNFields))
|
||||
for _, field := range DNFields {
|
||||
@ -37,12 +101,12 @@ func getNameAndOrgFromDN(DN string) (string, string, string) {
|
||||
}
|
||||
|
||||
if params["cn"] == "" {
|
||||
return "", "", "please use Admin Name format like cn=xxx,ou=xxx,dc=example,dc=com"
|
||||
return "", "", fmt.Errorf("please use Admin Name format like cn=xxx,ou=xxx,dc=example,dc=com")
|
||||
}
|
||||
if params["ou"] == "" {
|
||||
return params["cn"], object.CasdoorOrganization, ""
|
||||
return params["cn"], object.CasdoorOrganization, nil
|
||||
}
|
||||
return params["cn"], params["ou"], ""
|
||||
return params["cn"], params["ou"], nil
|
||||
}
|
||||
|
||||
func getNameAndOrgFromFilter(baseDN, filter string) (string, string, int) {
|
||||
@ -50,7 +114,11 @@ func getNameAndOrgFromFilter(baseDN, filter string) (string, string, int) {
|
||||
return "", "", ldap.LDAPResultInvalidDNSyntax
|
||||
}
|
||||
|
||||
name, org, _ := getNameAndOrgFromDN(fmt.Sprintf("cn=%s,", getUsername(filter)) + baseDN)
|
||||
name, org, err := getNameAndOrgFromDN(fmt.Sprintf("cn=%s,", getUsername(filter)) + baseDN)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return name, org, ldap.LDAPResultSuccess
|
||||
}
|
||||
|
||||
@ -83,6 +151,92 @@ func stringInSlice(value string, list []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func buildUserFilterCondition(filter interface{}) (builder.Cond, error) {
|
||||
switch f := filter.(type) {
|
||||
case message.FilterAnd:
|
||||
conditions := make([]builder.Cond, len(f))
|
||||
for i, v := range f {
|
||||
cond, err := buildUserFilterCondition(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conditions[i] = cond
|
||||
}
|
||||
return builder.And(conditions...), nil
|
||||
case message.FilterOr:
|
||||
conditions := make([]builder.Cond, len(f))
|
||||
for i, v := range f {
|
||||
cond, err := buildUserFilterCondition(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conditions[i] = cond
|
||||
}
|
||||
return builder.Or(conditions...), nil
|
||||
case message.FilterNot:
|
||||
cond, err := buildUserFilterCondition(f.Filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return builder.Not{cond}, nil
|
||||
case message.FilterEqualityMatch:
|
||||
field, err := getUserFieldFromAttribute(string(f.AttributeDesc()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return builder.Eq{field: string(f.AssertionValue())}, nil
|
||||
case message.FilterPresent:
|
||||
field, err := getUserFieldFromAttribute(string(f))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return builder.NotNull{field}, nil
|
||||
case message.FilterGreaterOrEqual:
|
||||
field, err := getUserFieldFromAttribute(string(f.AttributeDesc()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return builder.Gte{field: string(f.AssertionValue())}, nil
|
||||
case message.FilterLessOrEqual:
|
||||
field, err := getUserFieldFromAttribute(string(f.AttributeDesc()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return builder.Lte{field: string(f.AssertionValue())}, nil
|
||||
case message.FilterSubstrings:
|
||||
field, err := getUserFieldFromAttribute(string(f.Type_()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var expr string
|
||||
for _, substring := range f.Substrings() {
|
||||
switch s := substring.(type) {
|
||||
case message.SubstringInitial:
|
||||
expr += string(s) + "%"
|
||||
continue
|
||||
case message.SubstringAny:
|
||||
expr += string(s) + "%"
|
||||
continue
|
||||
case message.SubstringFinal:
|
||||
expr += string(s)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return builder.Expr(field+" LIKE ?", expr), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("LDAP filter operation %#v not supported", f)
|
||||
}
|
||||
}
|
||||
|
||||
func buildSafeCondition(filter interface{}) builder.Cond {
|
||||
condition, err := buildUserFilterCondition(filter)
|
||||
if err != nil {
|
||||
log.Printf("err = %v", err.Error())
|
||||
return nil
|
||||
}
|
||||
return condition
|
||||
}
|
||||
|
||||
func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int) {
|
||||
var err error
|
||||
r := m.GetSearchRequest()
|
||||
@ -94,15 +248,14 @@ func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int)
|
||||
|
||||
if name == "*" && m.Client.IsOrgAdmin { // get all users from organization 'org'
|
||||
if m.Client.IsGlobalAdmin && org == "*" {
|
||||
|
||||
filteredUsers, err = object.GetGlobalUsers()
|
||||
filteredUsers, err = object.GetGlobalUsersWithFilter(buildSafeCondition(r.Filter()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return filteredUsers, ldap.LDAPResultSuccess
|
||||
}
|
||||
if m.Client.IsGlobalAdmin || org == m.Client.OrgName {
|
||||
filteredUsers, err = object.GetUsers(org)
|
||||
filteredUsers, err = object.GetUsersWithFilter(org, buildSafeCondition(r.Filter()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -144,7 +297,7 @@ func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int)
|
||||
return nil, ldap.LDAPResultNoSuchObject
|
||||
}
|
||||
|
||||
users, err := object.GetUsersByTag(org, name)
|
||||
users, err := object.GetUsersByTagWithFilter(org, name, buildSafeCondition(r.Filter()))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -178,24 +331,17 @@ func getUserPasswordWithType(user *object.User) string {
|
||||
}
|
||||
|
||||
func getAttribute(attributeName string, user *object.User) message.AttributeValue {
|
||||
switch attributeName {
|
||||
case "cn":
|
||||
return message.AttributeValue(user.Name)
|
||||
case "uid":
|
||||
return message.AttributeValue(user.Name)
|
||||
case "displayname":
|
||||
return message.AttributeValue(user.DisplayName)
|
||||
case "email":
|
||||
return message.AttributeValue(user.Email)
|
||||
case "mail":
|
||||
return message.AttributeValue(user.Email)
|
||||
case "mobile":
|
||||
return message.AttributeValue(user.Phone)
|
||||
case "title":
|
||||
return message.AttributeValue(user.Tag)
|
||||
case "userPassword":
|
||||
return message.AttributeValue(getUserPasswordWithType(user))
|
||||
default:
|
||||
v, ok := ldapAttributesMapping[attributeName]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return v.GetAttributeValue(user)
|
||||
}
|
||||
|
||||
func getUserFieldFromAttribute(attributeName string) (string, error) {
|
||||
v, ok := ldapAttributesMapping[attributeName]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("attribute %s not supported", attributeName)
|
||||
}
|
||||
return v.GetField()
|
||||
}
|
||||
|
87
ldap/util_test.go
Normal file
87
ldap/util_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
ber "github.com/go-asn1-ber/asn1-ber"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/lor00x/goldap/message"
|
||||
"github.com/xorm-io/builder"
|
||||
)
|
||||
|
||||
func args(exp ...interface{}) []interface{} {
|
||||
return exp
|
||||
}
|
||||
|
||||
func TestLdapFilterAsQuery(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
description string
|
||||
input string
|
||||
expectedExpr string
|
||||
expectedArgs []interface{}
|
||||
}{
|
||||
{"Should be SQL for FilterAnd", "(&(mail=2)(email=1))", "email=? AND email=?", args("2", "1")},
|
||||
{"Should be SQL for FilterOr", "(|(mail=2)(email=1))", "email=? OR email=?", args("2", "1")},
|
||||
{"Should be SQL for FilterNot", "(!(mail=2))", "NOT email=?", args("2")},
|
||||
{"Should be SQL for FilterEqualityMatch", "(mail=2)", "email=?", args("2")},
|
||||
{"Should be SQL for FilterPresent", "(mail=*)", "email IS NOT NULL", nil},
|
||||
{"Should be SQL for FilterGreaterOrEqual", "(mail>=admin)", "email>=?", args("admin")},
|
||||
{"Should be SQL for FilterLessOrEqual", "(mail<=admin)", "email<=?", args("admin")},
|
||||
{"Should be SQL for FilterSubstrings", "(mail=admin*ex*c*m)", "email LIKE ?", args("admin%ex%c%m")},
|
||||
}
|
||||
|
||||
for _, scenery := range scenarios {
|
||||
t.Run(scenery.description, func(t *testing.T) {
|
||||
searchRequest, err := buildLdapSearchRequest(scenery.input)
|
||||
if err != nil {
|
||||
assert.FailNow(t, "Unable to create searchRequest", err)
|
||||
}
|
||||
m, err := message.ReadLDAPMessage(message.NewBytes(0, searchRequest.Bytes()))
|
||||
if err != nil {
|
||||
assert.FailNow(t, "Unable to create searchRequest", err)
|
||||
}
|
||||
req := m.ProtocolOp().(message.SearchRequest)
|
||||
|
||||
cond, err := buildUserFilterCondition(req.Filter())
|
||||
if err != nil {
|
||||
assert.FailNow(t, "Unable to build condition", err)
|
||||
}
|
||||
expr, args, err := builder.ToSQL(cond)
|
||||
if err != nil {
|
||||
assert.FailNow(t, "Unable to build sql", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, scenery.expectedExpr, expr)
|
||||
assert.Equal(t, scenery.expectedArgs, args)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func buildLdapSearchRequest(filter string) (*ber.Packet, error) {
|
||||
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 1, "MessageID"))
|
||||
|
||||
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, goldap.ApplicationSearchRequest, nil, "Search Request")
|
||||
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "Base DN"))
|
||||
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, 0, "Scope"))
|
||||
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, 0, "Deref Aliases"))
|
||||
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 0, "Size Limit"))
|
||||
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 0, "Time Limit"))
|
||||
pkt.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, false, "Types Only"))
|
||||
// compile and encode filter
|
||||
filterPacket, err := goldap.CompileFilter(filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pkt.AppendChild(filterPacket)
|
||||
// encode attributes
|
||||
attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
|
||||
attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "*", "Attribute"))
|
||||
pkt.AppendChild(attributesPacket)
|
||||
|
||||
packet.AppendChild(pkt)
|
||||
|
||||
return packet, nil
|
||||
}
|
1
main.go
1
main.go
@ -34,7 +34,6 @@ func main() {
|
||||
object.InitFlag()
|
||||
object.InitAdapter()
|
||||
object.CreateTables()
|
||||
object.DoMigration()
|
||||
|
||||
object.InitDb()
|
||||
object.InitFromFile()
|
||||
|
@ -1,23 +0,0 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
@ -1,24 +0,0 @@
|
||||
apiVersion: v2
|
||||
name: casdoor
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# 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.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.0
|
||||
|
||||
# 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
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "1.16.0"
|
@ -1,22 +0,0 @@
|
||||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range $host := .Values.ingress.hosts }}
|
||||
{{- range .paths }}
|
||||
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "casdoor.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "casdoor.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "casdoor.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "casdoor.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||
{{- end }}
|
@ -1,62 +0,0 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "casdoor.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "casdoor.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "casdoor.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "casdoor.labels" -}}
|
||||
helm.sh/chart: {{ include "casdoor.chart" . }}
|
||||
{{ include "casdoor.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "casdoor.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "casdoor.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "casdoor.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "casdoor.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
@ -1,8 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ printf "%s-config" (include "casdoor.fullname" .) }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
data:
|
||||
app.conf: {{ tpl .Values.config . | toYaml | nindent 4 }}
|
@ -1,83 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "casdoor.fullname" . }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "casdoor.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ tpl .Values.config . | toYaml | sha256sum }}
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "casdoor.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "casdoor.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}/{{ .Values.image.name }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
# command: ["sleep", "100000000"]
|
||||
env:
|
||||
- name: RUNNING_IN_DOCKER
|
||||
value: "true"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.port }}
|
||||
protocol: TCP
|
||||
{{ if .Values.probe.liveness.enabled }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
{{ end }}
|
||||
{{ if .Values.probe.readiness.enabled }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
{{ end }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: config-volume
|
||||
mountPath: /conf
|
||||
volumes:
|
||||
- name: config-volume
|
||||
projected:
|
||||
defaultMode: 420
|
||||
sources:
|
||||
- configMap:
|
||||
items:
|
||||
- key: app.conf
|
||||
path: app.conf
|
||||
name: {{ printf "%s-config" (include "casdoor.fullname" .) }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
@ -1,28 +0,0 @@
|
||||
{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2beta1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "casdoor.fullname" . }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "casdoor.fullname" . }}
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
@ -1,61 +0,0 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- $fullName := include "casdoor.fullname" . -}}
|
||||
{{- $svcPort := .Values.service.port -}}
|
||||
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
|
||||
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
|
||||
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
{{- else -}}
|
||||
apiVersion: extensions/v1beta1
|
||||
{{- end }}
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
|
||||
pathType: {{ .pathType }}
|
||||
{{- end }}
|
||||
backend:
|
||||
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
|
||||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
number: {{ $svcPort }}
|
||||
{{- else }}
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: {{ $svcPort }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
@ -1,15 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "casdoor.fullname" . }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "casdoor.selectorLabels" . | nindent 4 }}
|
@ -1,12 +0,0 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "casdoor.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
@ -1,15 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "{{ include "casdoor.fullname" . }}-test-connection"
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: wget
|
||||
image: busybox
|
||||
command: ['wget']
|
||||
args: ['{{ include "casdoor.fullname" . }}:{{ .Values.service.port }}']
|
||||
restartPolicy: Never
|
@ -1,110 +0,0 @@
|
||||
# Default values for casdoor.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: casbin
|
||||
name: casdoor
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
# ref: https://casdoor.org/docs/basic/server-installation#via-ini-file
|
||||
config: |
|
||||
appname = casdoor
|
||||
httpport = {{ .Values.service.port }}
|
||||
runmode = dev
|
||||
SessionOn = true
|
||||
copyrequestbody = true
|
||||
driverName = sqlite
|
||||
dataSourceName = "file:ent?mode=memory&cache=shared&_fk=1"
|
||||
dbName = casdoor
|
||||
redisEndpoint =
|
||||
defaultStorageProvider =
|
||||
isCloudIntranet = false
|
||||
authState = "casdoor"
|
||||
socks5Proxy = ""
|
||||
verificationCodeTimeout = 10
|
||||
initScore = 0
|
||||
logPostOnly = true
|
||||
origin =
|
||||
enableGzip = true
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
probe:
|
||||
readiness:
|
||||
enabled: true
|
||||
liveness:
|
||||
enabled: true
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8000
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
className: ""
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
hosts:
|
||||
- host: chart-example.local
|
||||
paths:
|
||||
- path: /
|
||||
pathType: ImplementationSpecific
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - chart-example.local
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 100
|
||||
targetCPUUtilizationPercentage: 80
|
||||
# targetMemoryUtilizationPercentage: 80
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
@ -15,10 +15,11 @@
|
||||
package notification
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"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 {
|
||||
var req *http.Request
|
||||
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" {
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.PostForm = map[string][]string{
|
||||
c.paramName: {content},
|
||||
formValues := url.Values{}
|
||||
formValues.Set(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" {
|
||||
req, err = http.NewRequest(c.method, c.endpoint, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Add(c.paramName, content)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
httpClient := proxy.DefaultHttpClient
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -66,7 +70,7 @@ func (c *HttpNotificationClient) Send(ctx context.Context, subject string, conte
|
||||
defer resp.Body.Close()
|
||||
|
||||
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
|
||||
|
@ -24,6 +24,12 @@ import (
|
||||
"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 {
|
||||
Name string `json:"name"`
|
||||
Visible bool `json:"visible"`
|
||||
@ -31,12 +37,13 @@ type SignupItem struct {
|
||||
Prompted bool `json:"prompted"`
|
||||
Label string `json:"label"`
|
||||
Placeholder string `json:"placeholder"`
|
||||
Regex string `json:"regex"`
|
||||
Rule string `json:"rule"`
|
||||
}
|
||||
|
||||
type SamlItem struct {
|
||||
Name string `json:"name"`
|
||||
NameFormat string `json:"nameformat"`
|
||||
NameFormat string `json:"nameFormat"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
@ -63,6 +70,7 @@ type Application struct {
|
||||
OrgChoiceMode string `json:"orgChoiceMode"`
|
||||
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
|
||||
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
|
||||
SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"`
|
||||
SignupItems []*SignupItem `xorm:"varchar(2000)" json:"signupItems"`
|
||||
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
|
||||
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
|
||||
@ -75,6 +83,7 @@ type Application struct {
|
||||
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
||||
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
|
||||
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
|
||||
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
|
||||
ExpireInHours int `json:"expireInHours"`
|
||||
RefreshExpireInHours int `json:"refreshExpireInHours"`
|
||||
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
|
||||
@ -90,6 +99,9 @@ type Application struct {
|
||||
FormOffset int `json:"formOffset"`
|
||||
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
|
||||
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
|
||||
|
||||
FailedSigninLimit int `json:"failedSigninLimit"`
|
||||
FailedSigninFrozenTime int `json:"failedSigninFrozenTime"`
|
||||
}
|
||||
|
||||
func GetApplicationCount(owner, field, value string) (int64, error) {
|
||||
@ -188,6 +200,30 @@ func extendApplicationWithOrg(application *Application) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func extendApplicationWithSigninMethods(application *Application) (err error) {
|
||||
if len(application.SigninMethods) == 0 {
|
||||
if application.EnablePassword {
|
||||
signinMethod := &SigninMethod{Name: "Password", DisplayName: "Password", Rule: "All"}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if len(application.SigninMethods) == 0 {
|
||||
signinMethod := &SigninMethod{Name: "Password", DisplayName: "Password", Rule: "All"}
|
||||
application.SigninMethods = append(application.SigninMethods, signinMethod)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getApplication(owner string, name string) (*Application, error) {
|
||||
if owner == "" || name == "" {
|
||||
return nil, nil
|
||||
@ -210,6 +246,11 @@ func getApplication(owner string, name string) (*Application, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = extendApplicationWithSigninMethods(&application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &application, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
@ -234,6 +275,11 @@ func GetApplicationByOrganizationName(organization string) (*Application, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = extendApplicationWithSigninMethods(&application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &application, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
@ -281,6 +327,11 @@ func GetApplicationByClientId(clientId string) (*Application, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = extendApplicationWithSigninMethods(&application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &application, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
@ -297,6 +348,17 @@ func GetMaskedApplication(application *Application, userId string) *Application
|
||||
return nil
|
||||
}
|
||||
|
||||
if application.TokenFields == nil {
|
||||
application.TokenFields = []string{}
|
||||
}
|
||||
|
||||
if application.FailedSigninLimit == 0 {
|
||||
application.FailedSigninLimit = DefaultFailedSigninLimit
|
||||
}
|
||||
if application.FailedSigninFrozenTime == 0 {
|
||||
application.FailedSigninFrozenTime = DefaultFailedSigninFrozenTime
|
||||
}
|
||||
|
||||
if userId != "" {
|
||||
if isUserIdGlobalAdmin(userId) {
|
||||
return application
|
||||
@ -319,6 +381,9 @@ func GetMaskedApplication(application *Application, userId string) *Application
|
||||
if application.OrganizationObj.DefaultPassword != "" {
|
||||
application.OrganizationObj.DefaultPassword = "***"
|
||||
}
|
||||
if application.OrganizationObj.MasterVerificationCode != "" {
|
||||
application.OrganizationObj.MasterVerificationCode = "***"
|
||||
}
|
||||
if application.OrganizationObj.PasswordType != "" {
|
||||
application.OrganizationObj.PasswordType = "***"
|
||||
}
|
||||
@ -345,6 +410,34 @@ func GetMaskedApplications(applications []*Application, userId string) []*Applic
|
||||
return applications
|
||||
}
|
||||
|
||||
func GetAllowedApplications(applications []*Application, userId string) ([]*Application, error) {
|
||||
if userId == "" || isUserIdGlobalAdmin(userId) {
|
||||
return applications, nil
|
||||
}
|
||||
|
||||
user, err := GetUser(userId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user != nil && user.IsAdmin {
|
||||
return applications, nil
|
||||
}
|
||||
|
||||
res := []*Application{}
|
||||
for _, application := range applications {
|
||||
var allowed bool
|
||||
allowed, err = CheckLoginPermission(userId, application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if allowed {
|
||||
res = append(res, application)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func UpdateApplication(id string, application *Application) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
oldApplication, err := getApplication(owner, name)
|
||||
@ -451,6 +544,69 @@ func (application *Application) IsRedirectUriValid(redirectUri string) bool {
|
||||
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) IsPasswordWithLdapEnabled() bool {
|
||||
if len(application.SigninMethods) == 0 {
|
||||
return application.EnablePassword
|
||||
} else {
|
||||
for _, signinMethod := range application.SigninMethods {
|
||||
if signinMethod.Name == "Password" && signinMethod.Rule == "All" {
|
||||
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 (application *Application) IsLdapEnabled() bool {
|
||||
if len(application.SigninMethods) > 0 {
|
||||
for _, signinMethod := range application.SigninMethods {
|
||||
if signinMethod.Name == "LDAP" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsOriginAllowed(origin string) (bool, error) {
|
||||
applications, err := GetApplications("")
|
||||
if err != nil {
|
||||
@ -545,7 +701,7 @@ func applicationChangeTrigger(oldName string, newName string) error {
|
||||
}
|
||||
}
|
||||
permissions[i].Resources = permissionResoureces
|
||||
_, err = session.Where("name=?", permissions[i].Name).Update(permissions[i])
|
||||
_, err = session.Where("owner=?", permissions[i].Owner).Where("name=?", permissions[i].Name).Update(permissions[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ func GetGlobalCertsCount(field, value string) (int64, error) {
|
||||
return session.Count(&Cert{})
|
||||
}
|
||||
|
||||
func GetGlobleCerts() ([]*Cert, error) {
|
||||
func GetGlobalCerts() ([]*Cert, error) {
|
||||
certs := []*Cert{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&certs)
|
||||
if err != nil {
|
||||
@ -163,6 +163,12 @@ func UpdateCert(id string, cert *Cert) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
err := cert.populateContent()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@ -172,10 +178,9 @@ func UpdateCert(id string, cert *Cert) (bool, error) {
|
||||
}
|
||||
|
||||
func AddCert(cert *Cert) (bool, error) {
|
||||
if cert.Certificate == "" || cert.PrivateKey == "" {
|
||||
certificate, privateKey := generateRsaKeys(cert.BitSize, cert.ExpireInYears, cert.Name, cert.Owner)
|
||||
cert.Certificate = certificate
|
||||
cert.PrivateKey = privateKey
|
||||
err := cert.populateContent()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.Insert(cert)
|
||||
@ -199,6 +204,20 @@ func (p *Cert) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
||||
}
|
||||
|
||||
func (p *Cert) populateContent() error {
|
||||
if p.Certificate == "" || p.PrivateKey == "" {
|
||||
certificate, privateKey, err := generateRsaKeys(p.BitSize, p.ExpireInYears, p.Name, p.Owner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Certificate = certificate
|
||||
p.PrivateKey = privateKey
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCertByApplication(application *Application) (*Cert, error) {
|
||||
if application.Cert != "" {
|
||||
return getCertByName(application.Cert)
|
||||
|
214
object/check.go
214
object/check.go
@ -16,6 +16,7 @@ package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
@ -28,93 +29,93 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
SigninWrongTimesLimit = 5
|
||||
LastSignWrongTimeDuration = time.Minute * 15
|
||||
DefaultFailedSigninLimit = 5
|
||||
DefaultFailedSigninFrozenTime = 15
|
||||
)
|
||||
|
||||
func CheckUserSignup(application *Application, organization *Organization, form *form.AuthForm, lang string) string {
|
||||
func CheckUserSignup(application *Application, organization *Organization, authForm *form.AuthForm, lang string) string {
|
||||
if organization == nil {
|
||||
return i18n.Translate(lang, "check:Organization does not exist")
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Username") {
|
||||
if len(form.Username) <= 1 {
|
||||
if len(authForm.Username) <= 1 {
|
||||
return i18n.Translate(lang, "check:Username must have at least 2 characters")
|
||||
}
|
||||
if unicode.IsDigit(rune(form.Username[0])) {
|
||||
if unicode.IsDigit(rune(authForm.Username[0])) {
|
||||
return i18n.Translate(lang, "check:Username cannot start with a digit")
|
||||
}
|
||||
if util.IsEmailValid(form.Username) {
|
||||
if util.IsEmailValid(authForm.Username) {
|
||||
return i18n.Translate(lang, "check:Username cannot be an email address")
|
||||
}
|
||||
if util.ReWhiteSpace.MatchString(form.Username) {
|
||||
if util.ReWhiteSpace.MatchString(authForm.Username) {
|
||||
return i18n.Translate(lang, "check:Username cannot contain white spaces")
|
||||
}
|
||||
|
||||
if msg := CheckUsername(form.Username, lang); msg != "" {
|
||||
if msg := CheckUsername(authForm.Username, lang); msg != "" {
|
||||
return msg
|
||||
}
|
||||
|
||||
if HasUserByField(organization.Name, "name", form.Username) {
|
||||
if HasUserByField(organization.Name, "name", authForm.Username) {
|
||||
return i18n.Translate(lang, "check:Username already exists")
|
||||
}
|
||||
if HasUserByField(organization.Name, "email", form.Email) {
|
||||
if HasUserByField(organization.Name, "email", authForm.Email) {
|
||||
return i18n.Translate(lang, "check:Email already exists")
|
||||
}
|
||||
if HasUserByField(organization.Name, "phone", form.Phone) {
|
||||
if HasUserByField(organization.Name, "phone", authForm.Phone) {
|
||||
return i18n.Translate(lang, "check:Phone already exists")
|
||||
}
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Password") {
|
||||
msg := CheckPasswordComplexityByOrg(organization, form.Password)
|
||||
msg := CheckPasswordComplexityByOrg(organization, authForm.Password)
|
||||
if msg != "" {
|
||||
return msg
|
||||
}
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Email") {
|
||||
if form.Email == "" {
|
||||
if authForm.Email == "" {
|
||||
if application.IsSignupItemRequired("Email") {
|
||||
return i18n.Translate(lang, "check:Email cannot be empty")
|
||||
}
|
||||
} else {
|
||||
if HasUserByField(organization.Name, "email", form.Email) {
|
||||
if HasUserByField(organization.Name, "email", authForm.Email) {
|
||||
return i18n.Translate(lang, "check:Email already exists")
|
||||
} else if !util.IsEmailValid(form.Email) {
|
||||
} else if !util.IsEmailValid(authForm.Email) {
|
||||
return i18n.Translate(lang, "check:Email is invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Phone") {
|
||||
if form.Phone == "" {
|
||||
if authForm.Phone == "" {
|
||||
if application.IsSignupItemRequired("Phone") {
|
||||
return i18n.Translate(lang, "check:Phone cannot be empty")
|
||||
}
|
||||
} else {
|
||||
if HasUserByField(organization.Name, "phone", form.Phone) {
|
||||
if HasUserByField(organization.Name, "phone", authForm.Phone) {
|
||||
return i18n.Translate(lang, "check:Phone already exists")
|
||||
} else if !util.IsPhoneAllowInRegin(form.CountryCode, organization.CountryCodes) {
|
||||
} else if !util.IsPhoneAllowInRegin(authForm.CountryCode, organization.CountryCodes) {
|
||||
return i18n.Translate(lang, "check:Your region is not allow to signup by phone")
|
||||
} else if !util.IsPhoneValid(form.Phone, form.CountryCode) {
|
||||
} else if !util.IsPhoneValid(authForm.Phone, authForm.CountryCode) {
|
||||
return i18n.Translate(lang, "check:Phone number is invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Display name") {
|
||||
if application.GetSignupItemRule("Display name") == "First, last" && (form.FirstName != "" || form.LastName != "") {
|
||||
if form.FirstName == "" {
|
||||
if application.GetSignupItemRule("Display name") == "First, last" && (authForm.FirstName != "" || authForm.LastName != "") {
|
||||
if authForm.FirstName == "" {
|
||||
return i18n.Translate(lang, "check:FirstName cannot be blank")
|
||||
} else if form.LastName == "" {
|
||||
} else if authForm.LastName == "" {
|
||||
return i18n.Translate(lang, "check:LastName cannot be blank")
|
||||
}
|
||||
} else {
|
||||
if form.Name == "" {
|
||||
if authForm.Name == "" {
|
||||
return i18n.Translate(lang, "check:DisplayName cannot be blank")
|
||||
} else if application.GetSignupItemRule("Display name") == "Real name" {
|
||||
if !isValidRealName(form.Name) {
|
||||
if !isValidRealName(authForm.Name) {
|
||||
return i18n.Translate(lang, "check:DisplayName is not valid real name")
|
||||
}
|
||||
}
|
||||
@ -122,65 +123,93 @@ func CheckUserSignup(application *Application, organization *Organization, form
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Affiliation") {
|
||||
if form.Affiliation == "" {
|
||||
if authForm.Affiliation == "" {
|
||||
return i18n.Translate(lang, "check:Affiliation cannot be blank")
|
||||
}
|
||||
}
|
||||
|
||||
if len(application.InvitationCodes) > 0 {
|
||||
if form.InvitationCode == "" {
|
||||
if authForm.InvitationCode == "" {
|
||||
if application.IsSignupItemRequired("Invitation code") {
|
||||
return i18n.Translate(lang, "check:Invitation code cannot be blank")
|
||||
}
|
||||
} else {
|
||||
if !util.InSlice(application.InvitationCodes, form.InvitationCode) {
|
||||
if !util.InSlice(application.InvitationCodes, authForm.InvitationCode) {
|
||||
return i18n.Translate(lang, "check:Invitation code is invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func checkSigninErrorTimes(user *User, lang string) string {
|
||||
if user.SigninWrongTimes >= SigninWrongTimesLimit {
|
||||
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
|
||||
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
|
||||
minutes := int(LastSignWrongTimeDuration.Minutes() - passedTime.Minutes())
|
||||
|
||||
// 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 {
|
||||
return fmt.Sprintf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), minutes)
|
||||
for _, signupItem := range application.SignupItems {
|
||||
if signupItem.Regex == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// reset the error times
|
||||
user.SigninWrongTimes = 0
|
||||
isString, value := form.GetAuthFormFieldValue(authForm, signupItem.Name)
|
||||
if !isString {
|
||||
continue
|
||||
}
|
||||
|
||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, false)
|
||||
regexSignupItem, err := regexp.Compile(signupItem.Regex)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
matched := regexSignupItem.MatchString(value)
|
||||
if !matched {
|
||||
return fmt.Sprintf(i18n.Translate(lang, "check:The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\""), value, signupItem.Name, application.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func CheckPassword(user *User, password string, lang string, options ...bool) string {
|
||||
func checkSigninErrorTimes(user *User, lang string) error {
|
||||
failedSigninLimit, failedSigninFrozenTime, err := GetFailedSigninConfigByUser(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.SigninWrongTimes >= failedSigninLimit {
|
||||
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
|
||||
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
|
||||
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
|
||||
if minutes > 0 {
|
||||
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"), minutes)
|
||||
}
|
||||
|
||||
// reset the error times
|
||||
user.SigninWrongTimes = 0
|
||||
|
||||
_, err := UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, false)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckPassword(user *User, password string, lang string, options ...bool) error {
|
||||
enableCaptcha := false
|
||||
if len(options) > 0 {
|
||||
enableCaptcha = options[0]
|
||||
}
|
||||
// check the login error times
|
||||
if !enableCaptcha {
|
||||
if msg := checkSigninErrorTimes(user, lang); msg != "" {
|
||||
return msg
|
||||
err := checkSigninErrorTimes(user, lang)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
organization, err := GetOrganizationByUser(user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if organization == nil {
|
||||
return i18n.Translate(lang, "check:Organization does not exist")
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Organization does not exist"))
|
||||
}
|
||||
|
||||
passwordType := user.PasswordType
|
||||
@ -191,19 +220,17 @@ func CheckPassword(user *User, password string, lang string, options ...bool) st
|
||||
if credManager != nil {
|
||||
if organization.MasterPassword != "" {
|
||||
if credManager.IsPasswordCorrect(password, organization.MasterPassword, "", organization.PasswordSalt) {
|
||||
resetUserSigninErrorTimes(user)
|
||||
return ""
|
||||
return resetUserSigninErrorTimes(user)
|
||||
}
|
||||
}
|
||||
|
||||
if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) {
|
||||
resetUserSigninErrorTimes(user)
|
||||
return ""
|
||||
return resetUserSigninErrorTimes(user)
|
||||
}
|
||||
|
||||
return recordSigninErrorInfo(user, lang, enableCaptcha)
|
||||
} else {
|
||||
return fmt.Sprintf(i18n.Translate(lang, "check:unsupported password type: %s"), organization.PasswordType)
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:unsupported password type: %s"), organization.PasswordType)
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,10 +244,10 @@ func CheckPasswordComplexity(user *User, password string) string {
|
||||
return CheckPasswordComplexityByOrg(organization, password)
|
||||
}
|
||||
|
||||
func checkLdapUserPassword(user *User, password string, lang string) string {
|
||||
func checkLdapUserPassword(user *User, password string, lang string) error {
|
||||
ldaps, err := GetLdaps(user.Owner)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
return err
|
||||
}
|
||||
|
||||
ldapLoginSuccess := false
|
||||
@ -237,65 +264,96 @@ func checkLdapUserPassword(user *User, password string, lang string) string {
|
||||
|
||||
searchResult, err := conn.Conn.Search(searchReq)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if len(searchResult.Entries) == 0 {
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
if len(searchResult.Entries) > 1 {
|
||||
return i18n.Translate(lang, "check:Multiple accounts with same uid, please check your ldap server")
|
||||
conn.Close()
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Multiple accounts with same uid, please check your ldap server"))
|
||||
}
|
||||
|
||||
hit = true
|
||||
dn := searchResult.Entries[0].DN
|
||||
if err := conn.Conn.Bind(dn, password); err == nil {
|
||||
if err = conn.Conn.Bind(dn, password); err == nil {
|
||||
ldapLoginSuccess = true
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
if !ldapLoginSuccess {
|
||||
if !hit {
|
||||
return "user not exist"
|
||||
return fmt.Errorf("user not exist")
|
||||
}
|
||||
return i18n.Translate(lang, "check:LDAP user name or password incorrect")
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:LDAP user name or password incorrect"))
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckUserPassword(organization string, username string, password string, lang string, options ...bool) (*User, string) {
|
||||
func CheckUserPassword(organization string, username string, password string, lang string, options ...bool) (*User, error) {
|
||||
enableCaptcha := false
|
||||
isSigninViaLdap := false
|
||||
isPasswordWithLdapEnabled := false
|
||||
if len(options) > 0 {
|
||||
enableCaptcha = options[0]
|
||||
isSigninViaLdap = options[1]
|
||||
isPasswordWithLdapEnabled = options[2]
|
||||
}
|
||||
user, err := GetUserByFields(organization, username)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if user == nil || user.IsDeleted {
|
||||
return nil, fmt.Sprintf(i18n.Translate(lang, "general:The user: %s doesn't exist"), util.GetId(organization, username))
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "general:The user: %s doesn't exist"), util.GetId(organization, username))
|
||||
}
|
||||
|
||||
if user.IsForbidden {
|
||||
return nil, i18n.Translate(lang, "check:The user is forbidden to sign in, please contact the administrator")
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user is forbidden to sign in, please contact the administrator"))
|
||||
}
|
||||
|
||||
if isSigninViaLdap {
|
||||
if user.Ldap == "" {
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
|
||||
}
|
||||
}
|
||||
|
||||
if user.Ldap != "" {
|
||||
// ONLY for ldap users
|
||||
if msg := checkLdapUserPassword(user, password, lang); msg != "" {
|
||||
if msg == "user not exist" {
|
||||
return nil, fmt.Sprintf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
|
||||
if !isSigninViaLdap && !isPasswordWithLdapEnabled {
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect"))
|
||||
}
|
||||
|
||||
// check the login error times
|
||||
if !enableCaptcha {
|
||||
err = checkSigninErrorTimes(user, lang)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, msg
|
||||
}
|
||||
|
||||
// only for LDAP users
|
||||
err = checkLdapUserPassword(user, password, lang)
|
||||
if err != nil {
|
||||
if err.Error() == "user not exist" {
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
|
||||
}
|
||||
|
||||
return nil, recordSigninErrorInfo(user, lang, enableCaptcha)
|
||||
}
|
||||
} else {
|
||||
if msg := CheckPassword(user, password, lang, enableCaptcha); msg != "" {
|
||||
return nil, msg
|
||||
err = CheckPassword(user, password, lang, enableCaptcha)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return user, ""
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func CheckUserPermission(requestUserId, userId string, strict bool, lang string) (bool, error) {
|
||||
@ -308,7 +366,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool, lang string)
|
||||
if userId != "" {
|
||||
targetUser, err := GetUser(userId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
if targetUser == nil {
|
||||
@ -366,7 +424,7 @@ func CheckLoginPermission(userId string, application *Application) (bool, error)
|
||||
allowCount := 0
|
||||
denyCount := 0
|
||||
for _, permission := range permissions {
|
||||
if !permission.IsEnabled || permission.ResourceType != "Application" || !permission.isResourceHit(application.Name) {
|
||||
if !permission.IsEnabled || permission.State != "Approved" || permission.ResourceType != "Application" || !permission.isResourceHit(application.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -471,7 +529,13 @@ func CheckToEnableCaptcha(application *Application, organization, username strin
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return user != nil && user.SigninWrongTimes >= SigninWrongTimesLimit, nil
|
||||
|
||||
failedSigninLimit := application.FailedSigninLimit
|
||||
if failedSigninLimit == 0 {
|
||||
failedSigninLimit = DefaultFailedSigninLimit
|
||||
}
|
||||
|
||||
return user != nil && user.SigninWrongTimes >= failedSigninLimit, nil
|
||||
}
|
||||
return providerItem.Rule == "Always", nil
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ var (
|
||||
regexLowerCase = regexp.MustCompile(`[a-z]`)
|
||||
regexUpperCase = regexp.MustCompile(`[A-Z]`)
|
||||
regexDigit = regexp.MustCompile(`\d`)
|
||||
regexSpecial = regexp.MustCompile(`[!@#$%^&*]`)
|
||||
regexSpecial = regexp.MustCompile("[!-/:-@[-`{-~]")
|
||||
)
|
||||
|
||||
func isValidOption_AtLeast6(password string) string {
|
||||
|
@ -36,38 +36,70 @@ func isValidRealName(s string) bool {
|
||||
return reRealName.MatchString(s)
|
||||
}
|
||||
|
||||
func resetUserSigninErrorTimes(user *User) {
|
||||
func resetUserSigninErrorTimes(user *User) error {
|
||||
// if the password is correct and wrong times is not zero, reset the error times
|
||||
if user.SigninWrongTimes == 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
user.SigninWrongTimes = 0
|
||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
|
||||
_, err := UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
|
||||
return err
|
||||
}
|
||||
|
||||
func recordSigninErrorInfo(user *User, lang string, options ...bool) string {
|
||||
func GetFailedSigninConfigByUser(user *User) (int, int, error) {
|
||||
application, err := GetApplicationByUser(user)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
failedSigninLimit := application.FailedSigninLimit
|
||||
if failedSigninLimit == 0 {
|
||||
failedSigninLimit = DefaultFailedSigninLimit
|
||||
}
|
||||
|
||||
failedSigninFrozenTime := application.FailedSigninFrozenTime
|
||||
if failedSigninFrozenTime == 0 {
|
||||
failedSigninFrozenTime = DefaultFailedSigninFrozenTime
|
||||
}
|
||||
|
||||
return failedSigninLimit, failedSigninFrozenTime, nil
|
||||
}
|
||||
|
||||
func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
|
||||
enableCaptcha := false
|
||||
if len(options) > 0 {
|
||||
enableCaptcha = options[0]
|
||||
}
|
||||
|
||||
failedSigninLimit, failedSigninFrozenTime, errSignin := GetFailedSigninConfigByUser(user)
|
||||
if errSignin != nil {
|
||||
return errSignin
|
||||
}
|
||||
|
||||
// increase failed login count
|
||||
if user.SigninWrongTimes < SigninWrongTimesLimit {
|
||||
if user.SigninWrongTimes < failedSigninLimit {
|
||||
user.SigninWrongTimes++
|
||||
}
|
||||
|
||||
if user.SigninWrongTimes >= SigninWrongTimesLimit {
|
||||
if user.SigninWrongTimes >= failedSigninLimit {
|
||||
// record the latest failed login time
|
||||
user.LastSigninWrongTime = time.Now().UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// update user
|
||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
|
||||
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
|
||||
if leftChances == 0 && enableCaptcha {
|
||||
return fmt.Sprint(i18n.Translate(lang, "check:password or code is incorrect"))
|
||||
} else if leftChances >= 0 {
|
||||
return fmt.Sprintf(i18n.Translate(lang, "check:password or code is incorrect, you have %d remaining chances"), leftChances)
|
||||
_, err := UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
leftChances := failedSigninLimit - user.SigninWrongTimes
|
||||
if leftChances == 0 && enableCaptcha {
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect"))
|
||||
} else if leftChances >= 0 {
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect, you have %d remaining chances"), leftChances)
|
||||
}
|
||||
|
||||
// don't show the chance error message if the user has no chance left
|
||||
return fmt.Sprintf(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 {
|
||||
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
|
||||
if fromAddress == "" {
|
||||
|
@ -125,6 +125,10 @@ func (enforcer *Enforcer) GetId() string {
|
||||
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 {
|
||||
if enforcer.Enforcer != nil {
|
||||
return nil
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/builder"
|
||||
"github.com/xorm-io/core"
|
||||
@ -224,7 +225,8 @@ func GetGroupUserCount(groupId string, field, value string) (int64, error) {
|
||||
if field == "" && value == "" {
|
||||
return int64(len(names)), nil
|
||||
} else {
|
||||
return ormer.Engine.Table("user").
|
||||
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||
return ormer.Engine.Table(tableNamePrefix+"user").
|
||||
Where("owner = ?", owner).In("name", names).
|
||||
And(fmt.Sprintf("user.%s like ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
||||
Count()
|
||||
@ -239,7 +241,9 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session := ormer.Engine.Table("user").
|
||||
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||
prefixedUserTable := tableNamePrefix + "user"
|
||||
session := ormer.Engine.Table(prefixedUserTable).
|
||||
Where("owner = ?", owner).In("name", names)
|
||||
|
||||
if offset != -1 && limit != -1 {
|
||||
@ -247,16 +251,19 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
|
||||
}
|
||||
|
||||
if field != "" && value != "" {
|
||||
session = session.And(fmt.Sprintf("user.%s like ?", util.CamelToSnakeCase(field)), "%"+value+"%")
|
||||
session = session.And(fmt.Sprintf("%s.%s like ?", prefixedUserTable, util.CamelToSnakeCase(field)), "%"+value+"%")
|
||||
}
|
||||
|
||||
if sortField == "" || sortOrder == "" {
|
||||
sortField = "created_time"
|
||||
}
|
||||
|
||||
orderQuery := fmt.Sprintf("%s.%s", prefixedUserTable, util.SnakeString(sortField))
|
||||
|
||||
if sortOrder == "ascend" {
|
||||
session = session.Asc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||
session = session.Asc(orderQuery)
|
||||
} else {
|
||||
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||
session = session.Desc(orderQuery)
|
||||
}
|
||||
|
||||
err = session.Find(&users)
|
||||
@ -271,7 +278,9 @@ func GetGroupUsers(groupId string) ([]*User, error) {
|
||||
users := []*User{}
|
||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = ormer.Engine.Where("owner = ?", owner).In("name", names).Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -303,6 +312,9 @@ func GroupChangeTrigger(oldName, newName string) error {
|
||||
|
||||
groups := []*Group{}
|
||||
err = session.Where("parent_id = ?", oldName).Find(&groups)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, group := range groups {
|
||||
group.ParentId = newName
|
||||
_, err := session.ID(core.PK{group.Owner, group.Name}).Cols("parent_id").Update(group)
|
||||
|
@ -180,6 +180,11 @@ func initBuiltInApplication() {
|
||||
Providers: []*ProviderItem{
|
||||
{Name: "provider_captcha_default", CanSignUp: false, CanSignIn: false, CanUnlink: false, Prompted: false, SignupGroup: "", Rule: "None", Provider: nil},
|
||||
},
|
||||
SigninMethods: []*SigninMethod{
|
||||
{Name: "Password", DisplayName: "Password", Rule: "All"},
|
||||
{Name: "Verification code", DisplayName: "Verification code", Rule: "All"},
|
||||
{Name: "WebAuthn", DisplayName: "WebAuthn", Rule: "None"},
|
||||
},
|
||||
SignupItems: []*SignupItem{
|
||||
{Name: "ID", Visible: false, Required: true, Prompted: false, Rule: "Random"},
|
||||
{Name: "Username", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||
@ -192,6 +197,7 @@ func initBuiltInApplication() {
|
||||
},
|
||||
Tags: []string{},
|
||||
RedirectUris: []string{},
|
||||
TokenFields: []string{},
|
||||
ExpireInHours: 168,
|
||||
FormOffset: 2,
|
||||
}
|
||||
@ -396,15 +402,22 @@ func initBuiltInPermission() {
|
||||
Name: "permission-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Built-in Permission",
|
||||
Description: "Built-in Permission",
|
||||
Users: []string{"built-in/*"},
|
||||
Groups: []string{},
|
||||
Roles: []string{},
|
||||
Domains: []string{},
|
||||
Model: "model-built-in",
|
||||
Adapter: "",
|
||||
ResourceType: "Application",
|
||||
Resources: []string{"app-built-in"},
|
||||
Actions: []string{"Read", "Write", "Admin"},
|
||||
Effect: "Allow",
|
||||
IsEnabled: true,
|
||||
Submitter: "admin",
|
||||
Approver: "admin",
|
||||
ApproveTime: util.GetCurrentTime(),
|
||||
State: "Approved",
|
||||
}
|
||||
_, err = AddPermission(permission)
|
||||
if err != nil {
|
||||
|
@ -136,17 +136,23 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
|
||||
if application.Providers == nil {
|
||||
application.Providers = []*ProviderItem{}
|
||||
}
|
||||
if application.SigninMethods == nil {
|
||||
application.SigninMethods = []*SigninMethod{}
|
||||
}
|
||||
if application.SignupItems == nil {
|
||||
application.SignupItems = []*SignupItem{}
|
||||
}
|
||||
if application.GrantTypes == nil {
|
||||
application.GrantTypes = []string{}
|
||||
}
|
||||
if application.Tags == nil {
|
||||
application.Tags = []string{}
|
||||
}
|
||||
if application.RedirectUris == nil {
|
||||
application.RedirectUris = []string{}
|
||||
}
|
||||
if application.Tags == nil {
|
||||
application.Tags = []string{}
|
||||
if application.TokenFields == nil {
|
||||
application.TokenFields = []string{}
|
||||
}
|
||||
}
|
||||
for _, permission := range data.Permissions {
|
||||
|
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)
|
||||
}
|
@ -100,6 +100,7 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) e
|
||||
|
||||
users, err := conn.GetLdapUsers(ldap)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
|
||||
continue
|
||||
}
|
||||
@ -111,6 +112,8 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) e
|
||||
} else {
|
||||
logs.Info(fmt.Sprintf("ldap autosync success, %d new users, %d existing users", len(users)-len(existed), len(existed)))
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/thanhpk/randstr"
|
||||
@ -81,6 +82,17 @@ func (ldap *Ldap) GetLdapConn() (c *LdapConn, err error) {
|
||||
return &LdapConn{Conn: conn, IsAD: isAD}, nil
|
||||
}
|
||||
|
||||
func (l *LdapConn) Close() {
|
||||
if l.Conn == nil {
|
||||
return
|
||||
}
|
||||
|
||||
err := l.Conn.Unbind()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func isMicrosoftAD(Conn *goldap.Conn) (bool, error) {
|
||||
SearchFilter := "(objectClass=*)"
|
||||
SearchAttributes := []string{"vendorname", "vendorversion", "isGlobalCatalogReady", "forestFunctionality"}
|
||||
@ -305,7 +317,7 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
name, err := syncUser.buildLdapUserName()
|
||||
name, err := syncUser.buildLdapUserName(owner)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -345,7 +357,8 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
|
||||
func GetExistUuids(owner string, uuids []string) ([]string, error) {
|
||||
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)
|
||||
if err != nil {
|
||||
return existUuids, err
|
||||
@ -354,10 +367,10 @@ func GetExistUuids(owner string, uuids []string) ([]string, error) {
|
||||
return existUuids, nil
|
||||
}
|
||||
|
||||
func (ldapUser *LdapUser) buildLdapUserName() (string, error) {
|
||||
func (ldapUser *LdapUser) buildLdapUserName(owner string) (string, error) {
|
||||
user := User{}
|
||||
uidWithNumber := fmt.Sprintf("%s_%s", ldapUser.Uid, ldapUser.UidNumber)
|
||||
has, err := ormer.Engine.Where("name = ? or name = ?", ldapUser.Uid, uidWithNumber).Get(&user)
|
||||
has, err := ormer.Engine.Where("owner = ? and (name = ? or name = ?)", owner, ldapUser.Uid, uidWithNumber).Get(&user)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import "github.com/xorm-io/xorm/migrate"
|
||||
|
||||
type Migrator interface {
|
||||
IsMigrationNeeded() bool
|
||||
DoMigration() *migrate.Migration
|
||||
}
|
||||
|
||||
func DoMigration() {
|
||||
migrators := []Migrator{
|
||||
&Migrator_1_101_0_PR_1083{},
|
||||
&Migrator_1_235_0_PR_1530{},
|
||||
&Migrator_1_240_0_PR_1539{},
|
||||
&Migrator_1_314_0_PR_1841{},
|
||||
// more migrators add here in chronological order...
|
||||
}
|
||||
|
||||
migrations := []*migrate.Migration{}
|
||||
|
||||
for _, migrator := range migrators {
|
||||
if migrator.IsMigrationNeeded() {
|
||||
migrations = append(migrations, migrator.DoMigration())
|
||||
}
|
||||
}
|
||||
|
||||
options := &migrate.Options{
|
||||
TableName: "migration",
|
||||
IDColumnName: "id",
|
||||
}
|
||||
|
||||
m := migrate.New(ormer.Engine, options, migrations)
|
||||
err := m.Migrate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/xorm-io/xorm"
|
||||
"github.com/xorm-io/xorm/migrate"
|
||||
)
|
||||
|
||||
type Migrator_1_101_0_PR_1083 struct{}
|
||||
|
||||
func (*Migrator_1_101_0_PR_1083) IsMigrationNeeded() bool {
|
||||
exist1, _ := ormer.Engine.IsTableExist("model")
|
||||
exist2, _ := ormer.Engine.IsTableExist("permission")
|
||||
exist3, _ := ormer.Engine.IsTableExist("permission_rule")
|
||||
|
||||
if exist1 && exist2 && exist3 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (*Migrator_1_101_0_PR_1083) DoMigration() *migrate.Migration {
|
||||
migration := migrate.Migration{
|
||||
ID: "20230209MigratePermissionRule--Use V5 instead of V1 to store permissionID",
|
||||
Migrate: func(engine *xorm.Engine) error {
|
||||
models := []*Model{}
|
||||
err := engine.Table("model").Find(&models, &Model{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
isHit := false
|
||||
for _, model := range models {
|
||||
if strings.Contains(model.ModelText, "permission") {
|
||||
// update model table
|
||||
model.ModelText = strings.Replace(model.ModelText, "permission,", "", -1)
|
||||
UpdateModel(model.GetId(), model)
|
||||
isHit = true
|
||||
}
|
||||
}
|
||||
|
||||
if isHit {
|
||||
// update permission_rule table
|
||||
sql := "UPDATE `permission_rule`SET V0 = V1, V1 = V2, V2 = V3, V3 = V4, V4 = V5 WHERE V0 IN (SELECT CONCAT(owner, '/', name) AS permission_id FROM `permission`)"
|
||||
_, err = engine.Exec(sql)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
return &migration
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
||||
"github.com/xorm-io/xorm"
|
||||
"github.com/xorm-io/xorm/migrate"
|
||||
)
|
||||
|
||||
type Migrator_1_235_0_PR_1530 struct{}
|
||||
|
||||
func (*Migrator_1_235_0_PR_1530) IsMigrationNeeded() bool {
|
||||
exist, _ := ormer.Engine.IsTableExist("casbin_rule")
|
||||
|
||||
return exist
|
||||
}
|
||||
|
||||
func (*Migrator_1_235_0_PR_1530) DoMigration() *migrate.Migration {
|
||||
migration := migrate.Migration{
|
||||
ID: "20221015CasbinRule--fill ptype field with p",
|
||||
Migrate: func(engine *xorm.Engine) error {
|
||||
_, err := engine.Cols("ptype").Update(&xormadapter.CasbinRule{
|
||||
Ptype: "p",
|
||||
})
|
||||
return err
|
||||
},
|
||||
Rollback: func(engine *xorm.Engine) error {
|
||||
return engine.DropTables(&xormadapter.CasbinRule{})
|
||||
},
|
||||
}
|
||||
|
||||
return &migration
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/xorm-io/xorm"
|
||||
"github.com/xorm-io/xorm/migrate"
|
||||
)
|
||||
|
||||
type Migrator_1_240_0_PR_1539 struct{}
|
||||
|
||||
func (*Migrator_1_240_0_PR_1539) IsMigrationNeeded() bool {
|
||||
exist, _ := ormer.Engine.IsTableExist("session")
|
||||
err := ormer.Engine.Table("session").Find(&[]*Session{})
|
||||
|
||||
if exist && err != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (*Migrator_1_240_0_PR_1539) DoMigration() *migrate.Migration {
|
||||
migration := migrate.Migration{
|
||||
ID: "20230211MigrateSession--Create a new field 'application' for table `session`",
|
||||
Migrate: func(engine *xorm.Engine) error {
|
||||
if alreadyCreated, _ := engine.IsTableExist("session_tmp"); alreadyCreated {
|
||||
return errors.New("there is already a table called 'session_tmp', please rename or delete it for casdoor version migration and restart")
|
||||
}
|
||||
|
||||
type oldSession struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
|
||||
SessionId []string `json:"sessionId"`
|
||||
}
|
||||
|
||||
tx := engine.NewSession()
|
||||
|
||||
defer tx.Close()
|
||||
|
||||
err := tx.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Table("session_tmp").CreateTable(&Session{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldSessions := []*oldSession{}
|
||||
newSessions := []*Session{}
|
||||
|
||||
err = tx.Table("session").Find(&oldSessions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, oldSession := range oldSessions {
|
||||
newApplication := "null"
|
||||
if oldSession.Owner == "built-in" {
|
||||
newApplication = "app-built-in"
|
||||
}
|
||||
newSessions = append(newSessions, &Session{
|
||||
Owner: oldSession.Owner,
|
||||
Name: oldSession.Name,
|
||||
Application: newApplication,
|
||||
CreatedTime: oldSession.CreatedTime,
|
||||
SessionId: oldSession.SessionId,
|
||||
})
|
||||
}
|
||||
|
||||
rollbackFlag := false
|
||||
_, err = tx.Table("session_tmp").Insert(newSessions)
|
||||
count1, _ := tx.Table("session_tmp").Count()
|
||||
count2, _ := tx.Table("session").Count()
|
||||
|
||||
if err != nil || count1 != count2 {
|
||||
rollbackFlag = true
|
||||
}
|
||||
|
||||
delete := &Session{
|
||||
Application: "null",
|
||||
}
|
||||
_, err = tx.Table("session_tmp").Delete(*delete)
|
||||
if err != nil {
|
||||
rollbackFlag = true
|
||||
}
|
||||
|
||||
if rollbackFlag {
|
||||
tx.DropTable("session_tmp")
|
||||
return errors.New("there is something wrong with data migration for table `session`, if there is a table called `session_tmp` not created by you in casdoor, please drop it, then restart anyhow")
|
||||
}
|
||||
|
||||
err = tx.DropTable("session")
|
||||
if err != nil {
|
||||
return errors.New("fail to drop table `session` for casdoor, please drop it and rename the table `session_tmp` to `session` manually and restart")
|
||||
}
|
||||
|
||||
// Already drop table `session`
|
||||
// Can't find an api from xorm for altering table name
|
||||
err = tx.Table("session").CreateTable(&Session{})
|
||||
if err != nil {
|
||||
return errors.New("there is something wrong with data migration for table `session`, please restart")
|
||||
}
|
||||
|
||||
sessions := []*Session{}
|
||||
tx.Table("session_tmp").Find(&sessions)
|
||||
_, err = tx.Table("session").Insert(sessions)
|
||||
if err != nil {
|
||||
return errors.New("there is something wrong with data migration for table `session`, please drop table `session` and rename table `session_tmp` to `session` and restart")
|
||||
}
|
||||
|
||||
err = tx.DropTable("session_tmp")
|
||||
if err != nil {
|
||||
return errors.New("fail to drop table `session_tmp` for casdoor, please drop it manually and restart")
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return &migration
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"github.com/xorm-io/xorm"
|
||||
"github.com/xorm-io/xorm/migrate"
|
||||
)
|
||||
|
||||
type Migrator_1_314_0_PR_1841 struct{}
|
||||
|
||||
func (*Migrator_1_314_0_PR_1841) IsMigrationNeeded() bool {
|
||||
count, err := ormer.Engine.Where("password_type=?", "").Count(&User{})
|
||||
if err != nil {
|
||||
// table doesn't exist
|
||||
return false
|
||||
}
|
||||
|
||||
return count > 100
|
||||
}
|
||||
|
||||
func (*Migrator_1_314_0_PR_1841) DoMigration() *migrate.Migration {
|
||||
migration := migrate.Migration{
|
||||
ID: "20230515MigrateUser--Create a new field 'passwordType' for table `user`",
|
||||
Migrate: func(engine *xorm.Engine) error {
|
||||
tx := engine.NewSession()
|
||||
|
||||
defer tx.Close()
|
||||
|
||||
err := tx.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
organizations := []*Organization{}
|
||||
err = tx.Table("organization").Find(&organizations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, organization := range organizations {
|
||||
user := &User{PasswordType: organization.PasswordType}
|
||||
_, err = tx.Where("owner = ?", organization.Name).Cols("password_type").Update(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return &migration
|
||||
}
|
@ -51,23 +51,24 @@ type Organization struct {
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
|
||||
Favicon string `xorm:"varchar(100)" json:"favicon"`
|
||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||
PasswordOptions []string `xorm:"varchar(100)" json:"passwordOptions"`
|
||||
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
|
||||
DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"`
|
||||
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
||||
Tags []string `xorm:"mediumtext" json:"tags"`
|
||||
Languages []string `xorm:"varchar(255)" json:"languages"`
|
||||
ThemeData *ThemeData `xorm:"json" json:"themeData"`
|
||||
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
|
||||
DefaultPassword string `xorm:"varchar(100)" json:"defaultPassword"`
|
||||
InitScore int `json:"initScore"`
|
||||
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
||||
IsProfilePublic bool `json:"isProfilePublic"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
|
||||
Favicon string `xorm:"varchar(100)" json:"favicon"`
|
||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||
PasswordOptions []string `xorm:"varchar(100)" json:"passwordOptions"`
|
||||
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
|
||||
DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"`
|
||||
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
||||
Tags []string `xorm:"mediumtext" json:"tags"`
|
||||
Languages []string `xorm:"varchar(255)" json:"languages"`
|
||||
ThemeData *ThemeData `xorm:"json" json:"themeData"`
|
||||
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
|
||||
DefaultPassword string `xorm:"varchar(100)" json:"defaultPassword"`
|
||||
MasterVerificationCode string `xorm:"varchar(100)" json:"masterVerificationCode"`
|
||||
InitScore int `json:"initScore"`
|
||||
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
||||
IsProfilePublic bool `json:"isProfilePublic"`
|
||||
|
||||
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
|
||||
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`
|
||||
@ -159,6 +160,9 @@ func GetMaskedOrganization(organization *Organization, errs ...error) (*Organiza
|
||||
if organization.DefaultPassword != "" {
|
||||
organization.DefaultPassword = "***"
|
||||
}
|
||||
if organization.MasterVerificationCode != "" {
|
||||
organization.MasterVerificationCode = "***"
|
||||
}
|
||||
return organization, nil
|
||||
}
|
||||
|
||||
@ -213,6 +217,9 @@ func UpdateOrganization(id string, organization *Organization) (bool, error) {
|
||||
if organization.DefaultPassword == "***" {
|
||||
session.Omit("default_password")
|
||||
}
|
||||
if organization.MasterVerificationCode == "***" {
|
||||
session.Omit("master_verification_code")
|
||||
}
|
||||
|
||||
affected, err := session.Update(organization)
|
||||
if err != nil {
|
||||
|
135
object/ormer.go
135
object/ormer.go
@ -36,16 +36,14 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ormer *Ormer = nil
|
||||
isCreateDatabaseDefined = false
|
||||
createDatabase = true
|
||||
ormer *Ormer = nil
|
||||
createDatabase = true
|
||||
configPath = "conf/app.conf"
|
||||
)
|
||||
|
||||
func InitFlag() {
|
||||
if !isCreateDatabaseDefined {
|
||||
isCreateDatabaseDefined = true
|
||||
createDatabase = getCreateDatabaseFlag()
|
||||
}
|
||||
createDatabase = getCreateDatabaseFlag()
|
||||
configPath = getConfigFlag()
|
||||
}
|
||||
|
||||
func getCreateDatabaseFlag() bool {
|
||||
@ -54,6 +52,12 @@ func getCreateDatabaseFlag() bool {
|
||||
return *res
|
||||
}
|
||||
|
||||
func getConfigFlag() string {
|
||||
res := flag.String("config", "conf/app.conf", "set it to \"/your/path/app.conf\" if your config file is not in: \"/conf/app.conf\"")
|
||||
flag.Parse()
|
||||
return *res
|
||||
}
|
||||
|
||||
func InitConfig() {
|
||||
err := beego.LoadAppConfig("ini", "../conf/app.conf")
|
||||
if err != nil {
|
||||
@ -64,12 +68,11 @@ func InitConfig() {
|
||||
|
||||
InitAdapter()
|
||||
CreateTables()
|
||||
DoMigration()
|
||||
}
|
||||
|
||||
func InitAdapter() {
|
||||
if conf.GetConfigString("driverName") == "" {
|
||||
if !util.FileExist("conf/app.conf") {
|
||||
if !util.FileExist(configPath) {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -235,12 +238,37 @@ func (a *Ormer) createTable() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Group))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(User))
|
||||
if err != nil {
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
@ -270,17 +298,7 @@ func (a *Ormer) createTable() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Provider))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Application))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Resource))
|
||||
err = a.Engine.Sync2(new(Session))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -290,26 +308,6 @@ func (a *Ormer) createTable() {
|
||||
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))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -320,6 +318,36 @@ func (a *Ormer) createTable() {
|
||||
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))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -330,33 +358,8 @@ func (a *Ormer) createTable() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(PermissionRule))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(xormadapter.CasbinRule))
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -49,17 +49,6 @@ type Permission struct {
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
}
|
||||
|
||||
type PermissionRule struct {
|
||||
Ptype string `xorm:"varchar(100) index not null default ''" json:"ptype"`
|
||||
V0 string `xorm:"varchar(100) index not null default ''" json:"v0"`
|
||||
V1 string `xorm:"varchar(100) index not null default ''" json:"v1"`
|
||||
V2 string `xorm:"varchar(100) index not null default ''" json:"v2"`
|
||||
V3 string `xorm:"varchar(100) index not null default ''" json:"v3"`
|
||||
V4 string `xorm:"varchar(100) index not null default ''" json:"v4"`
|
||||
V5 string `xorm:"varchar(100) index not null default ''" json:"v5"`
|
||||
Id string `xorm:"varchar(100) index not null default ''" json:"id"`
|
||||
}
|
||||
|
||||
const builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
|
||||
|
||||
func GetPermissionCount(owner, field, value string) (int64, error) {
|
||||
@ -131,7 +120,11 @@ func checkPermissionValid(permission *Permission) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
groupingPolicies := getGroupingPolicies(permission)
|
||||
groupingPolicies, err := getGroupingPolicies(permission)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(groupingPolicies) > 0 {
|
||||
_, err = enforcer.AddGroupingPolicies(groupingPolicies)
|
||||
if err != nil {
|
||||
@ -453,9 +446,8 @@ func GetMaskedPermissions(permissions []*Permission) []*Permission {
|
||||
// as the policyFilter when the enforcer load policy).
|
||||
func GroupPermissionsByModelAdapter(permissions []*Permission) map[string][]string {
|
||||
m := make(map[string][]string)
|
||||
|
||||
for _, permission := range permissions {
|
||||
key := permission.Model + permission.Adapter
|
||||
key := permission.GetModelAndAdapter()
|
||||
permissionIds, ok := m[key]
|
||||
if !ok {
|
||||
m[key] = []string{permission.GetId()}
|
||||
@ -471,9 +463,17 @@ func (p *Permission) GetId() string {
|
||||
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 {
|
||||
targetOrg, targetName := util.GetOwnerAndNameFromId(name)
|
||||
for _, user := range p.Users {
|
||||
if user == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
userOrg, userName := util.GetOwnerAndNameFromId(user)
|
||||
if userOrg == targetOrg && (userName == "*" || userName == targetName) {
|
||||
return true
|
||||
@ -487,9 +487,14 @@ func (p *Permission) isRoleHit(userId string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, role := range p.Roles {
|
||||
if role == "*" {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, targetRole := range targetRoles {
|
||||
if targetRole.GetId() == role {
|
||||
if role == targetRole.GetId() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user