mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-19 06:23:49 +08:00
Compare commits
77 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
@ -35,7 +35,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
cache-dependency-path: ./web/yarn.lock
|
cache-dependency-path: ./web/yarn.lock
|
||||||
- run: yarn install && CI=false yarn run build
|
- run: yarn install && CI=false yarn run build
|
||||||
@ -101,7 +101,7 @@ jobs:
|
|||||||
working-directory: ./
|
working-directory: ./
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
cache-dependency-path: ./web/yarn.lock
|
cache-dependency-path: ./web/yarn.lock
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
@ -127,7 +127,7 @@ jobs:
|
|||||||
release-and-push:
|
release-and-push:
|
||||||
name: Release And Push
|
name: Release And Push
|
||||||
runs-on: ubuntu-latest
|
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 ]
|
needs: [ frontend, backend, linter, e2e ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@ -137,7 +137,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
|
|
||||||
- name: Fetch Previous version
|
- name: Fetch Previous version
|
||||||
id: get-previous-tag
|
id: get-previous-tag
|
||||||
@ -184,14 +184,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v1
|
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:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
- name: Push to Docker Hub
|
- name: Push to Docker Hub
|
||||||
uses: docker/build-push-action@v3
|
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:
|
with:
|
||||||
context: .
|
context: .
|
||||||
target: STANDARD
|
target: STANDARD
|
||||||
@ -201,7 +201,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Push All In One Version to Docker Hub
|
- name: Push All In One Version to Docker Hub
|
||||||
uses: docker/build-push-action@v3
|
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:
|
with:
|
||||||
context: .
|
context: .
|
||||||
target: ALLINONE
|
target: ALLINONE
|
||||||
|
40
.github/workflows/helm.yml
vendored
Normal file
40
.github/workflows/helm.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
name: Helm Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'manifests/casdoor/Chart.yaml'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-helm-chart:
|
||||||
|
name: Release Helm Chart
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Helm
|
||||||
|
uses: azure/setup-helm@v3
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Release Helm Chart
|
||||||
|
run: |
|
||||||
|
cd manifests/casdoor
|
||||||
|
REGISTRY=oci://registry-1.docker.io/casbin
|
||||||
|
helm package .
|
||||||
|
PKG_NAME=$(ls *.tgz)
|
||||||
|
helm repo index . --url $REGISTRY --merge index.yaml
|
||||||
|
helm push $PKG_NAME $REGISTRY
|
||||||
|
rm $PKG_NAME
|
||||||
|
|
||||||
|
- name: Commit updated helm index.yaml
|
||||||
|
uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
|
with:
|
||||||
|
commit_message: 'ci: update helm index.yaml'
|
2
.github/workflows/sync.yml
vendored
2
.github/workflows/sync.yml
vendored
@ -7,7 +7,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
synchronize-with-crowdin:
|
synchronize-with-crowdin:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
|
if: github.repository == 'casbin/casdoor' && github.event_name == 'push'
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
FROM node:16.18.0 AS FRONT
|
FROM node:18.19.0 AS FRONT
|
||||||
WORKDIR /web
|
WORKDIR /web
|
||||||
COPY ./web .
|
COPY ./web .
|
||||||
RUN yarn install --frozen-lockfile --network-timeout 1000000 && yarn run build
|
RUN yarn install --frozen-lockfile --network-timeout 1000000 && yarn run build
|
||||||
|
|
||||||
|
|
||||||
FROM golang:1.19.9 AS BACK
|
FROM golang:1.20.12 AS BACK
|
||||||
WORKDIR /go/src/casdoor
|
WORKDIR /go/src/casdoor
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN ./build.sh
|
RUN ./build.sh
|
||||||
|
16
README.md
16
README.md
@ -1,5 +1,5 @@
|
|||||||
<h1 align="center" style="border-bottom: none;">📦⚡️ Casdoor</h1>
|
<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">
|
<p align="center">
|
||||||
<a href="#badge">
|
<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">
|
<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>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<sup>Sponsored by</sup>
|
||||||
|
<br>
|
||||||
|
<a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.casbin.org/img/stytch-white.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://cdn.casbin.org/img/stytch-charcoal.png">
|
||||||
|
<img src="https://cdn.casbin.org/img/stytch-charcoal.png" width="275">
|
||||||
|
</picture>
|
||||||
|
</a><br/>
|
||||||
|
<a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin"><b>Build auth with fraud prevention, faster.</b><br/> Try Stytch for API-first authentication, user & org management, multi-tenant SSO, MFA, device fingerprinting, and more.</a>
|
||||||
|
<br>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Online demo
|
## Online demo
|
||||||
|
|
||||||
- Read-only site: https://door.casdoor.com (any modification operation will fail)
|
- Read-only site: https://door.casdoor.com (any modification operation will fail)
|
||||||
|
@ -92,11 +92,14 @@ p, *, *, GET, /api/get-plan, *, *
|
|||||||
p, *, *, GET, /api/get-subscription, *, *
|
p, *, *, GET, /api/get-subscription, *, *
|
||||||
p, *, *, GET, /api/get-provider, *, *
|
p, *, *, GET, /api/get-provider, *, *
|
||||||
p, *, *, GET, /api/get-organization-names, *, *
|
p, *, *, GET, /api/get-organization-names, *, *
|
||||||
|
p, *, *, GET, /api/get-all-objects, *, *
|
||||||
|
p, *, *, GET, /api/get-all-actions, *, *
|
||||||
|
p, *, *, GET, /api/get-all-roles, *, *
|
||||||
`
|
`
|
||||||
|
|
||||||
sa := stringadapter.NewAdapter(ruleText)
|
sa := stringadapter.NewAdapter(ruleText)
|
||||||
// load all rules from string adapter to enforcer's memory
|
// load all rules from string adapter to enforcer's memory
|
||||||
err := sa.LoadPolicy(Enforcer.GetModel())
|
err = sa.LoadPolicy(Enforcer.GetModel())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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 {
|
func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
||||||
if method == "POST" {
|
if method == "POST" {
|
||||||
if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/callback" || urlPath == "/api/send-verification-code" || urlPath == "/api/send-email" || urlPath == "/api/verify-captcha" {
|
if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/callback" || urlPath == "/api/send-verification-code" || urlPath == "/api/send-email" || urlPath == "/api/verify-captcha" || urlPath == "/api/check-user-password" || strings.HasPrefix(urlPath, "/api/mfa/") {
|
||||||
return true
|
return true
|
||||||
} else if urlPath == "/api/update-user" {
|
} else if urlPath == "/api/update-user" {
|
||||||
// Allow ordinary users to update their own information
|
// Allow ordinary users to update their own information
|
||||||
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 true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -56,6 +56,17 @@ type Captcha struct {
|
|||||||
SubType string `json:"subType"`
|
SubType string `json:"subType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this API is used by "Api URL" of Flarum's FoF Passport plugin
|
||||||
|
// https://github.com/FriendsOfFlarum/passport
|
||||||
|
type LaravelResponse struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
EmailVerifiedAt string `json:"email_verified_at"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
UpdatedAt string `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
// Signup
|
// Signup
|
||||||
// @Tag Login API
|
// @Tag Login API
|
||||||
// @Title Signup
|
// @Title Signup
|
||||||
@ -238,7 +249,7 @@ func (c *ApiController) Signup() {
|
|||||||
// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri"
|
// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri"
|
||||||
// @Param state query string false "state"
|
// @Param state query string false "state"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /logout [get,post]
|
// @router /logout [post]
|
||||||
func (c *ApiController) Logout() {
|
func (c *ApiController) Logout() {
|
||||||
// https://openid.net/specs/openid-connect-rpinitiated-1_0-final.html
|
// https://openid.net/specs/openid-connect-rpinitiated-1_0-final.html
|
||||||
accessToken := c.Input().Get("id_token_hint")
|
accessToken := c.Input().Get("id_token_hint")
|
||||||
@ -282,17 +293,15 @@ func (c *ApiController) Logout() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, application, token, err := object.ExpireTokenByAccessToken(accessToken)
|
_, application, token, err := object.ExpireTokenByAccessToken(accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if token == nil {
|
||||||
if !affected {
|
|
||||||
c.ResponseError(c.T("token:Token not found, invalid accessToken"))
|
c.ResponseError(c.T("token:Token not found, invalid accessToken"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if application == nil {
|
if application == nil {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist")), token.Application)
|
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist")), token.Application)
|
||||||
return
|
return
|
||||||
@ -319,7 +328,15 @@ func (c *ApiController) Logout() {
|
|||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
if application.IsRedirectUriValid(redirectUri) {
|
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 {
|
} else {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri))
|
c.ResponseError(fmt.Sprintf(c.T("token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri))
|
||||||
return
|
return
|
||||||
@ -412,7 +429,7 @@ func (c *ApiController) GetUserinfo() {
|
|||||||
// @Title UserInfo2
|
// @Title UserInfo2
|
||||||
// @Tag Account API
|
// @Tag Account API
|
||||||
// @Description return Laravel compatible user information according to OAuth 2.0
|
// @Description return Laravel compatible user information according to OAuth 2.0
|
||||||
// @Success 200 {object} LaravelResponse The Response object
|
// @Success 200 {object} controllers.LaravelResponse The Response object
|
||||||
// @router /user [get]
|
// @router /user [get]
|
||||||
func (c *ApiController) GetUserinfo2() {
|
func (c *ApiController) GetUserinfo2() {
|
||||||
user, ok := c.RequireSignedInUser()
|
user, ok := c.RequireSignedInUser()
|
||||||
@ -420,17 +437,6 @@ func (c *ApiController) GetUserinfo2() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// this API is used by "Api URL" of Flarum's FoF Passport plugin
|
|
||||||
// https://github.com/FriendsOfFlarum/passport
|
|
||||||
type LaravelResponse struct {
|
|
||||||
Id string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
EmailVerifiedAt string `json:"email_verified_at"`
|
|
||||||
CreatedAt string `json:"created_at"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
response := LaravelResponse{
|
response := LaravelResponse{
|
||||||
Id: user.Id,
|
Id: user.Id,
|
||||||
Name: user.Name,
|
Name: user.Name,
|
||||||
@ -448,6 +454,7 @@ func (c *ApiController) GetUserinfo2() {
|
|||||||
// @Tag Login API
|
// @Tag Login API
|
||||||
// @Title GetCaptcha
|
// @Title GetCaptcha
|
||||||
// @router /api/get-captcha [get]
|
// @router /api/get-captcha [get]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) GetCaptcha() {
|
func (c *ApiController) GetCaptcha() {
|
||||||
applicationId := c.Input().Get("applicationId")
|
applicationId := c.Input().Get("applicationId")
|
||||||
isCurrentProvider := c.Input().Get("isCurrentProvider")
|
isCurrentProvider := c.Input().Get("isCurrentProvider")
|
||||||
@ -473,7 +480,7 @@ func (c *ApiController) GetCaptcha() {
|
|||||||
Type: captchaProvider.Type,
|
Type: captchaProvider.Type,
|
||||||
SubType: captchaProvider.SubType,
|
SubType: captchaProvider.SubType,
|
||||||
ClientId: captchaProvider.ClientId,
|
ClientId: captchaProvider.ClientId,
|
||||||
ClientSecret: captchaProvider.ClientSecret,
|
ClientSecret: "***",
|
||||||
ClientId2: captchaProvider.ClientId2,
|
ClientId2: captchaProvider.ClientId2,
|
||||||
ClientSecret2: captchaProvider.ClientSecret2,
|
ClientSecret2: captchaProvider.ClientSecret2,
|
||||||
})
|
})
|
||||||
|
@ -110,6 +110,14 @@ func (c *ApiController) GetApplication() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0 as an initialization value, corresponding to the default configuration parameters
|
||||||
|
if application.FailedSigninLimit == 0 {
|
||||||
|
application.FailedSigninLimit = object.DefaultFailedSigninLimit
|
||||||
|
}
|
||||||
|
if application.FailedSigninfrozenTime == 0 {
|
||||||
|
application.FailedSigninfrozenTime = object.DefaultFailedSigninfrozenTime
|
||||||
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetMaskedApplication(application, userId))
|
c.ResponseOk(object.GetMaskedApplication(application, userId))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +181,12 @@ func (c *ApiController) GetOrganizationApplications() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applications, err = object.GetAllowedApplications(applications, userId)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetMaskedApplications(applications, userId))
|
c.ResponseOk(object.GetMaskedApplications(applications, userId))
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
|
@ -34,6 +34,7 @@ import (
|
|||||||
"github.com/casdoor/casdoor/proxy"
|
"github.com/casdoor/casdoor/proxy"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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: ""}
|
resp = &Response{Status: "error", Msg: fmt.Sprintf("error: grant_type: %s is not supported in this application", form.Type), Data: ""}
|
||||||
} else {
|
} else {
|
||||||
scope := c.Input().Get("scope")
|
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)
|
resp = tokenToResponse(token)
|
||||||
}
|
}
|
||||||
} else if form.Type == ResponseTypeSaml { // saml flow
|
} 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 redirectUri query string true "redirect uri"
|
||||||
// @Param scope query string true "scope"
|
// @Param scope query string true "scope"
|
||||||
// @Param state query string true "state"
|
// @Param state query string true "state"
|
||||||
// @Success 200 {object} Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /get-app-login [get]
|
// @router /get-app-login [get]
|
||||||
func (c *ApiController) GetApplicationLogin() {
|
func (c *ApiController) GetApplicationLogin() {
|
||||||
clientId := c.Input().Get("clientId")
|
clientId := c.Input().Get("clientId")
|
||||||
@ -331,8 +333,6 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var user *object.User
|
var user *object.User
|
||||||
var msg string
|
|
||||||
|
|
||||||
if authForm.Password == "" {
|
if authForm.Password == "" {
|
||||||
if user, err = object.GetUserByFields(authForm.Organization, authForm.Username); err != nil {
|
if user, err = object.GetUserByFields(authForm.Organization, authForm.Username); err != nil {
|
||||||
c.ResponseError(err.Error(), nil)
|
c.ResponseError(err.Error(), nil)
|
||||||
@ -354,20 +354,21 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check result through Email or Phone
|
// check result through Email or Phone
|
||||||
checkResult := object.CheckSigninCode(user, checkDest, authForm.Code, c.GetAcceptLanguage())
|
err = object.CheckSigninCode(user, checkDest, authForm.Code, c.GetAcceptLanguage())
|
||||||
if len(checkResult) != 0 {
|
if err != nil {
|
||||||
c.ResponseError(fmt.Sprintf("%s - %s", verificationCodeType, checkResult))
|
c.ResponseError(fmt.Sprintf("%s - %s", verificationCodeType, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable the verification code
|
// disable the verification code
|
||||||
err := object.DisableVerificationCode(checkDest)
|
err = object.DisableVerificationCode(checkDest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error(), nil)
|
c.ResponseError(err.Error(), nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error(), nil)
|
c.ResponseError(err.Error(), nil)
|
||||||
return
|
return
|
||||||
@ -386,7 +387,18 @@ func (c *ApiController) Login() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
} else if enableCaptcha {
|
} 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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -399,13 +411,15 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
password := authForm.Password
|
password := authForm.Password
|
||||||
user, msg = object.CheckUserPassword(authForm.Organization, authForm.Username, password, c.GetAcceptLanguage(), enableCaptcha)
|
user, err = object.CheckUserPassword(authForm.Organization, authForm.Username, password, c.GetAcceptLanguage(), enableCaptcha)
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg != "" {
|
if err != nil {
|
||||||
resp = &Response{Status: "error", Msg: msg}
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
} else {
|
} 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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -416,7 +430,8 @@ func (c *ApiController) Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
organization, err := object.GetOrganizationByUser(user)
|
var organization *object.Organization
|
||||||
|
organization, err = object.GetOrganizationByUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
}
|
}
|
||||||
@ -461,12 +476,15 @@ func (c *ApiController) Login() {
|
|||||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
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 {
|
if err != nil {
|
||||||
c.ResponseError(c.T(err.Error()))
|
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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -488,7 +506,8 @@ func (c *ApiController) Login() {
|
|||||||
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
||||||
// OAuth
|
// OAuth
|
||||||
idpInfo := object.FromProviderToIdpInfo(c.Ctx, provider)
|
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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -506,7 +525,8 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/golang/oauth2/issues/123#issuecomment-103715338
|
// 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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -547,7 +567,12 @@ func (c *ApiController) Login() {
|
|||||||
if user.IsForbidden {
|
if user.IsForbidden {
|
||||||
c.ResponseError(c.T("check:The user is forbidden to sign in, please contact the administrator"))
|
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)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
record := object.NewRecord(c.Ctx)
|
||||||
@ -588,14 +613,16 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle username conflicts
|
// 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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if tmpUser != nil {
|
if tmpUser != nil {
|
||||||
uid, err := uuid.NewRandom()
|
var uid uuid.UUID
|
||||||
|
uid, err = uuid.NewRandom()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -606,14 +633,16 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
properties := map[string]string{}
|
properties := map[string]string{}
|
||||||
count, err := object.GetUserCount(application.Organization, "", "", "")
|
var count int64
|
||||||
|
count, err = object.GetUserCount(application.Organization, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
properties["no"] = strconv.Itoa(int(count + 2))
|
properties["no"] = strconv.Itoa(int(count + 2))
|
||||||
initScore, err := organization.GetInitScore()
|
var initScore int
|
||||||
|
initScore, err = organization.GetInitScore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
||||||
return
|
return
|
||||||
@ -645,7 +674,8 @@ func (c *ApiController) Login() {
|
|||||||
Properties: properties,
|
Properties: properties,
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := object.AddUser(user)
|
var affected bool
|
||||||
|
affected, err = object.AddUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -667,7 +697,7 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sync info from 3rd-party if possible
|
// 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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -703,7 +733,8 @@ func (c *ApiController) Login() {
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -714,7 +745,8 @@ func (c *ApiController) Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := object.GetUser(userId)
|
var user *object.User
|
||||||
|
user, err = object.GetUser(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -727,7 +759,8 @@ func (c *ApiController) Login() {
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -740,7 +773,8 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if c.getMfaUserSession() != "" {
|
} else if c.getMfaUserSession() != "" {
|
||||||
user, err := object.GetUser(c.getMfaUserSession())
|
var user *object.User
|
||||||
|
user, err = object.GetUser(c.getMfaUserSession())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -773,7 +807,8 @@ func (c *ApiController) Login() {
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -794,7 +829,8 @@ func (c *ApiController) Login() {
|
|||||||
} else {
|
} else {
|
||||||
if c.GetSessionUsername() != "" {
|
if c.GetSessionUsername() != "" {
|
||||||
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
|
// 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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -851,6 +887,7 @@ func (c *ApiController) HandleSamlLogin() {
|
|||||||
// @Tag HandleOfficialAccountEvent API
|
// @Tag HandleOfficialAccountEvent API
|
||||||
// @Title HandleOfficialAccountEvent
|
// @Title HandleOfficialAccountEvent
|
||||||
// @router /api/webhook [POST]
|
// @router /api/webhook [POST]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) HandleOfficialAccountEvent() {
|
func (c *ApiController) HandleOfficialAccountEvent() {
|
||||||
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
|
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -881,6 +918,7 @@ func (c *ApiController) HandleOfficialAccountEvent() {
|
|||||||
// @Tag GetWebhookEventType API
|
// @Tag GetWebhookEventType API
|
||||||
// @Title GetWebhookEventType
|
// @Title GetWebhookEventType
|
||||||
// @router /api/get-webhook-event [GET]
|
// @router /api/get-webhook-event [GET]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) GetWebhookEventType() {
|
func (c *ApiController) GetWebhookEventType() {
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
@ -910,8 +948,14 @@ func (c *ApiController) GetCaptchaStatus() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
failedSigninLimit, _, err := object.GetFailedSigninConfigByUser(user)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var captchaEnabled bool
|
var captchaEnabled bool
|
||||||
if user != nil && user.SigninWrongTimes >= object.SigninWrongTimesLimit {
|
if user != nil && user.SigninWrongTimes >= failedSigninLimit {
|
||||||
captchaEnabled = true
|
captchaEnabled = true
|
||||||
}
|
}
|
||||||
c.ResponseOk(captchaEnabled)
|
c.ResponseOk(captchaEnabled)
|
||||||
@ -922,6 +966,7 @@ func (c *ApiController) GetCaptchaStatus() {
|
|||||||
// @Tag Callback API
|
// @Tag Callback API
|
||||||
// @Description Get Login Error Counts
|
// @Description Get Login Error Counts
|
||||||
// @router /api/Callback [post]
|
// @router /api/Callback [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) Callback() {
|
func (c *ApiController) Callback() {
|
||||||
code := c.GetString("code")
|
code := c.GetString("code")
|
||||||
state := c.GetString("state")
|
state := c.GetString("state")
|
||||||
|
@ -16,6 +16,7 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/object"
|
"github.com/casdoor/casdoor/object"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -25,7 +26,7 @@ import (
|
|||||||
// @Title Enforce
|
// @Title Enforce
|
||||||
// @Tag Enforce API
|
// @Tag Enforce API
|
||||||
// @Description Call Casbin Enforce API
|
// @Description Call Casbin Enforce API
|
||||||
// @Param body body object.CasbinRequest true "Casbin request"
|
// @Param body body []string true "Casbin request"
|
||||||
// @Param permissionId query string false "permission id"
|
// @Param permissionId query string false "permission id"
|
||||||
// @Param modelId query string false "model id"
|
// @Param modelId query string false "model id"
|
||||||
// @Param resourceId query string false "resource id"
|
// @Param resourceId query string false "resource id"
|
||||||
@ -42,7 +43,7 @@ func (c *ApiController) Enforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request object.CasbinRequest
|
var request []string
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
@ -56,13 +57,22 @@ func (c *ApiController) Enforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := enforcer.Enforce(request...)
|
res := []bool{}
|
||||||
|
keyRes := []string{}
|
||||||
|
|
||||||
|
// type transformation
|
||||||
|
interfaceRequest := util.StringToInterfaceArray(request)
|
||||||
|
|
||||||
|
enforceResult, err := enforcer.Enforce(interfaceRequest...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
res = append(res, enforceResult)
|
||||||
|
keyRes = append(keyRes, enforcer.GetModelAndAdapter())
|
||||||
|
|
||||||
|
c.ResponseOk(res, keyRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,22 +82,24 @@ func (c *ApiController) Enforce() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if permission == nil {
|
||||||
|
c.ResponseError(fmt.Sprintf("permission: %s doesn't exist", permissionId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
res := []bool{}
|
res := []bool{}
|
||||||
|
keyRes := []string{}
|
||||||
|
|
||||||
if permission == nil {
|
enforceResult, err := object.Enforce(permission, request)
|
||||||
res = append(res, false)
|
|
||||||
} else {
|
|
||||||
enforceResult, err := object.Enforce(permission, &request)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, enforceResult)
|
res = append(res, enforceResult)
|
||||||
}
|
keyRes = append(keyRes, permission.GetModelAndAdapter())
|
||||||
|
|
||||||
c.ResponseOk(res)
|
c.ResponseOk(res, keyRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,32 +123,33 @@ func (c *ApiController) Enforce() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res := []bool{}
|
res := []bool{}
|
||||||
|
keyRes := []string{}
|
||||||
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
||||||
for _, permissionIds := range listPermissionIdMap {
|
for key, permissionIds := range listPermissionIdMap {
|
||||||
firstPermission, err := object.GetPermission(permissionIds[0])
|
firstPermission, err := object.GetPermission(permissionIds[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
enforceResult, err := object.Enforce(firstPermission, &request, permissionIds...)
|
enforceResult, err := object.Enforce(firstPermission, request, permissionIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, enforceResult)
|
res = append(res, enforceResult)
|
||||||
|
keyRes = append(keyRes, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
c.ResponseOk(res, keyRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BatchEnforce
|
// BatchEnforce
|
||||||
// @Title BatchEnforce
|
// @Title BatchEnforce
|
||||||
// @Tag Enforce API
|
// @Tag Enforce API
|
||||||
// @Description Call Casbin BatchEnforce API
|
// @Description Call Casbin BatchEnforce API
|
||||||
// @Param body body object.CasbinRequest true "array of casbin requests"
|
// @Param body body []string true "array of casbin requests"
|
||||||
// @Param permissionId query string false "permission id"
|
// @Param permissionId query string false "permission id"
|
||||||
// @Param modelId query string false "model id"
|
// @Param modelId query string false "model id"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
@ -146,7 +159,7 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
modelId := c.Input().Get("modelId")
|
modelId := c.Input().Get("modelId")
|
||||||
enforcerId := c.Input().Get("enforcerId")
|
enforcerId := c.Input().Get("enforcerId")
|
||||||
|
|
||||||
var requests []object.CasbinRequest
|
var requests [][]string
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
@ -160,13 +173,22 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := enforcer.BatchEnforce(requests)
|
res := [][]bool{}
|
||||||
|
keyRes := []string{}
|
||||||
|
|
||||||
|
// type transformation
|
||||||
|
interfaceRequests := util.StringToInterfaceArray2d(requests)
|
||||||
|
|
||||||
|
enforceResult, err := enforcer.BatchEnforce(interfaceRequests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
res = append(res, enforceResult)
|
||||||
|
keyRes = append(keyRes, enforcer.GetModelAndAdapter())
|
||||||
|
|
||||||
|
c.ResponseOk(res, keyRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,28 +198,24 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res := [][]bool{}
|
|
||||||
|
|
||||||
if permission == nil {
|
if permission == nil {
|
||||||
l := len(requests)
|
c.ResponseError(fmt.Sprintf("permission: %s doesn't exist", permissionId))
|
||||||
resRequest := make([]bool, l)
|
return
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
resRequest[i] = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, resRequest)
|
res := [][]bool{}
|
||||||
} else {
|
keyRes := []string{}
|
||||||
enforceResult, err := object.BatchEnforce(permission, &requests)
|
|
||||||
|
enforceResult, err := object.BatchEnforce(permission, requests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, enforceResult)
|
res = append(res, enforceResult)
|
||||||
}
|
keyRes = append(keyRes, permission.GetModelAndAdapter())
|
||||||
|
|
||||||
c.ResponseOk(res)
|
c.ResponseOk(res, keyRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +233,7 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res := [][]bool{}
|
res := [][]bool{}
|
||||||
|
keyRes := []string{}
|
||||||
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
||||||
for _, permissionIds := range listPermissionIdMap {
|
for _, permissionIds := range listPermissionIdMap {
|
||||||
firstPermission, err := object.GetPermission(permissionIds[0])
|
firstPermission, err := object.GetPermission(permissionIds[0])
|
||||||
@ -224,16 +242,17 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
enforceResult, err := object.BatchEnforce(firstPermission, &requests, permissionIds...)
|
enforceResult, err := object.BatchEnforce(firstPermission, requests, permissionIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, enforceResult)
|
res = append(res, enforceResult)
|
||||||
|
keyRes = append(keyRes, firstPermission.GetModelAndAdapter())
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
c.ResponseOk(res, keyRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) GetAllObjects() {
|
func (c *ApiController) GetAllObjects() {
|
||||||
@ -243,7 +262,13 @@ func (c *ApiController) GetAllObjects() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetAllObjects(userId))
|
objects, err := object.GetAllObjects(userId)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(objects)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) GetAllActions() {
|
func (c *ApiController) GetAllActions() {
|
||||||
@ -253,7 +278,13 @@ func (c *ApiController) GetAllActions() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetAllActions(userId))
|
actions, err := object.GetAllActions(userId)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) GetAllRoles() {
|
func (c *ApiController) GetAllRoles() {
|
||||||
@ -263,5 +294,11 @@ func (c *ApiController) GetAllRoles() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetAllRoles(userId))
|
roles, err := object.GetAllRoles(userId)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(roles)
|
||||||
}
|
}
|
||||||
|
@ -65,13 +65,13 @@ func (c *ApiController) GetCerts() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGlobleCerts
|
// GetGlobalCerts
|
||||||
// @Title GetGlobleCerts
|
// @Title GetGlobalCerts
|
||||||
// @Tag Cert API
|
// @Tag Cert API
|
||||||
// @Description get globle certs
|
// @Description get globle certs
|
||||||
// @Success 200 {array} object.Cert The Response object
|
// @Success 200 {array} object.Cert The Response object
|
||||||
// @router /get-globle-certs [get]
|
// @router /get-global-certs [get]
|
||||||
func (c *ApiController) GetGlobleCerts() {
|
func (c *ApiController) GetGlobalCerts() {
|
||||||
limit := c.Input().Get("pageSize")
|
limit := c.Input().Get("pageSize")
|
||||||
page := c.Input().Get("p")
|
page := c.Input().Get("p")
|
||||||
field := c.Input().Get("field")
|
field := c.Input().Get("field")
|
||||||
@ -80,7 +80,7 @@ func (c *ApiController) GetGlobleCerts() {
|
|||||||
sortOrder := c.Input().Get("sortOrder")
|
sortOrder := c.Input().Get("sortOrder")
|
||||||
|
|
||||||
if limit == "" || page == "" {
|
if limit == "" || page == "" {
|
||||||
maskedCerts, err := object.GetMaskedCerts(object.GetGlobleCerts())
|
maskedCerts, err := object.GetMaskedCerts(object.GetGlobalCerts())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
|
@ -71,7 +71,7 @@ func (c *ApiController) GetEnforcers() {
|
|||||||
// @Tag Enforcer API
|
// @Tag Enforcer API
|
||||||
// @Description get enforcer
|
// @Description get enforcer
|
||||||
// @Param id query string true "The id ( owner/name ) of enforcer"
|
// @Param id query string true "The id ( owner/name ) of enforcer"
|
||||||
// @Success 200 {object} object
|
// @Success 200 {object} object.Enforcer
|
||||||
// @router /get-enforcer [get]
|
// @router /get-enforcer [get]
|
||||||
func (c *ApiController) GetEnforcer() {
|
func (c *ApiController) GetEnforcer() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
@ -99,7 +99,7 @@ func (c *ApiController) GetEnforcer() {
|
|||||||
// @Description update enforcer
|
// @Description update enforcer
|
||||||
// @Param id query string true "The id ( owner/name ) of enforcer"
|
// @Param id query string true "The id ( owner/name ) of enforcer"
|
||||||
// @Param enforcer body object true "The enforcer object"
|
// @Param enforcer body object true "The enforcer object"
|
||||||
// @Success 200 {object} object
|
// @Success 200 {object} object.Enforcer
|
||||||
// @router /update-enforcer [post]
|
// @router /update-enforcer [post]
|
||||||
func (c *ApiController) UpdateEnforcer() {
|
func (c *ApiController) UpdateEnforcer() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
@ -120,7 +120,7 @@ func (c *ApiController) UpdateEnforcer() {
|
|||||||
// @Tag Enforcer API
|
// @Tag Enforcer API
|
||||||
// @Description add enforcer
|
// @Description add enforcer
|
||||||
// @Param enforcer body object true "The enforcer object"
|
// @Param enforcer body object true "The enforcer object"
|
||||||
// @Success 200 {object} object
|
// @Success 200 {object} object.Enforcer
|
||||||
// @router /add-enforcer [post]
|
// @router /add-enforcer [post]
|
||||||
func (c *ApiController) AddEnforcer() {
|
func (c *ApiController) AddEnforcer() {
|
||||||
enforcer := object.Enforcer{}
|
enforcer := object.Enforcer{}
|
||||||
@ -138,8 +138,8 @@ func (c *ApiController) AddEnforcer() {
|
|||||||
// @Title DeleteEnforcer
|
// @Title DeleteEnforcer
|
||||||
// @Tag Enforcer API
|
// @Tag Enforcer API
|
||||||
// @Description delete enforcer
|
// @Description delete enforcer
|
||||||
// @Param body body object.Enforce true "The enforcer object"
|
// @Param body body object.Enforcer true "The enforcer object"
|
||||||
// @Success 200 {object} object
|
// @Success 200 {object} object.Enforcer
|
||||||
// @router /delete-enforcer [post]
|
// @router /delete-enforcer [post]
|
||||||
func (c *ApiController) DeleteEnforcer() {
|
func (c *ApiController) DeleteEnforcer() {
|
||||||
var enforcer object.Enforcer
|
var enforcer object.Enforcer
|
||||||
|
@ -42,7 +42,7 @@ type LdapSyncResp struct {
|
|||||||
// @Tag Account API
|
// @Tag Account API
|
||||||
// @Description get ldap users
|
// @Description get ldap users
|
||||||
// Param id string true "id"
|
// Param id string true "id"
|
||||||
// @Success 200 {object} LdapResp The Response object
|
// @Success 200 {object} controllers.LdapResp The Response object
|
||||||
// @router /get-ldap-users [get]
|
// @router /get-ldap-users [get]
|
||||||
func (c *ApiController) GetLdapUsers() {
|
func (c *ApiController) GetLdapUsers() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
@ -59,6 +59,7 @@ func (c *ApiController) GetLdapUsers() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
//groupsMap, err := conn.GetLdapGroups(ldapServer.BaseDn)
|
//groupsMap, err := conn.GetLdapGroups(ldapServer.BaseDn)
|
||||||
//if err != nil {
|
//if err != nil {
|
||||||
@ -249,7 +250,7 @@ func (c *ApiController) DeleteLdap() {
|
|||||||
// @Tag Account API
|
// @Tag Account API
|
||||||
// @Description sync ldap users
|
// @Description sync ldap users
|
||||||
// @Param id query string true "id"
|
// @Param id query string true "id"
|
||||||
// @Success 200 {object} LdapSyncResp The Response object
|
// @Success 200 {object} controllers.LdapSyncResp The Response object
|
||||||
// @router /sync-ldap-users [post]
|
// @router /sync-ldap-users [post]
|
||||||
func (c *ApiController) SyncLdapUsers() {
|
func (c *ApiController) SyncLdapUsers() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
@ -26,8 +26,10 @@ type LinkForm struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unlink ...
|
// Unlink ...
|
||||||
// @router /unlink [post]
|
|
||||||
// @Tag Login API
|
// @Tag Login API
|
||||||
|
// @Title Unlink
|
||||||
|
// @router /unlink [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) Unlink() {
|
func (c *ApiController) Unlink() {
|
||||||
user, ok := c.RequireSignedInUser()
|
user, ok := c.RequireSignedInUser()
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -73,7 +73,7 @@ func (c *ApiController) MfaSetupInitiate() {
|
|||||||
// @Description setup verify totp
|
// @Description setup verify totp
|
||||||
// @param secret form string true "MFA secret"
|
// @param secret form string true "MFA secret"
|
||||||
// @param passcode form string true "MFA passcode"
|
// @param passcode form string true "MFA passcode"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /mfa/setup/verify [post]
|
// @router /mfa/setup/verify [post]
|
||||||
func (c *ApiController) MfaSetupVerify() {
|
func (c *ApiController) MfaSetupVerify() {
|
||||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||||
@ -104,7 +104,7 @@ func (c *ApiController) MfaSetupVerify() {
|
|||||||
// @param owner form string true "owner of user"
|
// @param owner form string true "owner of user"
|
||||||
// @param name form string true "name of user"
|
// @param name form string true "name of user"
|
||||||
// @param type form string true "MFA auth type"
|
// @param type form string true "MFA auth type"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /mfa/setup/enable [post]
|
// @router /mfa/setup/enable [post]
|
||||||
func (c *ApiController) MfaSetupEnable() {
|
func (c *ApiController) MfaSetupEnable() {
|
||||||
owner := c.Ctx.Request.Form.Get("owner")
|
owner := c.Ctx.Request.Form.Get("owner")
|
||||||
@ -143,7 +143,7 @@ func (c *ApiController) MfaSetupEnable() {
|
|||||||
// @Description: Delete MFA
|
// @Description: Delete MFA
|
||||||
// @param owner form string true "owner of user"
|
// @param owner form string true "owner of user"
|
||||||
// @param name form string true "name of user"
|
// @param name form string true "name of user"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /delete-mfa/ [post]
|
// @router /delete-mfa/ [post]
|
||||||
func (c *ApiController) DeleteMfa() {
|
func (c *ApiController) DeleteMfa() {
|
||||||
owner := c.Ctx.Request.Form.Get("owner")
|
owner := c.Ctx.Request.Form.Get("owner")
|
||||||
@ -176,7 +176,7 @@ func (c *ApiController) DeleteMfa() {
|
|||||||
// @param owner form string true "owner of user"
|
// @param owner form string true "owner of user"
|
||||||
// @param name form string true "name of user"
|
// @param name form string true "name of user"
|
||||||
// @param id form string true "id of user's MFA props"
|
// @param id form string true "id of user's MFA props"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /set-preferred-mfa [post]
|
// @router /set-preferred-mfa [post]
|
||||||
func (c *ApiController) SetPreferredMfa() {
|
func (c *ApiController) SetPreferredMfa() {
|
||||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||||
|
@ -178,7 +178,7 @@ func (c *ApiController) DeleteOrganization() {
|
|||||||
// @Tag Organization API
|
// @Tag Organization API
|
||||||
// @Description get default application
|
// @Description get default application
|
||||||
// @Param id query string true "organization id"
|
// @Param id query string true "organization id"
|
||||||
// @Success 200 {object} Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /get-default-application [get]
|
// @router /get-default-application [get]
|
||||||
func (c *ApiController) GetDefaultApplication() {
|
func (c *ApiController) GetDefaultApplication() {
|
||||||
userId := c.GetSessionUsername()
|
userId := c.GetSessionUsername()
|
||||||
|
@ -163,6 +163,8 @@ func (c *ApiController) BuyProduct() {
|
|||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
host := c.Ctx.Request.Host
|
host := c.Ctx.Request.Host
|
||||||
providerName := c.Input().Get("providerName")
|
providerName := c.Input().Get("providerName")
|
||||||
|
paymentEnv := c.Input().Get("paymentEnv")
|
||||||
|
|
||||||
// buy `pricingName/planName` for `paidUserName`
|
// buy `pricingName/planName` for `paidUserName`
|
||||||
pricingName := c.Input().Get("pricingName")
|
pricingName := c.Input().Get("pricingName")
|
||||||
planName := c.Input().Get("planName")
|
planName := c.Input().Get("planName")
|
||||||
@ -187,11 +189,11 @@ func (c *ApiController) BuyProduct() {
|
|||||||
return
|
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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(payment)
|
c.ResponseOk(payment, attachInfo)
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,14 @@ type NotificationForm struct {
|
|||||||
// @Param clientId query string true "The clientId of the application"
|
// @Param clientId query string true "The clientId of the application"
|
||||||
// @Param clientSecret query string true "The clientSecret of the application"
|
// @Param clientSecret query string true "The clientSecret of the application"
|
||||||
// @Param from body controllers.EmailForm true "Details of the email request"
|
// @Param from body controllers.EmailForm true "Details of the email request"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /api/send-email [post]
|
// @router /api/send-email [post]
|
||||||
func (c *ApiController) SendEmail() {
|
func (c *ApiController) SendEmail() {
|
||||||
|
user, ok := c.RequireSignedInUser()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var emailForm EmailForm
|
var emailForm EmailForm
|
||||||
|
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &emailForm)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &emailForm)
|
||||||
@ -108,8 +113,13 @@ func (c *ApiController) SendEmail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
code := "123456"
|
code := "123456"
|
||||||
|
|
||||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||||
content := fmt.Sprintf(emailForm.Content, code)
|
content := strings.Replace(provider.Content, "%s", code, 1)
|
||||||
|
if user != nil {
|
||||||
|
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
|
||||||
|
}
|
||||||
|
|
||||||
for _, receiver := range emailForm.Receivers {
|
for _, receiver := range emailForm.Receivers {
|
||||||
err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender)
|
err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -128,7 +138,7 @@ func (c *ApiController) SendEmail() {
|
|||||||
// @Param clientId query string true "The clientId of the application"
|
// @Param clientId query string true "The clientId of the application"
|
||||||
// @Param clientSecret query string true "The clientSecret of the application"
|
// @Param clientSecret query string true "The clientSecret of the application"
|
||||||
// @Param from body controllers.SmsForm true "Details of the sms request"
|
// @Param from body controllers.SmsForm true "Details of the sms request"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /api/send-sms [post]
|
// @router /api/send-sms [post]
|
||||||
func (c *ApiController) SendSms() {
|
func (c *ApiController) SendSms() {
|
||||||
provider, err := c.GetProviderFromContext("SMS")
|
provider, err := c.GetProviderFromContext("SMS")
|
||||||
@ -166,7 +176,7 @@ func (c *ApiController) SendSms() {
|
|||||||
// @Tag Service API
|
// @Tag Service API
|
||||||
// @Description This API is not for Casdoor frontend to call, it is for Casdoor SDKs.
|
// @Description This API is not for Casdoor frontend to call, it is for Casdoor SDKs.
|
||||||
// @Param from body controllers.NotificationForm true "Details of the notification request"
|
// @Param from body controllers.NotificationForm true "Details of the notification request"
|
||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /api/send-notification [post]
|
// @router /api/send-notification [post]
|
||||||
func (c *ApiController) SendNotification() {
|
func (c *ApiController) SendNotification() {
|
||||||
provider, err := c.GetProviderFromContext("Notification")
|
provider, err := c.GetProviderFromContext("Notification")
|
||||||
|
@ -158,10 +158,9 @@ func (c *ApiController) DeleteToken() {
|
|||||||
// @Success 401 {object} object.TokenError The Response object
|
// @Success 401 {object} object.TokenError The Response object
|
||||||
// @router api/login/oauth/access_token [post]
|
// @router api/login/oauth/access_token [post]
|
||||||
func (c *ApiController) GetOAuthToken() {
|
func (c *ApiController) GetOAuthToken() {
|
||||||
grantType := c.Input().Get("grant_type")
|
|
||||||
refreshToken := c.Input().Get("refresh_token")
|
|
||||||
clientId := c.Input().Get("client_id")
|
clientId := c.Input().Get("client_id")
|
||||||
clientSecret := c.Input().Get("client_secret")
|
clientSecret := c.Input().Get("client_secret")
|
||||||
|
grantType := c.Input().Get("grant_type")
|
||||||
code := c.Input().Get("code")
|
code := c.Input().Get("code")
|
||||||
verifier := c.Input().Get("code_verifier")
|
verifier := c.Input().Get("code_verifier")
|
||||||
scope := c.Input().Get("scope")
|
scope := c.Input().Get("scope")
|
||||||
@ -169,35 +168,61 @@ func (c *ApiController) GetOAuthToken() {
|
|||||||
password := c.Input().Get("password")
|
password := c.Input().Get("password")
|
||||||
tag := c.Input().Get("tag")
|
tag := c.Input().Get("tag")
|
||||||
avatar := c.Input().Get("avatar")
|
avatar := c.Input().Get("avatar")
|
||||||
|
refreshToken := c.Input().Get("refresh_token")
|
||||||
|
|
||||||
if clientId == "" && clientSecret == "" {
|
if clientId == "" && clientSecret == "" {
|
||||||
clientId, clientSecret, _ = c.Ctx.Request.BasicAuth()
|
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
|
var tokenRequest TokenRequest
|
||||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &tokenRequest); err == nil {
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &tokenRequest)
|
||||||
|
if err == nil {
|
||||||
|
if clientId == "" {
|
||||||
clientId = tokenRequest.ClientId
|
clientId = tokenRequest.ClientId
|
||||||
|
}
|
||||||
|
if clientSecret == "" {
|
||||||
clientSecret = tokenRequest.ClientSecret
|
clientSecret = tokenRequest.ClientSecret
|
||||||
|
}
|
||||||
|
if grantType == "" {
|
||||||
grantType = tokenRequest.GrantType
|
grantType = tokenRequest.GrantType
|
||||||
refreshToken = tokenRequest.RefreshToken
|
}
|
||||||
|
if code == "" {
|
||||||
code = tokenRequest.Code
|
code = tokenRequest.Code
|
||||||
|
}
|
||||||
|
if verifier == "" {
|
||||||
verifier = tokenRequest.Verifier
|
verifier = tokenRequest.Verifier
|
||||||
|
}
|
||||||
|
if scope == "" {
|
||||||
scope = tokenRequest.Scope
|
scope = tokenRequest.Scope
|
||||||
|
}
|
||||||
|
if username == "" {
|
||||||
username = tokenRequest.Username
|
username = tokenRequest.Username
|
||||||
|
}
|
||||||
|
if password == "" {
|
||||||
password = tokenRequest.Password
|
password = tokenRequest.Password
|
||||||
|
}
|
||||||
|
if tag == "" {
|
||||||
tag = tokenRequest.Tag
|
tag = tokenRequest.Tag
|
||||||
|
}
|
||||||
|
if avatar == "" {
|
||||||
avatar = tokenRequest.Avatar
|
avatar = tokenRequest.Avatar
|
||||||
}
|
}
|
||||||
|
if refreshToken == "" {
|
||||||
|
refreshToken = tokenRequest.RefreshToken
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
host := c.Ctx.Request.Host
|
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 {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = oAuthtoken
|
c.Data["json"] = token
|
||||||
c.SetTokenErrorHttpStatus()
|
c.SetTokenErrorHttpStatus()
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
type TokenRequest struct {
|
type TokenRequest struct {
|
||||||
GrantType string `json:"grant_type"`
|
|
||||||
Code string `json:"code"`
|
|
||||||
ClientId string `json:"client_id"`
|
ClientId string `json:"client_id"`
|
||||||
ClientSecret string `json:"client_secret"`
|
ClientSecret string `json:"client_secret"`
|
||||||
|
GrantType string `json:"grant_type"`
|
||||||
|
Code string `json:"code"`
|
||||||
Verifier string `json:"code_verifier"`
|
Verifier string `json:"code_verifier"`
|
||||||
Scope string `json:"scope"`
|
Scope string `json:"scope"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
@ -476,16 +476,16 @@ func (c *ApiController) SetPassword() {
|
|||||||
isAdmin := c.IsAdmin()
|
isAdmin := c.IsAdmin()
|
||||||
if isAdmin {
|
if isAdmin {
|
||||||
if oldPassword != "" {
|
if oldPassword != "" {
|
||||||
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
err = object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||||
if msg != "" {
|
if err != nil {
|
||||||
c.ResponseError(msg)
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if code == "" {
|
} else if code == "" {
|
||||||
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
err = object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||||
if msg != "" {
|
if err != nil {
|
||||||
c.ResponseError(msg)
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,6 +510,7 @@ func (c *ApiController) SetPassword() {
|
|||||||
// @Title CheckUserPassword
|
// @Title CheckUserPassword
|
||||||
// @router /check-user-password [post]
|
// @router /check-user-password [post]
|
||||||
// @Tag User API
|
// @Tag User API
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) CheckUserPassword() {
|
func (c *ApiController) CheckUserPassword() {
|
||||||
var user object.User
|
var user object.User
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
||||||
@ -518,11 +519,11 @@ func (c *ApiController) CheckUserPassword() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, msg := object.CheckUserPassword(user.Owner, user.Name, user.Password, c.GetAcceptLanguage())
|
_, err = object.CheckUserPassword(user.Owner, user.Name, user.Password, c.GetAcceptLanguage())
|
||||||
if msg == "" {
|
if err != nil {
|
||||||
c.ResponseOk()
|
c.ResponseError(err.Error())
|
||||||
} else {
|
} else {
|
||||||
c.ResponseError(msg)
|
c.ResponseOk()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -580,6 +581,7 @@ func (c *ApiController) GetUserCount() {
|
|||||||
// @Title AddUserKeys
|
// @Title AddUserKeys
|
||||||
// @router /add-user-keys [post]
|
// @router /add-user-keys [post]
|
||||||
// @Tag User API
|
// @Tag User API
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) AddUserKeys() {
|
func (c *ApiController) AddUserKeys() {
|
||||||
var user object.User
|
var user object.User
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
||||||
|
@ -39,6 +39,7 @@ const (
|
|||||||
// @Title SendVerificationCode
|
// @Title SendVerificationCode
|
||||||
// @Tag Verification API
|
// @Tag Verification API
|
||||||
// @router /send-verification-code [post]
|
// @router /send-verification-code [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) SendVerificationCode() {
|
func (c *ApiController) SendVerificationCode() {
|
||||||
var vform form.VerificationForm
|
var vform form.VerificationForm
|
||||||
err := c.ParseForm(&vform)
|
err := c.ParseForm(&vform)
|
||||||
@ -53,6 +54,22 @@ func (c *ApiController) SendVerificationCode() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 vform.CaptchaType != "none" {
|
||||||
if captchaProvider := captcha.GetCaptchaProvider(vform.CaptchaType); captchaProvider == nil {
|
if captchaProvider := captcha.GetCaptchaProvider(vform.CaptchaType); captchaProvider == nil {
|
||||||
c.ResponseError(c.T("general:don't support captchaProvider: ") + vform.CaptchaType)
|
c.ResponseError(c.T("general:don't support captchaProvider: ") + vform.CaptchaType)
|
||||||
@ -65,6 +82,7 @@ func (c *ApiController) SendVerificationCode() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
application, err := object.GetApplication(vform.ApplicationId)
|
application, err := object.GetApplication(vform.ApplicationId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -212,6 +230,7 @@ func (c *ApiController) SendVerificationCode() {
|
|||||||
// @Title VerifyCaptcha
|
// @Title VerifyCaptcha
|
||||||
// @Tag Verification API
|
// @Tag Verification API
|
||||||
// @router /verify-captcha [post]
|
// @router /verify-captcha [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) VerifyCaptcha() {
|
func (c *ApiController) VerifyCaptcha() {
|
||||||
var vform form.VerificationForm
|
var vform form.VerificationForm
|
||||||
err := c.ParseForm(&vform)
|
err := c.ParseForm(&vform)
|
||||||
@ -225,6 +244,16 @@ func (c *ApiController) VerifyCaptcha() {
|
|||||||
return
|
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)
|
provider := captcha.GetCaptchaProvider(vform.CaptchaType)
|
||||||
if provider == nil {
|
if provider == nil {
|
||||||
c.ResponseError(c.T("verification:Invalid captcha provider."))
|
c.ResponseError(c.T("verification:Invalid captcha provider."))
|
||||||
@ -244,6 +273,7 @@ func (c *ApiController) VerifyCaptcha() {
|
|||||||
// @Tag Account API
|
// @Tag Account API
|
||||||
// @Title ResetEmailOrPhone
|
// @Title ResetEmailOrPhone
|
||||||
// @router /api/reset-email-or-phone [post]
|
// @router /api/reset-email-or-phone [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) ResetEmailOrPhone() {
|
func (c *ApiController) ResetEmailOrPhone() {
|
||||||
user, ok := c.RequireSignedInUser()
|
user, ok := c.RequireSignedInUser()
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -338,6 +368,7 @@ func (c *ApiController) ResetEmailOrPhone() {
|
|||||||
// @Tag Verification API
|
// @Tag Verification API
|
||||||
// @Title VerifyCode
|
// @Title VerifyCode
|
||||||
// @router /api/verify-code [post]
|
// @router /api/verify-code [post]
|
||||||
|
// @Success 200 {object} object.Userinfo The Response object
|
||||||
func (c *ApiController) VerifyCode() {
|
func (c *ApiController) VerifyCode() {
|
||||||
var authForm form.AuthForm
|
var authForm form.AuthForm
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &authForm)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &authForm)
|
||||||
|
@ -146,7 +146,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WebAuthnSigninFinish
|
// WebAuthnSigninFinish
|
||||||
// @Title WebAuthnSigninBegin
|
// @Title WebAuthnSigninFinish
|
||||||
// @Tag Login API
|
// @Tag Login API
|
||||||
// @Description WebAuthn Login Flow 2nd stage
|
// @Description WebAuthn Login Flow 2nd stage
|
||||||
// @Param body body protocol.CredentialAssertionResponse true "authenticator assertion Response"
|
// @Param body body protocol.CredentialAssertionResponse true "authenticator assertion Response"
|
||||||
|
82
email/custom_http.go
Normal file
82
email/custom_http.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package email
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HttpEmailProvider struct {
|
||||||
|
endpoint string
|
||||||
|
method string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHttpEmailProvider(endpoint string, method string) *HttpEmailProvider {
|
||||||
|
client := &HttpEmailProvider{
|
||||||
|
endpoint: endpoint,
|
||||||
|
method: method,
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
|
||||||
|
var req *http.Request
|
||||||
|
var err error
|
||||||
|
if c.method == "POST" {
|
||||||
|
formValues := url.Values{}
|
||||||
|
formValues.Set("fromName", fromName)
|
||||||
|
formValues.Set("toAddress", toAddress)
|
||||||
|
formValues.Set("subject", subject)
|
||||||
|
formValues.Set("content", content)
|
||||||
|
req, err = http.NewRequest(c.method, c.endpoint, strings.NewReader(formValues.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
} else if c.method == "GET" {
|
||||||
|
req, err = http.NewRequest(c.method, c.endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("fromName", fromName)
|
||||||
|
q.Add("toAddress", toAddress)
|
||||||
|
q.Add("subject", subject)
|
||||||
|
q.Add("content", content)
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("HttpEmailProvider's Send() error, unsupported method: %s", c.method)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := proxy.DefaultHttpClient
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("HttpEmailProvider's Send() error, custom HTTP Email request failed with status: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
@ -18,9 +18,11 @@ type EmailProvider interface {
|
|||||||
Send(fromAddress string, fromName, toAddress string, subject string, content string) error
|
Send(fromAddress string, fromName, toAddress string, subject string, content string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool) EmailProvider {
|
func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool, endpoint string, method string) EmailProvider {
|
||||||
if typ == "Azure ACS" {
|
if typ == "Azure ACS" {
|
||||||
return NewAzureACSEmailProvider(clientSecret, host)
|
return NewAzureACSEmailProvider(clientSecret, host)
|
||||||
|
} else if typ == "Custom HTTP Email" {
|
||||||
|
return NewHttpEmailProvider(endpoint, method)
|
||||||
} else {
|
} else {
|
||||||
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl)
|
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl)
|
||||||
}
|
}
|
||||||
|
27
go.mod
27
go.mod
@ -4,18 +4,16 @@ go 1.16
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/squirrel v1.5.3
|
github.com/Masterminds/squirrel v1.5.3
|
||||||
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b
|
|
||||||
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
|
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
|
||||||
github.com/aws/aws-sdk-go v1.45.5
|
github.com/aws/aws-sdk-go v1.45.5
|
||||||
github.com/beego/beego v1.12.12
|
github.com/beego/beego v1.12.12
|
||||||
github.com/beevik/etree v1.1.0
|
github.com/beevik/etree v1.1.0
|
||||||
github.com/casbin/casbin v1.9.1 // indirect
|
|
||||||
github.com/casbin/casbin/v2 v2.77.2
|
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/gomail/v2 v2.0.1
|
||||||
github.com/casdoor/notify v0.45.0
|
github.com/casdoor/notify v0.45.0
|
||||||
github.com/casdoor/oss v1.3.0
|
github.com/casdoor/oss v1.4.1
|
||||||
github.com/casdoor/xorm-adapter/v3 v3.0.4
|
github.com/casdoor/xorm-adapter/v3 v3.1.0
|
||||||
github.com/casvisor/casvisor-go-sdk v1.0.3
|
github.com/casvisor/casvisor-go-sdk v1.0.3
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
github.com/denisenkom/go-mssqldb v0.9.0
|
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/elimity-com/scim v0.0.0-20230426070224-941a5eac92f3
|
||||||
github.com/fogleman/gg v1.3.0
|
github.com/fogleman/gg v1.3.0
|
||||||
github.com/forestmgy/ldapserver v1.1.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-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-mysql-org/go-mysql v1.7.0
|
||||||
github.com/go-pay/gopay v1.5.72
|
github.com/go-pay/gopay v1.5.72
|
||||||
github.com/go-sql-driver/mysql v1.6.0
|
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-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||||
github.com/go-webauthn/webauthn v0.6.0
|
github.com/go-webauthn/webauthn v0.6.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.4.0
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/lestrrat-go/jwx v1.2.21
|
github.com/lestrrat-go/jwx v1.2.21
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
||||||
github.com/markbates/goth v1.75.2
|
github.com/markbates/goth v1.75.2
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
|
||||||
github.com/nyaruka/phonenumbers v1.1.5
|
github.com/nyaruka/phonenumbers v1.1.5
|
||||||
github.com/pquerna/otp v1.4.0
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/prometheus/client_golang v1.11.1
|
github.com/prometheus/client_golang v1.11.1
|
||||||
github.com/prometheus/client_model v0.3.0
|
github.com/prometheus/client_model v0.4.0
|
||||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/russellhaering/gosaml2 v0.9.0
|
github.com/russellhaering/gosaml2 v0.9.0
|
||||||
@ -61,11 +59,10 @@ require (
|
|||||||
github.com/xorm-io/core v0.7.4
|
github.com/xorm-io/core v0.7.4
|
||||||
github.com/xorm-io/xorm v1.1.6
|
github.com/xorm-io/xorm v1.1.6
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
golang.org/x/crypto v0.12.0
|
golang.org/x/crypto v0.14.0
|
||||||
golang.org/x/net v0.14.0
|
golang.org/x/net v0.17.0
|
||||||
golang.org/x/oauth2 v0.11.0
|
golang.org/x/oauth2 v0.13.0
|
||||||
golang.org/x/text v0.13.0 // indirect
|
google.golang.org/api v0.150.0
|
||||||
google.golang.org/api v0.138.0
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
layeh.com/radius v0.0.0-20221205141417-e7fbddd11d68
|
layeh.com/radius v0.0.0-20221205141417-e7fbddd11d68
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert",
|
"The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert",
|
||||||
"Unauthorized operation": "Nicht autorisierte Operation",
|
"Unauthorized operation": "Nicht autorisierte Operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s und %s stimmen nicht überein"
|
"Service %s and %s do not match": "Service %s und %s stimmen nicht überein"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "E-Mail ist ungültig",
|
"Email is invalid": "E-Mail ist ungültig",
|
||||||
"Empty username.": "Leerer Benutzername.",
|
"Empty username.": "Leerer Benutzername.",
|
||||||
"FirstName cannot be blank": "Vorname darf nicht leer sein",
|
"FirstName cannot be blank": "Vorname darf nicht leer sein",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Ldap Benutzername oder Passwort falsch",
|
"LDAP user name or password incorrect": "Ldap Benutzername oder Passwort falsch",
|
||||||
"LastName cannot be blank": "Nachname darf nicht leer sein",
|
"LastName cannot be blank": "Nachname darf nicht leer sein",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Mehrere Konten mit derselben uid, bitte überprüfen Sie Ihren LDAP-Server",
|
"Multiple accounts with same uid, please check your ldap server": "Mehrere Konten mit derselben uid, bitte überprüfen Sie Ihren LDAP-Server",
|
||||||
"Organization does not exist": "Organisation existiert nicht",
|
"Organization does not exist": "Organisation existiert nicht",
|
||||||
"Password must have at least 6 characters": "Das Passwort muss mindestens 6 Zeichen enthalten",
|
|
||||||
"Phone already exists": "Telefon existiert bereits",
|
"Phone already exists": "Telefon existiert bereits",
|
||||||
"Phone cannot be empty": "Das Telefon darf nicht leer sein",
|
"Phone cannot be empty": "Das Telefon darf nicht leer sein",
|
||||||
"Phone number is invalid": "Die Telefonnummer ist ungültig",
|
"Phone number is invalid": "Die Telefonnummer ist ungültig",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "El proveedor: %s no está habilitado para la aplicación",
|
"The provider: %s is not enabled for the application": "El proveedor: %s no está habilitado para la aplicación",
|
||||||
"Unauthorized operation": "Operación no autorizada",
|
"Unauthorized operation": "Operación no autorizada",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Los servicios %s y %s no coinciden"
|
"Service %s and %s do not match": "Los servicios %s y %s no coinciden"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "El correo electrónico no es válido",
|
"Email is invalid": "El correo electrónico no es válido",
|
||||||
"Empty username.": "Nombre de usuario vacío.",
|
"Empty username.": "Nombre de usuario vacío.",
|
||||||
"FirstName cannot be blank": "El nombre no puede estar en blanco",
|
"FirstName cannot be blank": "El nombre no puede estar en blanco",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Nombre de usuario o contraseña de Ldap incorrectos",
|
"LDAP user name or password incorrect": "Nombre de usuario o contraseña de Ldap incorrectos",
|
||||||
"LastName cannot be blank": "El apellido no puede estar en blanco",
|
"LastName cannot be blank": "El apellido no puede estar en blanco",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Cuentas múltiples con el mismo uid, por favor revise su servidor ldap",
|
"Multiple accounts with same uid, please check your ldap server": "Cuentas múltiples con el mismo uid, por favor revise su servidor ldap",
|
||||||
"Organization does not exist": "La organización no existe",
|
"Organization does not exist": "La organización no existe",
|
||||||
"Password must have at least 6 characters": "La contraseña debe tener al menos 6 caracteres",
|
|
||||||
"Phone already exists": "El teléfono ya existe",
|
"Phone already exists": "El teléfono ya existe",
|
||||||
"Phone cannot be empty": "Teléfono no puede estar vacío",
|
"Phone cannot be empty": "Teléfono no puede estar vacío",
|
||||||
"Phone number is invalid": "El número de teléfono no es válido",
|
"Phone number is invalid": "El número de teléfono no es válido",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "Le fournisseur :%s n'est pas activé pour l'application",
|
"The provider: %s is not enabled for the application": "Le fournisseur :%s n'est pas activé pour l'application",
|
||||||
"Unauthorized operation": "Opération non autorisée",
|
"Unauthorized operation": "Opération non autorisée",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "Le tag de l’utilisateur %s n’est pas répertorié dans les tags de l’application"
|
"User's tag: %s is not listed in the application's tags": "Le tag de l’utilisateur %s n’est pas répertorié dans les tags de l’application",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Les services %s et %s ne correspondent pas"
|
"Service %s and %s do not match": "Les services %s et %s ne correspondent pas"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "L'adresse e-mail est invalide",
|
"Email is invalid": "L'adresse e-mail est invalide",
|
||||||
"Empty username.": "Nom d'utilisateur vide.",
|
"Empty username.": "Nom d'utilisateur vide.",
|
||||||
"FirstName cannot be blank": "Le prénom ne peut pas être laissé vide",
|
"FirstName cannot be blank": "Le prénom ne peut pas être laissé vide",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Nom d'utilisateur ou mot de passe LDAP incorrect",
|
"LDAP user name or password incorrect": "Nom d'utilisateur ou mot de passe LDAP incorrect",
|
||||||
"LastName cannot be blank": "Le nom de famille ne peut pas être vide",
|
"LastName cannot be blank": "Le nom de famille ne peut pas être vide",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Plusieurs comptes avec le même identifiant d'utilisateur, veuillez vérifier votre serveur LDAP",
|
"Multiple accounts with same uid, please check your ldap server": "Plusieurs comptes avec le même identifiant d'utilisateur, veuillez vérifier votre serveur LDAP",
|
||||||
"Organization does not exist": "L'organisation n'existe pas",
|
"Organization does not exist": "L'organisation n'existe pas",
|
||||||
"Password must have at least 6 characters": "Le mot de passe doit comporter au moins 6 caractères",
|
|
||||||
"Phone already exists": "Le téléphone existe déjà",
|
"Phone already exists": "Le téléphone existe déjà",
|
||||||
"Phone cannot be empty": "Le téléphone ne peut pas être vide",
|
"Phone cannot be empty": "Le téléphone ne peut pas être vide",
|
||||||
"Phone number is invalid": "Le numéro de téléphone est invalide",
|
"Phone number is invalid": "Le numéro de téléphone est invalide",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
|
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
|
||||||
"Unauthorized operation": "Operasi tidak sah",
|
"Unauthorized operation": "Operasi tidak sah",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok"
|
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email tidak valid",
|
"Email is invalid": "Email tidak valid",
|
||||||
"Empty username.": "Nama pengguna kosong.",
|
"Empty username.": "Nama pengguna kosong.",
|
||||||
"FirstName cannot be blank": "Nama depan tidak boleh kosong",
|
"FirstName cannot be blank": "Nama depan tidak boleh kosong",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Nama pengguna atau kata sandi Ldap salah",
|
"LDAP user name or password incorrect": "Nama pengguna atau kata sandi Ldap salah",
|
||||||
"LastName cannot be blank": "Nama belakang tidak boleh kosong",
|
"LastName cannot be blank": "Nama belakang tidak boleh kosong",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Beberapa akun dengan uid yang sama, harap periksa server ldap Anda",
|
"Multiple accounts with same uid, please check your ldap server": "Beberapa akun dengan uid yang sama, harap periksa server ldap Anda",
|
||||||
"Organization does not exist": "Organisasi tidak ada",
|
"Organization does not exist": "Organisasi tidak ada",
|
||||||
"Password must have at least 6 characters": "Kata sandi harus memiliki minimal 6 karakter",
|
|
||||||
"Phone already exists": "Telepon sudah ada",
|
"Phone already exists": "Telepon sudah ada",
|
||||||
"Phone cannot be empty": "Telepon tidak boleh kosong",
|
"Phone cannot be empty": "Telepon tidak boleh kosong",
|
||||||
"Phone number is invalid": "Nomor telepon tidak valid",
|
"Phone number is invalid": "Nomor telepon tidak valid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません",
|
"The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません",
|
||||||
"Unauthorized operation": "不正操作",
|
"Unauthorized operation": "不正操作",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s",
|
"Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "サービス%sと%sは一致しません"
|
"Service %s and %s do not match": "サービス%sと%sは一致しません"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "電子メールは無効です",
|
"Email is invalid": "電子メールは無効です",
|
||||||
"Empty username.": "空のユーザー名。",
|
"Empty username.": "空のユーザー名。",
|
||||||
"FirstName cannot be blank": "ファーストネームは空白にできません",
|
"FirstName cannot be blank": "ファーストネームは空白にできません",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Ldapのユーザー名またはパスワードが間違っています",
|
"LDAP user name or password incorrect": "Ldapのユーザー名またはパスワードが間違っています",
|
||||||
"LastName cannot be blank": "姓は空白にできません",
|
"LastName cannot be blank": "姓は空白にできません",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "同じuidを持つ複数のアカウントがあります。あなたのLDAPサーバーを確認してください",
|
"Multiple accounts with same uid, please check your ldap server": "同じuidを持つ複数のアカウントがあります。あなたのLDAPサーバーを確認してください",
|
||||||
"Organization does not exist": "組織は存在しません",
|
"Organization does not exist": "組織は存在しません",
|
||||||
"Password must have at least 6 characters": "パスワードは少なくとも6つの文字が必要です",
|
|
||||||
"Phone already exists": "電話はすでに存在しています",
|
"Phone already exists": "電話はすでに存在しています",
|
||||||
"Phone cannot be empty": "電話は空っぽにできません",
|
"Phone cannot be empty": "電話は空っぽにできません",
|
||||||
"Phone number is invalid": "電話番号が無効です",
|
"Phone number is invalid": "電話番号が無効です",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
|
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
|
||||||
"Unauthorized operation": "무단 조작",
|
"Unauthorized operation": "무단 조작",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s",
|
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"
|
"Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "이메일이 유효하지 않습니다",
|
"Email is invalid": "이메일이 유효하지 않습니다",
|
||||||
"Empty username.": "빈 사용자 이름.",
|
"Empty username.": "빈 사용자 이름.",
|
||||||
"FirstName cannot be blank": "이름은 공백일 수 없습니다",
|
"FirstName cannot be blank": "이름은 공백일 수 없습니다",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP 사용자 이름 또는 암호가 잘못되었습니다",
|
"LDAP user name or password incorrect": "LDAP 사용자 이름 또는 암호가 잘못되었습니다",
|
||||||
"LastName cannot be blank": "성은 비어 있을 수 없습니다",
|
"LastName cannot be blank": "성은 비어 있을 수 없습니다",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "동일한 UID를 가진 여러 계정이 있습니다. LDAP 서버를 확인해주세요",
|
"Multiple accounts with same uid, please check your ldap server": "동일한 UID를 가진 여러 계정이 있습니다. LDAP 서버를 확인해주세요",
|
||||||
"Organization does not exist": "조직은 존재하지 않습니다",
|
"Organization does not exist": "조직은 존재하지 않습니다",
|
||||||
"Password must have at least 6 characters": "암호는 적어도 6자 이상이어야 합니다",
|
|
||||||
"Phone already exists": "전화기는 이미 존재합니다",
|
"Phone already exists": "전화기는 이미 존재합니다",
|
||||||
"Phone cannot be empty": "전화는 비워 둘 수 없습니다",
|
"Phone cannot be empty": "전화는 비워 둘 수 없습니다",
|
||||||
"Phone number is invalid": "전화번호가 유효하지 않습니다",
|
"Phone number is invalid": "전화번호가 유효하지 않습니다",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
|
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
|
||||||
"Unauthorized operation": "Несанкционированная операция",
|
"Unauthorized operation": "Несанкционированная операция",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Сервисы %s и %s не совпадают"
|
"Service %s and %s do not match": "Сервисы %s и %s не совпадают"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Адрес электронной почты недействительный",
|
"Email is invalid": "Адрес электронной почты недействительный",
|
||||||
"Empty username.": "Пустое имя пользователя.",
|
"Empty username.": "Пустое имя пользователя.",
|
||||||
"FirstName cannot be blank": "Имя не может быть пустым",
|
"FirstName cannot be blank": "Имя не может быть пустым",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Неправильное имя пользователя или пароль Ldap",
|
"LDAP user name or password incorrect": "Неправильное имя пользователя или пароль Ldap",
|
||||||
"LastName cannot be blank": "Фамилия не может быть пустой",
|
"LastName cannot be blank": "Фамилия не может быть пустой",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Множественные учетные записи с тем же UID. Пожалуйста, проверьте свой сервер LDAP",
|
"Multiple accounts with same uid, please check your ldap server": "Множественные учетные записи с тем же UID. Пожалуйста, проверьте свой сервер LDAP",
|
||||||
"Organization does not exist": "Организация не существует",
|
"Organization does not exist": "Организация не существует",
|
||||||
"Password must have at least 6 characters": "Пароль должен содержать не менее 6 символов",
|
|
||||||
"Phone already exists": "Телефон уже существует",
|
"Phone already exists": "Телефон уже существует",
|
||||||
"Phone cannot be empty": "Телефон не может быть пустым",
|
"Phone cannot be empty": "Телефон не может быть пустым",
|
||||||
"Phone number is invalid": "Номер телефона является недействительным",
|
"Phone number is invalid": "Номер телефона является недействительным",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
"Unauthorized operation": "Unauthorized operation",
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Email is invalid",
|
"Email is invalid": "Email is invalid",
|
||||||
"Empty username.": "Empty username.",
|
"Empty username.": "Empty username.",
|
||||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
"LastName cannot be blank": "LastName cannot be blank",
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
"Organization does not exist": "Organization does not exist",
|
"Organization does not exist": "Organization does not exist",
|
||||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
|
||||||
"Phone already exists": "Phone already exists",
|
"Phone already exists": "Phone already exists",
|
||||||
"Phone cannot be empty": "Phone cannot be empty",
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
"Phone number is invalid": "Phone number is invalid",
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "Nhà cung cấp: %s không được kích hoạt cho ứng dụng",
|
"The provider: %s is not enabled for the application": "Nhà cung cấp: %s không được kích hoạt cho ứng dụng",
|
||||||
"Unauthorized operation": "Hoạt động không được ủy quyền",
|
"Unauthorized operation": "Hoạt động không được ủy quyền",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s",
|
"Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s",
|
||||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "Dịch sang tiếng Việt: Dịch vụ %s và %s không khớp"
|
"Service %s and %s do not match": "Dịch sang tiếng Việt: Dịch vụ %s và %s không khớp"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "Địa chỉ email không hợp lệ",
|
"Email is invalid": "Địa chỉ email không hợp lệ",
|
||||||
"Empty username.": "Tên đăng nhập trống.",
|
"Empty username.": "Tên đăng nhập trống.",
|
||||||
"FirstName cannot be blank": "Tên không được để trống",
|
"FirstName cannot be blank": "Tên không được để trống",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "Tên người dùng hoặc mật khẩu Ldap không chính xác",
|
"LDAP user name or password incorrect": "Tên người dùng hoặc mật khẩu Ldap không chính xác",
|
||||||
"LastName cannot be blank": "Họ không thể để trống",
|
"LastName cannot be blank": "Họ không thể để trống",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "Nhiều tài khoản với cùng một uid, vui lòng kiểm tra máy chủ ldap của bạn",
|
"Multiple accounts with same uid, please check your ldap server": "Nhiều tài khoản với cùng một uid, vui lòng kiểm tra máy chủ ldap của bạn",
|
||||||
"Organization does not exist": "Tổ chức không tồn tại",
|
"Organization does not exist": "Tổ chức không tồn tại",
|
||||||
"Password must have at least 6 characters": "Mật khẩu phải ít nhất 6 ký tự",
|
|
||||||
"Phone already exists": "Điện thoại đã tồn tại",
|
"Phone already exists": "Điện thoại đã tồn tại",
|
||||||
"Phone cannot be empty": "Điện thoại không thể để trống",
|
"Phone cannot be empty": "Điện thoại không thể để trống",
|
||||||
"Phone number is invalid": "Số điện thoại không hợp lệ",
|
"Phone number is invalid": "Số điện thoại không hợp lệ",
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
|
"The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
|
||||||
"Unauthorized operation": "未授权的操作",
|
"Unauthorized operation": "未授权的操作",
|
||||||
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s",
|
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s",
|
||||||
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中"
|
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||||
},
|
},
|
||||||
"cas": {
|
"cas": {
|
||||||
"Service %s and %s do not match": "服务%s与%s不匹配"
|
"Service %s and %s do not match": "服务%s与%s不匹配"
|
||||||
@ -33,11 +34,12 @@
|
|||||||
"Email is invalid": "无效邮箱",
|
"Email is invalid": "无效邮箱",
|
||||||
"Empty username.": "用户名不可为空",
|
"Empty username.": "用户名不可为空",
|
||||||
"FirstName cannot be blank": "名不可以为空",
|
"FirstName cannot be blank": "名不可以为空",
|
||||||
|
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||||
|
"Invitation code is invalid": "Invitation code is invalid",
|
||||||
"LDAP user name or password incorrect": "LDAP密码错误",
|
"LDAP user name or password incorrect": "LDAP密码错误",
|
||||||
"LastName cannot be blank": "姓不可以为空",
|
"LastName cannot be blank": "姓不可以为空",
|
||||||
"Multiple accounts with same uid, please check your ldap server": "多个帐户具有相同的uid,请检查您的 LDAP 服务器",
|
"Multiple accounts with same uid, please check your ldap server": "多个帐户具有相同的uid,请检查您的 LDAP 服务器",
|
||||||
"Organization does not exist": "组织不存在",
|
"Organization does not exist": "组织不存在",
|
||||||
"Password must have at least 6 characters": "新密码至少为6位",
|
|
||||||
"Phone already exists": "该手机号已存在",
|
"Phone already exists": "该手机号已存在",
|
||||||
"Phone cannot be empty": "手机号不可为空",
|
"Phone cannot be empty": "手机号不可为空",
|
||||||
"Phone number is invalid": "无效手机号",
|
"Phone number is invalid": "无效手机号",
|
||||||
|
@ -85,10 +85,12 @@ func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
payload.Set("client_id", idp.Config.ClientID)
|
payload.Set("client_id", idp.Config.ClientID)
|
||||||
payload.Set("client_secret", idp.Config.ClientSecret)
|
payload.Set("client_secret", idp.Config.ClientSecret)
|
||||||
payload.Set("redirect_uri", idp.Config.RedirectURL)
|
payload.Set("redirect_uri", idp.Config.RedirectURL)
|
||||||
|
|
||||||
resp, err := idp.Client.PostForm(idp.Config.Endpoint.TokenURL, payload)
|
resp, err := idp.Client.PostForm(idp.Config.Endpoint.TokenURL, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := io.ReadAll(resp.Body)
|
data, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -97,10 +99,10 @@ func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
pToken := &AdfsToken{}
|
pToken := &AdfsToken{}
|
||||||
err = json.Unmarshal(data, pToken)
|
err = json.Unmarshal(data, pToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fail to unmarshal token response: %s", err.Error())
|
return nil, err
|
||||||
}
|
}
|
||||||
if pToken.ErrMsg != "" {
|
if pToken.ErrMsg != "" {
|
||||||
return nil, fmt.Errorf("pToken.Errmsg = %s", pToken.ErrMsg)
|
return nil, fmt.Errorf(pToken.ErrMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
token := &oauth2.Token{
|
token := &oauth2.Token{
|
||||||
|
126
idp/azuread_b2c.go
Normal file
126
idp/azuread_b2c.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package idp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AzureADB2CProvider struct {
|
||||||
|
Client *http.Client
|
||||||
|
Config *oauth2.Config
|
||||||
|
Tenant string
|
||||||
|
UserFlow string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAzureAdB2cProvider(clientId, clientSecret, redirectUrl, tenant string, userFlow string) *AzureADB2CProvider {
|
||||||
|
return &AzureADB2CProvider{
|
||||||
|
Config: &oauth2.Config{
|
||||||
|
ClientID: clientId,
|
||||||
|
ClientSecret: clientSecret,
|
||||||
|
RedirectURL: redirectUrl,
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: fmt.Sprintf("https://%s.b2clogin.com/%s.onmicrosoft.com/%s/oauth2/v2.0/authorize", tenant, tenant, userFlow),
|
||||||
|
TokenURL: fmt.Sprintf("https://%s.b2clogin.com/%s.onmicrosoft.com/%s/oauth2/v2.0/token", tenant, tenant, userFlow),
|
||||||
|
},
|
||||||
|
Scopes: []string{"openid", "email"},
|
||||||
|
},
|
||||||
|
Tenant: tenant,
|
||||||
|
UserFlow: userFlow,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AzureADB2CProvider) SetHttpClient(client *http.Client) {
|
||||||
|
p.Client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
type AzureadB2cToken struct {
|
||||||
|
IdToken string `json:"id_token"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
NotBefore int `json:"not_before"`
|
||||||
|
IdTokenExpiresIn int `json:"id_token_expires_in"`
|
||||||
|
ProfileInfo string `json:"profile_info"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AzureADB2CProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||||
|
payload := url.Values{}
|
||||||
|
payload.Set("code", code)
|
||||||
|
payload.Set("grant_type", "authorization_code")
|
||||||
|
payload.Set("client_id", p.Config.ClientID)
|
||||||
|
payload.Set("client_secret", p.Config.ClientSecret)
|
||||||
|
payload.Set("redirect_uri", p.Config.RedirectURL)
|
||||||
|
|
||||||
|
resp, err := p.Client.PostForm(p.Config.Endpoint.TokenURL, payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pToken := &AzureadB2cToken{}
|
||||||
|
err = json.Unmarshal(data, pToken)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
token := &oauth2.Token{
|
||||||
|
AccessToken: pToken.IdToken,
|
||||||
|
Expiry: time.Unix(time.Now().Unix()+int64(pToken.IdTokenExpiresIn), 0),
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AzureADB2CProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||||
|
userInfoEndpoint := fmt.Sprintf("https://%s.b2clogin.com/%s.onmicrosoft.com/%s/openid/v2.0/userinfo", p.Tenant, p.Tenant, p.UserFlow)
|
||||||
|
req, err := http.NewRequest("GET", userInfoEndpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Add("Authorization", "Bearer "+token.AccessToken)
|
||||||
|
|
||||||
|
resp, err := p.Client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("error fetching user info: status code %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyBytes, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var userInfo UserInfo
|
||||||
|
err = json.Unmarshal(bodyBytes, &userInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &userInfo, nil
|
||||||
|
}
|
@ -31,6 +31,7 @@ type UserInfo struct {
|
|||||||
Phone string
|
Phone string
|
||||||
CountryCode string
|
CountryCode string
|
||||||
AvatarUrl string
|
AvatarUrl string
|
||||||
|
Extra map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProviderInfo struct {
|
type ProviderInfo struct {
|
||||||
@ -90,6 +91,8 @@ func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) (IdProvider, error
|
|||||||
return NewGitlabIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
return NewGitlabIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||||
case "ADFS":
|
case "ADFS":
|
||||||
return NewAdfsIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl), nil
|
return NewAdfsIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl), nil
|
||||||
|
case "AzureADB2C":
|
||||||
|
return NewAzureAdB2cProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl, idpInfo.AppId), nil
|
||||||
case "Baidu":
|
case "Baidu":
|
||||||
return NewBaiduIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
return NewBaiduIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||||
case "Alipay":
|
case "Alipay":
|
||||||
|
@ -186,15 +186,24 @@ func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
|||||||
id = wechatUserInfo.Openid
|
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{
|
userInfo := UserInfo{
|
||||||
Id: id,
|
Id: id,
|
||||||
Username: wechatUserInfo.Nickname,
|
Username: wechatUserInfo.Nickname,
|
||||||
DisplayName: wechatUserInfo.Nickname,
|
DisplayName: wechatUserInfo.Nickname,
|
||||||
AvatarUrl: wechatUserInfo.Headimgurl,
|
AvatarUrl: wechatUserInfo.Headimgurl,
|
||||||
|
Extra: extra,
|
||||||
}
|
}
|
||||||
return &userInfo, nil
|
return &userInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BuildWechatOpenIdKey(appId string) string {
|
||||||
|
return fmt.Sprintf("wechat_openid_%s", appId)
|
||||||
|
}
|
||||||
|
|
||||||
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
|
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
|
||||||
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", clientId, clientSecret)
|
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)
|
request, err := http.NewRequest("GET", accessTokenUrl, nil)
|
||||||
|
@ -104,7 +104,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"redirectUris": [""],
|
"redirectUris": [""],
|
||||||
"expireInHours": 168
|
"expireInHours": 168,
|
||||||
|
"failedSigninLimit": 5,
|
||||||
|
"failedSigninfrozenTime": 15
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"users": [
|
"users": [
|
||||||
|
@ -16,6 +16,7 @@ package ldap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash/fnv"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
@ -49,20 +50,20 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
|
|
||||||
if r.AuthenticationChoice() == "simple" {
|
if r.AuthenticationChoice() == "simple" {
|
||||||
bindUsername, bindOrg, err := getNameAndOrgFromDN(string(r.Name()))
|
bindUsername, bindOrg, err := getNameAndOrgFromDN(string(r.Name()))
|
||||||
if err != "" {
|
if err != nil {
|
||||||
log.Printf("Bind failed ,ErrMsg=%s", err)
|
log.Printf("getNameAndOrgFromDN() error: %s", err.Error())
|
||||||
res.SetResultCode(ldap.LDAPResultInvalidDNSyntax)
|
res.SetResultCode(ldap.LDAPResultInvalidDNSyntax)
|
||||||
res.SetDiagnosticMessage("bind failed ErrMsg: " + err)
|
res.SetDiagnosticMessage(fmt.Sprintf("getNameAndOrgFromDN() error: %s", err.Error()))
|
||||||
w.Write(res)
|
w.Write(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bindPassword := string(r.AuthenticationSimple())
|
bindPassword := string(r.AuthenticationSimple())
|
||||||
bindUser, err := object.CheckUserPassword(bindOrg, bindUsername, bindPassword, "en")
|
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)
|
log.Printf("Bind failed User=%s, Pass=%#v, ErrMsg=%s", string(r.Name()), r.Authentication(), err)
|
||||||
res.SetResultCode(ldap.LDAPResultInvalidCredentials)
|
res.SetResultCode(ldap.LDAPResultInvalidCredentials)
|
||||||
res.SetDiagnosticMessage("invalid credentials ErrMsg: " + err)
|
res.SetDiagnosticMessage("invalid credentials ErrMsg: " + err.Error())
|
||||||
w.Write(res)
|
w.Write(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -78,7 +79,7 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
m.Client.OrgName = bindOrg
|
m.Client.OrgName = bindOrg
|
||||||
} else {
|
} else {
|
||||||
res.SetResultCode(ldap.LDAPResultAuthMethodNotSupported)
|
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)
|
w.Write(res)
|
||||||
}
|
}
|
||||||
@ -113,10 +114,22 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, user := range users {
|
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)
|
e := ldap.NewSearchResultEntry(dn)
|
||||||
|
uidNumberStr := fmt.Sprintf("%v", hash(user.Name))
|
||||||
for _, attr := range r.Attributes() {
|
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))
|
e.AddAttribute(message.AttributeDescription(attr), getAttribute(string(attr), user))
|
||||||
if string(attr) == "cn" {
|
if string(attr) == "cn" {
|
||||||
e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user))
|
e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user))
|
||||||
@ -127,3 +140,9 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
}
|
}
|
||||||
w.Write(res)
|
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"
|
"github.com/lor00x/goldap/message"
|
||||||
|
|
||||||
ldap "github.com/forestmgy/ldapserver"
|
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, ",")
|
DNFields := strings.Split(DN, ",")
|
||||||
params := make(map[string]string, len(DNFields))
|
params := make(map[string]string, len(DNFields))
|
||||||
for _, field := range DNFields {
|
for _, field := range DNFields {
|
||||||
@ -37,12 +101,12 @@ func getNameAndOrgFromDN(DN string) (string, string, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if params["cn"] == "" {
|
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"] == "" {
|
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) {
|
func getNameAndOrgFromFilter(baseDN, filter string) (string, string, int) {
|
||||||
@ -50,7 +114,11 @@ func getNameAndOrgFromFilter(baseDN, filter string) (string, string, int) {
|
|||||||
return "", "", ldap.LDAPResultInvalidDNSyntax
|
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
|
return name, org, ldap.LDAPResultSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +151,92 @@ func stringInSlice(value string, list []string) bool {
|
|||||||
return false
|
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) {
|
func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int) {
|
||||||
var err error
|
var err error
|
||||||
r := m.GetSearchRequest()
|
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 name == "*" && m.Client.IsOrgAdmin { // get all users from organization 'org'
|
||||||
if m.Client.IsGlobalAdmin && org == "*" {
|
if m.Client.IsGlobalAdmin && org == "*" {
|
||||||
|
filteredUsers, err = object.GetGlobalUsersWithFilter(buildSafeCondition(r.Filter()))
|
||||||
filteredUsers, err = object.GetGlobalUsers()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return filteredUsers, ldap.LDAPResultSuccess
|
return filteredUsers, ldap.LDAPResultSuccess
|
||||||
}
|
}
|
||||||
if m.Client.IsGlobalAdmin || org == m.Client.OrgName {
|
if m.Client.IsGlobalAdmin || org == m.Client.OrgName {
|
||||||
filteredUsers, err = object.GetUsers(org)
|
filteredUsers, err = object.GetUsersWithFilter(org, buildSafeCondition(r.Filter()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -144,7 +297,7 @@ func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int)
|
|||||||
return nil, ldap.LDAPResultNoSuchObject
|
return nil, ldap.LDAPResultNoSuchObject
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := object.GetUsersByTag(org, name)
|
users, err := object.GetUsersByTagWithFilter(org, name, buildSafeCondition(r.Filter()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -178,24 +331,17 @@ func getUserPasswordWithType(user *object.User) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getAttribute(attributeName string, user *object.User) message.AttributeValue {
|
func getAttribute(attributeName string, user *object.User) message.AttributeValue {
|
||||||
switch attributeName {
|
v, ok := ldapAttributesMapping[attributeName]
|
||||||
case "cn":
|
if !ok {
|
||||||
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:
|
|
||||||
return ""
|
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.InitFlag()
|
||||||
object.InitAdapter()
|
object.InitAdapter()
|
||||||
object.CreateTables()
|
object.CreateTables()
|
||||||
object.DoMigration()
|
|
||||||
|
|
||||||
object.InitDb()
|
object.InitDb()
|
||||||
object.InitFromFile()
|
object.InitFromFile()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
name: casdoor
|
name: casdoor-helm-charts
|
||||||
description: A Helm chart for Kubernetes
|
description: A Helm chart for Kubernetes
|
||||||
|
|
||||||
# A chart can be either an 'application' or a 'library' chart.
|
# A chart can be either an 'application' or a 'library' chart.
|
||||||
@ -15,10 +15,10 @@ type: application
|
|||||||
# This is the chart version. This version number should be incremented each time you make changes
|
# This is the chart version. This version number should be incremented each time you make changes
|
||||||
# to the chart and its templates, including the app version.
|
# to the chart and its templates, including the app version.
|
||||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||||
version: 0.1.0
|
version: 0.3.0
|
||||||
|
|
||||||
# This is the version number of the application being deployed. This version number should be
|
# This is the version number of the application being deployed. This version number should be
|
||||||
# incremented each time you make changes to the application. Versions are not expected to
|
# incremented each time you make changes to the application. Versions are not expected to
|
||||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||||
# It is recommended to use it with quotes.
|
# It is recommended to use it with quotes.
|
||||||
appVersion: "1.16.0"
|
appVersion: "1.18.0"
|
||||||
|
@ -59,6 +59,9 @@ spec:
|
|||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: config-volume
|
- name: config-volume
|
||||||
mountPath: /conf
|
mountPath: /conf
|
||||||
|
{{ if .Values.extraContainersEnabled }}
|
||||||
|
{{- .Values.extraContainers | nindent 8 }}
|
||||||
|
{{- end }}
|
||||||
volumes:
|
volumes:
|
||||||
- name: config-volume
|
- name: config-volume
|
||||||
projected:
|
projected:
|
||||||
|
@ -108,3 +108,10 @@ nodeSelector: {}
|
|||||||
tolerations: []
|
tolerations: []
|
||||||
|
|
||||||
affinity: {}
|
affinity: {}
|
||||||
|
|
||||||
|
# -- Optionally add extra sidecar containers.
|
||||||
|
extraContainersEnabled: false
|
||||||
|
extraContainers: ""
|
||||||
|
# extraContainers: |
|
||||||
|
# - name: ...
|
||||||
|
# image: ...
|
@ -15,10 +15,11 @@
|
|||||||
package notification
|
package notification
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/proxy"
|
"github.com/casdoor/casdoor/proxy"
|
||||||
)
|
)
|
||||||
@ -39,26 +40,29 @@ func NewCustomHttpProvider(endpoint string, method string, paramName string) (*H
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *HttpNotificationClient) Send(ctx context.Context, subject string, content string) error {
|
func (c *HttpNotificationClient) Send(ctx context.Context, subject string, content string) error {
|
||||||
|
var req *http.Request
|
||||||
var err error
|
var err error
|
||||||
|
if c.method == "POST" {
|
||||||
httpClient := proxy.DefaultHttpClient
|
formValues := url.Values{}
|
||||||
|
formValues.Set(c.paramName, content)
|
||||||
req, err := http.NewRequest(c.method, c.endpoint, bytes.NewBufferString(content))
|
req, err = http.NewRequest(c.method, c.endpoint, strings.NewReader(formValues.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.method == "POST" {
|
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
req.PostForm = map[string][]string{
|
|
||||||
c.paramName: {content},
|
|
||||||
}
|
|
||||||
} else if c.method == "GET" {
|
} else if c.method == "GET" {
|
||||||
|
req, err = http.NewRequest(c.method, c.endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
q := req.URL.Query()
|
q := req.URL.Query()
|
||||||
q.Add(c.paramName, content)
|
q.Add(c.paramName, content)
|
||||||
req.URL.RawQuery = q.Encode()
|
req.URL.RawQuery = q.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
httpClient := proxy.DefaultHttpClient
|
||||||
resp, err := httpClient.Do(req)
|
resp, err := httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -66,7 +70,7 @@ func (c *HttpNotificationClient) Send(ctx context.Context, subject string, conte
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return fmt.Errorf("SendMessage() error, custom HTTP Notification request failed with status: %s", resp.Status)
|
return fmt.Errorf("HttpNotificationClient's SendMessage() error, custom HTTP Notification request failed with status: %s", resp.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@ -90,6 +90,9 @@ type Application struct {
|
|||||||
FormOffset int `json:"formOffset"`
|
FormOffset int `json:"formOffset"`
|
||||||
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
|
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
|
||||||
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
|
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
|
||||||
|
|
||||||
|
FailedSigninLimit int `json:"failedSigninLimit"`
|
||||||
|
FailedSigninfrozenTime int `json:"failedSigninfrozenTime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApplicationCount(owner, field, value string) (int64, error) {
|
func GetApplicationCount(owner, field, value string) (int64, error) {
|
||||||
@ -319,6 +322,9 @@ func GetMaskedApplication(application *Application, userId string) *Application
|
|||||||
if application.OrganizationObj.DefaultPassword != "" {
|
if application.OrganizationObj.DefaultPassword != "" {
|
||||||
application.OrganizationObj.DefaultPassword = "***"
|
application.OrganizationObj.DefaultPassword = "***"
|
||||||
}
|
}
|
||||||
|
if application.OrganizationObj.MasterVerificationCode != "" {
|
||||||
|
application.OrganizationObj.MasterVerificationCode = "***"
|
||||||
|
}
|
||||||
if application.OrganizationObj.PasswordType != "" {
|
if application.OrganizationObj.PasswordType != "" {
|
||||||
application.OrganizationObj.PasswordType = "***"
|
application.OrganizationObj.PasswordType = "***"
|
||||||
}
|
}
|
||||||
@ -345,6 +351,34 @@ func GetMaskedApplications(applications []*Application, userId string) []*Applic
|
|||||||
return applications
|
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) {
|
func UpdateApplication(id string, application *Application) (bool, error) {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
oldApplication, err := getApplication(owner, name)
|
oldApplication, err := getApplication(owner, name)
|
||||||
|
@ -87,7 +87,7 @@ func GetGlobalCertsCount(field, value string) (int64, error) {
|
|||||||
return session.Count(&Cert{})
|
return session.Count(&Cert{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGlobleCerts() ([]*Cert, error) {
|
func GetGlobalCerts() ([]*Cert, error) {
|
||||||
certs := []*Cert{}
|
certs := []*Cert{}
|
||||||
err := ormer.Engine.Desc("created_time").Find(&certs)
|
err := ormer.Engine.Desc("created_time").Find(&certs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -163,6 +163,12 @@ func UpdateCert(id string, cert *Cert) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := cert.populateContent()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
|
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -172,10 +178,9 @@ func UpdateCert(id string, cert *Cert) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AddCert(cert *Cert) (bool, error) {
|
func AddCert(cert *Cert) (bool, error) {
|
||||||
if cert.Certificate == "" || cert.PrivateKey == "" {
|
err := cert.populateContent()
|
||||||
certificate, privateKey := generateRsaKeys(cert.BitSize, cert.ExpireInYears, cert.Name, cert.Owner)
|
if err != nil {
|
||||||
cert.Certificate = certificate
|
return false, err
|
||||||
cert.PrivateKey = privateKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := ormer.Engine.Insert(cert)
|
affected, err := ormer.Engine.Insert(cert)
|
||||||
@ -199,6 +204,20 @@ func (p *Cert) GetId() string {
|
|||||||
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
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) {
|
func getCertByApplication(application *Application) (*Cert, error) {
|
||||||
if application.Cert != "" {
|
if application.Cert != "" {
|
||||||
return getCertByName(application.Cert)
|
return getCertByName(application.Cert)
|
||||||
|
103
object/check.go
103
object/check.go
@ -28,8 +28,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SigninWrongTimesLimit = 5
|
DefaultFailedSigninLimit = 5
|
||||||
LastSignWrongTimeDuration = time.Minute * 15
|
// DefaultFailedSigninfrozenTime The unit of frozen time is minutes
|
||||||
|
DefaultFailedSigninfrozenTime = 15
|
||||||
)
|
)
|
||||||
|
|
||||||
func CheckUserSignup(application *Application, organization *Organization, form *form.AuthForm, lang string) string {
|
func CheckUserSignup(application *Application, organization *Organization, form *form.AuthForm, lang string) string {
|
||||||
@ -142,45 +143,52 @@ func CheckUserSignup(application *Application, organization *Organization, form
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkSigninErrorTimes(user *User, lang string) string {
|
func checkSigninErrorTimes(user *User, lang string) error {
|
||||||
if user.SigninWrongTimes >= SigninWrongTimesLimit {
|
failedSigninLimit, failedSigninfrozenTime, err := GetFailedSigninConfigByUser(user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.SigninWrongTimes >= failedSigninLimit {
|
||||||
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
|
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
|
||||||
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
|
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
|
||||||
minutes := int(LastSignWrongTimeDuration.Minutes() - passedTime.Minutes())
|
minutes := failedSigninfrozenTime - int(passedTime.Minutes())
|
||||||
|
|
||||||
// deny the login if the error times is greater than the limit and the last login time is less than the duration
|
// deny the login if the error times is greater than the limit and the last login time is less than the duration
|
||||||
if minutes > 0 {
|
if minutes > 0 {
|
||||||
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)
|
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
|
// reset the error times
|
||||||
user.SigninWrongTimes = 0
|
user.SigninWrongTimes = 0
|
||||||
|
|
||||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, false)
|
_, err := UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, false)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckPassword(user *User, password string, lang string, options ...bool) string {
|
func CheckPassword(user *User, password string, lang string, options ...bool) error {
|
||||||
enableCaptcha := false
|
enableCaptcha := false
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
enableCaptcha = options[0]
|
enableCaptcha = options[0]
|
||||||
}
|
}
|
||||||
// check the login error times
|
// check the login error times
|
||||||
if !enableCaptcha {
|
if !enableCaptcha {
|
||||||
if msg := checkSigninErrorTimes(user, lang); msg != "" {
|
err := checkSigninErrorTimes(user, lang)
|
||||||
return msg
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
organization, err := GetOrganizationByUser(user)
|
organization, err := GetOrganizationByUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if organization == nil {
|
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
|
passwordType := user.PasswordType
|
||||||
@ -191,19 +199,17 @@ func CheckPassword(user *User, password string, lang string, options ...bool) st
|
|||||||
if credManager != nil {
|
if credManager != nil {
|
||||||
if organization.MasterPassword != "" {
|
if organization.MasterPassword != "" {
|
||||||
if credManager.IsPasswordCorrect(password, organization.MasterPassword, "", organization.PasswordSalt) {
|
if credManager.IsPasswordCorrect(password, organization.MasterPassword, "", organization.PasswordSalt) {
|
||||||
resetUserSigninErrorTimes(user)
|
return resetUserSigninErrorTimes(user)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) {
|
if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) {
|
||||||
resetUserSigninErrorTimes(user)
|
return resetUserSigninErrorTimes(user)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return recordSigninErrorInfo(user, lang, enableCaptcha)
|
return recordSigninErrorInfo(user, lang, enableCaptcha)
|
||||||
} else {
|
} 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 +223,10 @@ func CheckPasswordComplexity(user *User, password string) string {
|
|||||||
return CheckPasswordComplexityByOrg(organization, password)
|
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)
|
ldaps, err := GetLdaps(user.Owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Error()
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapLoginSuccess := false
|
ldapLoginSuccess := false
|
||||||
@ -237,65 +243,73 @@ func checkLdapUserPassword(user *User, password string, lang string) string {
|
|||||||
|
|
||||||
searchResult, err := conn.Conn.Search(searchReq)
|
searchResult, err := conn.Conn.Search(searchReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Error()
|
conn.Close()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(searchResult.Entries) == 0 {
|
if len(searchResult.Entries) == 0 {
|
||||||
|
conn.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(searchResult.Entries) > 1 {
|
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
|
hit = true
|
||||||
dn := searchResult.Entries[0].DN
|
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
|
ldapLoginSuccess = true
|
||||||
|
conn.Close()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ldapLoginSuccess {
|
if !ldapLoginSuccess {
|
||||||
if !hit {
|
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
|
enableCaptcha := false
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
enableCaptcha = options[0]
|
enableCaptcha = options[0]
|
||||||
}
|
}
|
||||||
user, err := GetUserByFields(organization, username)
|
user, err := GetUserByFields(organization, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user == nil || user.IsDeleted {
|
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 {
|
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 user.Ldap != "" {
|
if user.Ldap != "" {
|
||||||
// ONLY for ldap users
|
// only for LDAP users
|
||||||
if msg := checkLdapUserPassword(user, password, lang); msg != "" {
|
err = checkLdapUserPassword(user, password, lang)
|
||||||
if msg == "user not exist" {
|
if err != nil {
|
||||||
return nil, fmt.Sprintf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
|
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, msg
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if msg := CheckPassword(user, password, lang, enableCaptcha); msg != "" {
|
err = CheckPassword(user, password, lang, enableCaptcha)
|
||||||
return nil, msg
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return user, ""
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckUserPermission(requestUserId, userId string, strict bool, lang string) (bool, error) {
|
func CheckUserPermission(requestUserId, userId string, strict bool, lang string) (bool, error) {
|
||||||
@ -308,7 +322,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool, lang string)
|
|||||||
if userId != "" {
|
if userId != "" {
|
||||||
targetUser, err := GetUser(userId)
|
targetUser, err := GetUser(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if targetUser == nil {
|
if targetUser == nil {
|
||||||
@ -366,7 +380,7 @@ func CheckLoginPermission(userId string, application *Application) (bool, error)
|
|||||||
allowCount := 0
|
allowCount := 0
|
||||||
denyCount := 0
|
denyCount := 0
|
||||||
for _, permission := range permissions {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,7 +485,14 @@ func CheckToEnableCaptcha(application *Application, organization, username strin
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return user != nil && user.SigninWrongTimes >= SigninWrongTimesLimit, nil
|
|
||||||
|
var failedSigninLimit int
|
||||||
|
if application.FailedSigninLimit == 0 {
|
||||||
|
failedSigninLimit = 5
|
||||||
|
} else {
|
||||||
|
failedSigninLimit = application.FailedSigninLimit
|
||||||
|
}
|
||||||
|
return user != nil && user.SigninWrongTimes >= failedSigninLimit, nil
|
||||||
}
|
}
|
||||||
return providerItem.Rule == "Always", nil
|
return providerItem.Rule == "Always", nil
|
||||||
}
|
}
|
||||||
|
@ -36,38 +36,70 @@ func isValidRealName(s string) bool {
|
|||||||
return reRealName.MatchString(s)
|
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 the password is correct and wrong times is not zero, reset the error times
|
||||||
if user.SigninWrongTimes == 0 {
|
if user.SigninWrongTimes == 0 {
|
||||||
return
|
return nil
|
||||||
}
|
|
||||||
user.SigninWrongTimes = 0
|
|
||||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordSigninErrorInfo(user *User, lang string, options ...bool) string {
|
user.SigninWrongTimes = 0
|
||||||
|
_, err := UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFailedSigninConfigByUser(user *User) (int, int, error) {
|
||||||
|
application, err := GetApplicationByUser(user)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
failedSigninLimit := application.FailedSigninLimit
|
||||||
|
failedSigninfrozenTime := application.FailedSigninfrozenTime
|
||||||
|
|
||||||
|
// 0 as an initialization value, corresponding to the default configuration parameters
|
||||||
|
if failedSigninLimit == 0 {
|
||||||
|
failedSigninLimit = DefaultFailedSigninLimit
|
||||||
|
}
|
||||||
|
if failedSigninfrozenTime == 0 {
|
||||||
|
failedSigninfrozenTime = DefaultFailedSigninfrozenTime
|
||||||
|
}
|
||||||
|
|
||||||
|
return failedSigninLimit, failedSigninfrozenTime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
|
||||||
enableCaptcha := false
|
enableCaptcha := false
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
enableCaptcha = options[0]
|
enableCaptcha = options[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
failedSigninLimit, failedSigninfrozenTime, errSignin := GetFailedSigninConfigByUser(user)
|
||||||
|
if errSignin != nil {
|
||||||
|
return errSignin
|
||||||
|
}
|
||||||
|
|
||||||
// increase failed login count
|
// increase failed login count
|
||||||
if user.SigninWrongTimes < SigninWrongTimesLimit {
|
if user.SigninWrongTimes < failedSigninLimit {
|
||||||
user.SigninWrongTimes++
|
user.SigninWrongTimes++
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.SigninWrongTimes >= SigninWrongTimesLimit {
|
if user.SigninWrongTimes >= failedSigninLimit {
|
||||||
// record the latest failed login time
|
// record the latest failed login time
|
||||||
user.LastSigninWrongTime = time.Now().UTC().Format(time.RFC3339)
|
user.LastSigninWrongTime = time.Now().UTC().Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update user
|
// update user
|
||||||
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)
|
||||||
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
leftChances := failedSigninLimit - user.SigninWrongTimes
|
||||||
if leftChances == 0 && enableCaptcha {
|
if leftChances == 0 && enableCaptcha {
|
||||||
return fmt.Sprint(i18n.Translate(lang, "check:password or code is incorrect"))
|
return fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect"))
|
||||||
} else if leftChances >= 0 {
|
} else if leftChances >= 0 {
|
||||||
return fmt.Sprintf(i18n.Translate(lang, "check:password or code is incorrect, you have %d remaining chances"), leftChances)
|
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
|
// 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 {
|
func SendEmail(provider *Provider, title string, content string, dest string, sender string) error {
|
||||||
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl)
|
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl, provider.Endpoint, provider.Method)
|
||||||
|
|
||||||
fromAddress := provider.ClientId2
|
fromAddress := provider.ClientId2
|
||||||
if fromAddress == "" {
|
if fromAddress == "" {
|
||||||
|
@ -125,6 +125,10 @@ func (enforcer *Enforcer) GetId() string {
|
|||||||
return fmt.Sprintf("%s/%s", enforcer.Owner, enforcer.Name)
|
return fmt.Sprintf("%s/%s", enforcer.Owner, enforcer.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (enforcer *Enforcer) GetModelAndAdapter() string {
|
||||||
|
return util.GetId(enforcer.Model, enforcer.Adapter)
|
||||||
|
}
|
||||||
|
|
||||||
func (enforcer *Enforcer) InitEnforcer() error {
|
func (enforcer *Enforcer) InitEnforcer() error {
|
||||||
if enforcer.Enforcer != nil {
|
if enforcer.Enforcer != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -271,7 +271,9 @@ func GetGroupUsers(groupId string) ([]*User, error) {
|
|||||||
users := []*User{}
|
users := []*User{}
|
||||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
err = ormer.Engine.Where("owner = ?", owner).In("name", names).Find(&users)
|
err = ormer.Engine.Where("owner = ?", owner).In("name", names).Find(&users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -303,6 +305,9 @@ func GroupChangeTrigger(oldName, newName string) error {
|
|||||||
|
|
||||||
groups := []*Group{}
|
groups := []*Group{}
|
||||||
err = session.Where("parent_id = ?", oldName).Find(&groups)
|
err = session.Where("parent_id = ?", oldName).Find(&groups)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
group.ParentId = newName
|
group.ParentId = newName
|
||||||
_, err := session.ID(core.PK{group.Owner, group.Name}).Cols("parent_id").Update(group)
|
_, err := session.ID(core.PK{group.Owner, group.Name}).Cols("parent_id").Update(group)
|
||||||
|
@ -396,15 +396,22 @@ func initBuiltInPermission() {
|
|||||||
Name: "permission-built-in",
|
Name: "permission-built-in",
|
||||||
CreatedTime: util.GetCurrentTime(),
|
CreatedTime: util.GetCurrentTime(),
|
||||||
DisplayName: "Built-in Permission",
|
DisplayName: "Built-in Permission",
|
||||||
|
Description: "Built-in Permission",
|
||||||
Users: []string{"built-in/*"},
|
Users: []string{"built-in/*"},
|
||||||
|
Groups: []string{},
|
||||||
Roles: []string{},
|
Roles: []string{},
|
||||||
Domains: []string{},
|
Domains: []string{},
|
||||||
Model: "model-built-in",
|
Model: "model-built-in",
|
||||||
|
Adapter: "",
|
||||||
ResourceType: "Application",
|
ResourceType: "Application",
|
||||||
Resources: []string{"app-built-in"},
|
Resources: []string{"app-built-in"},
|
||||||
Actions: []string{"Read", "Write", "Admin"},
|
Actions: []string{"Read", "Write", "Admin"},
|
||||||
Effect: "Allow",
|
Effect: "Allow",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
|
Submitter: "admin",
|
||||||
|
Approver: "admin",
|
||||||
|
ApproveTime: util.GetCurrentTime(),
|
||||||
|
State: "Approved",
|
||||||
}
|
}
|
||||||
_, err = AddPermission(permission)
|
_, err = AddPermission(permission)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -100,6 +100,7 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) e
|
|||||||
|
|
||||||
users, err := conn.GetLdapUsers(ldap)
|
users, err := conn.GetLdapUsers(ldap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
|
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -111,6 +112,8 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) e
|
|||||||
} else {
|
} else {
|
||||||
logs.Info(fmt.Sprintf("ldap autosync success, %d new users, %d existing users", len(users)-len(existed), len(existed)))
|
logs.Info(fmt.Sprintf("ldap autosync success, %d new users, %d existing users", len(users)-len(existed), len(existed)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +81,17 @@ func (ldap *Ldap) GetLdapConn() (c *LdapConn, err error) {
|
|||||||
return &LdapConn{Conn: conn, IsAD: isAD}, nil
|
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) {
|
func isMicrosoftAD(Conn *goldap.Conn) (bool, error) {
|
||||||
SearchFilter := "(objectClass=*)"
|
SearchFilter := "(objectClass=*)"
|
||||||
SearchAttributes := []string{"vendorname", "vendorversion", "isGlobalCatalogReady", "forestFunctionality"}
|
SearchAttributes := []string{"vendorname", "vendorversion", "isGlobalCatalogReady", "forestFunctionality"}
|
||||||
@ -305,7 +316,7 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
name, err := syncUser.buildLdapUserName()
|
name, err := syncUser.buildLdapUserName(owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@ -354,10 +365,10 @@ func GetExistUuids(owner string, uuids []string) ([]string, error) {
|
|||||||
return existUuids, nil
|
return existUuids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ldapUser *LdapUser) buildLdapUserName() (string, error) {
|
func (ldapUser *LdapUser) buildLdapUserName(owner string) (string, error) {
|
||||||
user := User{}
|
user := User{}
|
||||||
uidWithNumber := fmt.Sprintf("%s_%s", ldapUser.Uid, ldapUser.UidNumber)
|
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 {
|
if err != nil {
|
||||||
return "", err
|
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
|
|
||||||
}
|
|
@ -65,6 +65,7 @@ type Organization struct {
|
|||||||
ThemeData *ThemeData `xorm:"json" json:"themeData"`
|
ThemeData *ThemeData `xorm:"json" json:"themeData"`
|
||||||
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
|
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
|
||||||
DefaultPassword string `xorm:"varchar(100)" json:"defaultPassword"`
|
DefaultPassword string `xorm:"varchar(100)" json:"defaultPassword"`
|
||||||
|
MasterVerificationCode string `xorm:"varchar(100)" json:"masterVerificationCode"`
|
||||||
InitScore int `json:"initScore"`
|
InitScore int `json:"initScore"`
|
||||||
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
||||||
IsProfilePublic bool `json:"isProfilePublic"`
|
IsProfilePublic bool `json:"isProfilePublic"`
|
||||||
@ -159,6 +160,9 @@ func GetMaskedOrganization(organization *Organization, errs ...error) (*Organiza
|
|||||||
if organization.DefaultPassword != "" {
|
if organization.DefaultPassword != "" {
|
||||||
organization.DefaultPassword = "***"
|
organization.DefaultPassword = "***"
|
||||||
}
|
}
|
||||||
|
if organization.MasterVerificationCode != "" {
|
||||||
|
organization.MasterVerificationCode = "***"
|
||||||
|
}
|
||||||
return organization, nil
|
return organization, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,6 +217,9 @@ func UpdateOrganization(id string, organization *Organization) (bool, error) {
|
|||||||
if organization.DefaultPassword == "***" {
|
if organization.DefaultPassword == "***" {
|
||||||
session.Omit("default_password")
|
session.Omit("default_password")
|
||||||
}
|
}
|
||||||
|
if organization.MasterVerificationCode == "***" {
|
||||||
|
session.Omit("master_verification_code")
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := session.Update(organization)
|
affected, err := session.Update(organization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
110
object/ormer.go
110
object/ormer.go
@ -64,7 +64,6 @@ func InitConfig() {
|
|||||||
|
|
||||||
InitAdapter()
|
InitAdapter()
|
||||||
CreateTables()
|
CreateTables()
|
||||||
DoMigration()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitAdapter() {
|
func InitAdapter() {
|
||||||
@ -235,12 +234,32 @@ func (a *Ormer) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Group))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(User))
|
err = a.Engine.Sync2(new(User))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Group))
|
err = a.Engine.Sync2(new(Application))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Provider))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Resource))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Cert))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -270,17 +289,7 @@ func (a *Ormer) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Provider))
|
err = a.Engine.Sync2(new(Session))
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Application))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Resource))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -290,26 +299,6 @@ func (a *Ormer) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(VerificationRecord))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Webhook))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Syncer))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Cert))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Product))
|
err = a.Engine.Sync2(new(Product))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -320,6 +309,36 @@ func (a *Ormer) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Plan))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Pricing))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Subscription))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Syncer))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Webhook))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(VerificationRecord))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Ldap))
|
err = a.Engine.Sync2(new(Ldap))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -330,33 +349,8 @@ func (a *Ormer) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(PermissionRule))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(xormadapter.CasbinRule))
|
err = a.Engine.Sync2(new(xormadapter.CasbinRule))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Session))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Subscription))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Plan))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Pricing))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -49,17 +49,6 @@ type Permission struct {
|
|||||||
State string `xorm:"varchar(100)" json:"state"`
|
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
|
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) {
|
func GetPermissionCount(owner, field, value string) (int64, error) {
|
||||||
@ -131,7 +120,11 @@ func checkPermissionValid(permission *Permission) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
groupingPolicies := getGroupingPolicies(permission)
|
groupingPolicies, err := getGroupingPolicies(permission)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if len(groupingPolicies) > 0 {
|
if len(groupingPolicies) > 0 {
|
||||||
_, err = enforcer.AddGroupingPolicies(groupingPolicies)
|
_, err = enforcer.AddGroupingPolicies(groupingPolicies)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -453,9 +446,8 @@ func GetMaskedPermissions(permissions []*Permission) []*Permission {
|
|||||||
// as the policyFilter when the enforcer load policy).
|
// as the policyFilter when the enforcer load policy).
|
||||||
func GroupPermissionsByModelAdapter(permissions []*Permission) map[string][]string {
|
func GroupPermissionsByModelAdapter(permissions []*Permission) map[string][]string {
|
||||||
m := make(map[string][]string)
|
m := make(map[string][]string)
|
||||||
|
|
||||||
for _, permission := range permissions {
|
for _, permission := range permissions {
|
||||||
key := permission.Model + permission.Adapter
|
key := permission.GetModelAndAdapter()
|
||||||
permissionIds, ok := m[key]
|
permissionIds, ok := m[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
m[key] = []string{permission.GetId()}
|
m[key] = []string{permission.GetId()}
|
||||||
@ -471,9 +463,17 @@ func (p *Permission) GetId() string {
|
|||||||
return util.GetId(p.Owner, p.Name)
|
return util.GetId(p.Owner, p.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Permission) GetModelAndAdapter() string {
|
||||||
|
return util.GetId(p.Model, p.Adapter)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Permission) isUserHit(name string) bool {
|
func (p *Permission) isUserHit(name string) bool {
|
||||||
targetOrg, targetName := util.GetOwnerAndNameFromId(name)
|
targetOrg, targetName := util.GetOwnerAndNameFromId(name)
|
||||||
for _, user := range p.Users {
|
for _, user := range p.Users {
|
||||||
|
if user == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
userOrg, userName := util.GetOwnerAndNameFromId(user)
|
userOrg, userName := util.GetOwnerAndNameFromId(user)
|
||||||
if userOrg == targetOrg && (userName == "*" || userName == targetName) {
|
if userOrg == targetOrg && (userName == "*" || userName == targetName) {
|
||||||
return true
|
return true
|
||||||
@ -487,9 +487,14 @@ func (p *Permission) isRoleHit(userId string) bool {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, role := range p.Roles {
|
for _, role := range p.Roles {
|
||||||
|
if role == "*" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
for _, targetRole := range targetRoles {
|
for _, targetRole := range targetRoles {
|
||||||
if targetRole.GetId() == role {
|
if role == targetRole.GetId() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/casbin/casbin/v2/log"
|
"github.com/casbin/casbin/v2/log"
|
||||||
"github.com/casbin/casbin/v2/model"
|
"github.com/casbin/casbin/v2/model"
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -137,6 +138,16 @@ func getPolicies(permission *Permission) [][]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getRolesInRole(roleId string, visited map[string]struct{}) ([]*Role, error) {
|
func getRolesInRole(roleId string, visited map[string]struct{}) ([]*Role, error) {
|
||||||
|
roleOwner, roleName := util.GetOwnerAndNameFromId(roleId)
|
||||||
|
if roleName == "*" {
|
||||||
|
roles, err := GetRoles(roleOwner)
|
||||||
|
if err != nil {
|
||||||
|
return []*Role{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
|
|
||||||
role, err := GetRole(roleId)
|
role, err := GetRole(roleId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*Role{}, err
|
return []*Role{}, err
|
||||||
@ -162,7 +173,7 @@ func getRolesInRole(roleId string, visited map[string]struct{}) ([]*Role, error)
|
|||||||
return roles, nil
|
return roles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGroupingPolicies(permission *Permission) [][]string {
|
func getGroupingPolicies(permission *Permission) ([][]string, error) {
|
||||||
var groupingPolicies [][]string
|
var groupingPolicies [][]string
|
||||||
|
|
||||||
domainExist := len(permission.Domains) > 0
|
domainExist := len(permission.Domains) > 0
|
||||||
@ -170,12 +181,18 @@ func getGroupingPolicies(permission *Permission) [][]string {
|
|||||||
|
|
||||||
for _, roleId := range permission.Roles {
|
for _, roleId := range permission.Roles {
|
||||||
visited := map[string]struct{}{}
|
visited := map[string]struct{}{}
|
||||||
|
|
||||||
|
if roleId == "*" {
|
||||||
|
roleId = util.GetId(permission.Owner, "*")
|
||||||
|
}
|
||||||
|
|
||||||
rolesInRole, err := getRolesInRole(roleId, visited)
|
rolesInRole, err := getRolesInRole(roleId, visited)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, role := range rolesInRole {
|
for _, role := range rolesInRole {
|
||||||
roleId := role.GetId()
|
roleId = role.GetId()
|
||||||
for _, subUser := range role.Users {
|
for _, subUser := range role.Users {
|
||||||
if domainExist {
|
if domainExist {
|
||||||
for _, domain := range permission.Domains {
|
for _, domain := range permission.Domains {
|
||||||
@ -198,7 +215,7 @@ func getGroupingPolicies(permission *Permission) [][]string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupingPolicies
|
return groupingPolicies, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addPolicies(permission *Permission) error {
|
func addPolicies(permission *Permission) error {
|
||||||
@ -231,7 +248,10 @@ func addGroupingPolicies(permission *Permission) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
groupingPolicies := getGroupingPolicies(permission)
|
groupingPolicies, err := getGroupingPolicies(permission)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if len(groupingPolicies) > 0 {
|
if len(groupingPolicies) > 0 {
|
||||||
_, err = enforcer.AddGroupingPolicies(groupingPolicies)
|
_, err = enforcer.AddGroupingPolicies(groupingPolicies)
|
||||||
@ -249,7 +269,10 @@ func removeGroupingPolicies(permission *Permission) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
groupingPolicies := getGroupingPolicies(permission)
|
groupingPolicies, err := getGroupingPolicies(permission)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if len(groupingPolicies) > 0 {
|
if len(groupingPolicies) > 0 {
|
||||||
_, err = enforcer.RemoveGroupingPolicies(groupingPolicies)
|
_, err = enforcer.RemoveGroupingPolicies(groupingPolicies)
|
||||||
@ -261,24 +284,28 @@ func removeGroupingPolicies(permission *Permission) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CasbinRequest = []interface{}
|
func Enforce(permission *Permission, request []string, permissionIds ...string) (bool, error) {
|
||||||
|
|
||||||
func Enforce(permission *Permission, request *CasbinRequest, permissionIds ...string) (bool, error) {
|
|
||||||
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return enforcer.Enforce(*request...)
|
// type transformation
|
||||||
|
interfaceRequest := util.StringToInterfaceArray(request)
|
||||||
|
|
||||||
|
return enforcer.Enforce(interfaceRequest...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BatchEnforce(permission *Permission, requests *[]CasbinRequest, permissionIds ...string) ([]bool, error) {
|
func BatchEnforce(permission *Permission, requests [][]string, permissionIds ...string) ([]bool, error) {
|
||||||
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
enforcer, err := getPermissionEnforcer(permission, permissionIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return enforcer.BatchEnforce(*requests)
|
// type transformation
|
||||||
|
interfaceRequests := util.StringToInterfaceArray2d(requests)
|
||||||
|
|
||||||
|
return enforcer.BatchEnforce(interfaceRequests)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) ([]string, error) {
|
func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) ([]string, error) {
|
||||||
@ -287,7 +314,12 @@ func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) ([
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, role := range GetAllRoles(userId) {
|
allRoles, err := GetAllRoles(userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range allRoles {
|
||||||
permissionsByRole, err := GetPermissionsByRole(role)
|
permissionsByRole, err := GetPermissionsByRole(role)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -321,17 +353,17 @@ func GetAllActions(userId string) ([]string, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllRoles(userId string) []string {
|
func GetAllRoles(userId string) ([]string, error) {
|
||||||
roles, err := getRolesByUser(userId)
|
roles, err := getRolesByUser(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var res []string
|
res := []string{}
|
||||||
for _, role := range roles {
|
for _, role := range roles {
|
||||||
res = append(res, role.Name)
|
res = append(res, role.Name)
|
||||||
}
|
}
|
||||||
return res
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBuiltInModel(modelText string) (model.Model, error) {
|
func GetBuiltInModel(modelText string) (model.Model, error) {
|
||||||
|
@ -17,6 +17,8 @@ package object
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/idp"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/pp"
|
"github.com/casdoor/casdoor/pp"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -30,8 +32,8 @@ type Product struct {
|
|||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
|
||||||
Image string `xorm:"varchar(100)" json:"image"`
|
Image string `xorm:"varchar(100)" json:"image"`
|
||||||
Detail string `xorm:"varchar(255)" json:"detail"`
|
Detail string `xorm:"varchar(1000)" json:"detail"`
|
||||||
Description string `xorm:"varchar(100)" json:"description"`
|
Description string `xorm:"varchar(200)" json:"description"`
|
||||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||||
Price float64 `json:"price"`
|
Price float64 `json:"price"`
|
||||||
@ -158,30 +160,28 @@ func (product *Product) getProvider(providerName string) (*Provider, error) {
|
|||||||
return provider, nil
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuyProduct(id string, user *User, providerName, pricingName, planName, host string) (*Payment, error) {
|
func BuyProduct(id string, user *User, providerName, pricingName, planName, host, paymentEnv string) (payment *Payment, attachInfo map[string]interface{}, err error) {
|
||||||
product, err := GetProduct(id)
|
product, err := GetProduct(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if product == nil {
|
if product == nil {
|
||||||
return nil, fmt.Errorf("the product: %s does not exist", id)
|
return nil, nil, fmt.Errorf("the product: %s does not exist", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, err := product.getProvider(providerName)
|
provider, err := product.getProvider(providerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pProvider, err := GetPaymentProvider(provider)
|
pProvider, err := GetPaymentProvider(provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
owner := product.Owner
|
owner := product.Owner
|
||||||
productName := product.Name
|
|
||||||
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
|
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
|
||||||
paymentName := fmt.Sprintf("payment_%v", util.GenerateTimeId())
|
paymentName := fmt.Sprintf("payment_%v", util.GenerateTimeId())
|
||||||
productDisplayName := product.DisplayName
|
|
||||||
|
|
||||||
originFrontend, originBackend := getOriginFromHost(host)
|
originFrontend, originBackend := getOriginFromHost(host)
|
||||||
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
|
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
|
||||||
@ -191,26 +191,46 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
if pricingName != "" && planName != "" {
|
if pricingName != "" && planName != "" {
|
||||||
plan, err := GetPlan(util.GetId(owner, planName))
|
plan, err := GetPlan(util.GetId(owner, planName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if plan == nil {
|
if plan == nil {
|
||||||
return nil, fmt.Errorf("the plan: %s does not exist", planName)
|
return nil, nil, fmt.Errorf("the plan: %s does not exist", planName)
|
||||||
}
|
}
|
||||||
sub := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
|
sub := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
|
||||||
_, err = AddSubscription(sub)
|
_, err = AddSubscription(sub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
|
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create an OrderId and get the payUrl
|
// Create an order
|
||||||
payUrl, orderId, err := pProvider.Pay(providerName, productName, payerName, paymentName, productDisplayName, product.Price, product.Currency, returnUrl, notifyUrl)
|
payReq := &pp.PayReq{
|
||||||
|
ProviderName: providerName,
|
||||||
|
ProductName: product.Name,
|
||||||
|
PayerName: payerName,
|
||||||
|
PayerId: user.Id,
|
||||||
|
PaymentName: paymentName,
|
||||||
|
ProductDisplayName: product.DisplayName,
|
||||||
|
Price: product.Price,
|
||||||
|
Currency: product.Currency,
|
||||||
|
ReturnUrl: returnUrl,
|
||||||
|
NotifyUrl: notifyUrl,
|
||||||
|
PaymentEnv: paymentEnv,
|
||||||
|
}
|
||||||
|
// custom process for WeChat & WeChat Pay
|
||||||
|
if provider.Type == "WeChat Pay" {
|
||||||
|
payReq.PayerId, err = getUserExtraProperty(user, "WeChat", idp.BuildWechatOpenIdKey(provider.ClientId2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payResp, err := pProvider.Pay(payReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// Create a Payment linked with Product and Order
|
// Create a Payment linked with Product and Order
|
||||||
payment := &Payment{
|
payment = &Payment{
|
||||||
Owner: product.Owner,
|
Owner: product.Owner,
|
||||||
Name: paymentName,
|
Name: paymentName,
|
||||||
CreatedTime: util.GetCurrentTime(),
|
CreatedTime: util.GetCurrentTime(),
|
||||||
@ -219,8 +239,8 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
Provider: provider.Name,
|
Provider: provider.Name,
|
||||||
Type: provider.Type,
|
Type: provider.Type,
|
||||||
|
|
||||||
ProductName: productName,
|
ProductName: product.Name,
|
||||||
ProductDisplayName: productDisplayName,
|
ProductDisplayName: product.DisplayName,
|
||||||
Detail: product.Detail,
|
Detail: product.Detail,
|
||||||
Tag: product.Tag,
|
Tag: product.Tag,
|
||||||
Currency: product.Currency,
|
Currency: product.Currency,
|
||||||
@ -228,10 +248,10 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
ReturnUrl: product.ReturnUrl,
|
ReturnUrl: product.ReturnUrl,
|
||||||
|
|
||||||
User: user.Name,
|
User: user.Name,
|
||||||
PayUrl: payUrl,
|
PayUrl: payResp.PayUrl,
|
||||||
SuccessUrl: returnUrl,
|
SuccessUrl: returnUrl,
|
||||||
State: pp.PaymentStateCreated,
|
State: pp.PaymentStateCreated,
|
||||||
OutOrderId: orderId,
|
OutOrderId: payResp.OrderId,
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider.Type == "Dummy" {
|
if provider.Type == "Dummy" {
|
||||||
@ -240,13 +260,13 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
|
|
||||||
affected, err := AddPayment(payment)
|
affected, err := AddPayment(payment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !affected {
|
if !affected {
|
||||||
return nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
return nil, nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
||||||
}
|
}
|
||||||
return payment, err
|
return payment, payResp.AttachInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtendProductWithProviders(product *Product) error {
|
func ExtendProductWithProviders(product *Product) error {
|
||||||
|
@ -37,7 +37,7 @@ type Provider struct {
|
|||||||
SubType string `xorm:"varchar(100)" json:"subType"`
|
SubType string `xorm:"varchar(100)" json:"subType"`
|
||||||
Method string `xorm:"varchar(100)" json:"method"`
|
Method string `xorm:"varchar(100)" json:"method"`
|
||||||
ClientId string `xorm:"varchar(200)" json:"clientId"`
|
ClientId string `xorm:"varchar(200)" json:"clientId"`
|
||||||
ClientSecret string `xorm:"varchar(2000)" json:"clientSecret"`
|
ClientSecret string `xorm:"varchar(3000)" json:"clientSecret"`
|
||||||
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
|
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
|
||||||
ClientSecret2 string `xorm:"varchar(500)" json:"clientSecret2"`
|
ClientSecret2 string `xorm:"varchar(500)" json:"clientSecret2"`
|
||||||
Cert string `xorm:"varchar(100)" json:"cert"`
|
Cert string `xorm:"varchar(100)" json:"cert"`
|
||||||
@ -52,7 +52,7 @@ type Provider struct {
|
|||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
DisableSsl bool `json:"disableSsl"` // If the provider type is WeChat, DisableSsl means EnableQRCode
|
DisableSsl bool `json:"disableSsl"` // If the provider type is WeChat, DisableSsl means EnableQRCode
|
||||||
Title string `xorm:"varchar(100)" json:"title"`
|
Title string `xorm:"varchar(100)" json:"title"`
|
||||||
Content string `xorm:"varchar(1000)" json:"content"` // If provider type is WeChat, Content means QRCode string by Base64 encoding
|
Content string `xorm:"varchar(2000)" json:"content"` // If provider type is WeChat, Content means QRCode string by Base64 encoding
|
||||||
Receiver string `xorm:"varchar(100)" json:"receiver"`
|
Receiver string `xorm:"varchar(100)" json:"receiver"`
|
||||||
|
|
||||||
RegionId string `xorm:"varchar(100)" json:"regionId"`
|
RegionId string `xorm:"varchar(100)" json:"regionId"`
|
||||||
@ -417,7 +417,7 @@ func FromProviderToIdpInfo(ctx *context.Context, provider *Provider) *idp.Provid
|
|||||||
providerInfo.ClientId = provider.ClientId2
|
providerInfo.ClientId = provider.ClientId2
|
||||||
providerInfo.ClientSecret = provider.ClientSecret2
|
providerInfo.ClientSecret = provider.ClientSecret2
|
||||||
}
|
}
|
||||||
} else if provider.Type == "AzureAD" || provider.Type == "ADFS" || provider.Type == "Okta" {
|
} else if provider.Type == "AzureAD" || provider.Type == "AzureADB2C" || provider.Type == "ADFS" || provider.Type == "Okta" {
|
||||||
providerInfo.HostUrl = provider.Domain
|
providerInfo.HostUrl = provider.Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/sec_usr_radatt/configuration/xe-16/sec-usr-radatt-xe-16-book/sec-rad-ov-ietf-attr.html
|
// https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/sec_usr_radatt/configuration/xe-16/sec-usr-radatt-xe-16-book/sec-rad-ov-ietf-attr.html
|
||||||
// https://support.huawei.com/enterprise/zh/doc/EDOC1000178159/35071f9a
|
|
||||||
type RadiusAccounting struct {
|
type RadiusAccounting struct {
|
||||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
|
@ -271,6 +271,9 @@ func getRolesByUserInternal(userId string) ([]*Role, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return roles, err
|
return roles, err
|
||||||
}
|
}
|
||||||
|
if user == nil {
|
||||||
|
return nil, fmt.Errorf("The user: %s doesn't exist", userId)
|
||||||
|
}
|
||||||
|
|
||||||
query := ormer.Engine.Alias("r").Where("r.users like ?", fmt.Sprintf("%%%s%%", userId))
|
query := ormer.Engine.Alias("r").Where("r.users like ?", fmt.Sprintf("%%%s%%", userId))
|
||||||
for _, group := range user.Groups {
|
for _, group := range user.Groups {
|
||||||
|
@ -28,10 +28,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/RobotsAndPencils/go-saml"
|
|
||||||
"github.com/beevik/etree"
|
"github.com/beevik/etree"
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
saml "github.com/russellhaering/gosaml2"
|
||||||
dsig "github.com/russellhaering/goxmldsig"
|
dsig "github.com/russellhaering/goxmldsig"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -283,15 +283,15 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var authnRequest saml.AuthnRequest
|
var authnRequest saml.AuthNRequest
|
||||||
err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
|
err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", method, fmt.Errorf("err: Failed to unmarshal AuthnRequest, please check the SAML request. %s", err.Error())
|
return "", "", method, fmt.Errorf("err: Failed to unmarshal AuthnRequest, please check the SAML request. %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify samlRequest
|
// verify samlRequest
|
||||||
if isValid := application.IsRedirectUriValid(authnRequest.Issuer.Url); !isValid {
|
if isValid := application.IsRedirectUriValid(authnRequest.Issuer); !isValid {
|
||||||
return "", "", method, fmt.Errorf("err: Issuer URI: %s doesn't exist in the allowed Redirect URI list", authnRequest.Issuer.Url)
|
return "", "", method, fmt.Errorf("err: Issuer URI: %s doesn't exist in the allowed Redirect URI list", authnRequest.Issuer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get certificate string
|
// get certificate string
|
||||||
@ -317,7 +317,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
|||||||
|
|
||||||
_, originBackend := getOriginFromHost(host)
|
_, originBackend := getOriginFromHost(host)
|
||||||
// build signedResponse
|
// build signedResponse
|
||||||
samlResponse, _ := NewSamlResponse(application, user, originBackend, certificate, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer.Url, authnRequest.ID, application.RedirectUris)
|
samlResponse, _ := NewSamlResponse(application, user, originBackend, certificate, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer, authnRequest.ID, application.RedirectUris)
|
||||||
randomKeyStore := &X509Key{
|
randomKeyStore := &X509Key{
|
||||||
PrivateKey: cert.PrivateKey,
|
PrivateKey: cert.PrivateKey,
|
||||||
X509Certificate: certificate,
|
X509Certificate: certificate,
|
||||||
|
@ -26,6 +26,8 @@ func getSmsClient(provider *Provider) (sender.SmsClient, error) {
|
|||||||
|
|
||||||
if provider.Type == sender.HuaweiCloud || provider.Type == sender.AzureACS {
|
if provider.Type == sender.HuaweiCloud || provider.Type == sender.AzureACS {
|
||||||
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.ProviderUrl, provider.AppId)
|
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.ProviderUrl, provider.AppId)
|
||||||
|
} else if provider.Type == "Custom HTTP SMS" {
|
||||||
|
client, err = newHttpSmsClient(provider.Endpoint, provider.Method, provider.Title)
|
||||||
} else {
|
} else {
|
||||||
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
|
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
|
||||||
}
|
}
|
||||||
|
83
object/sms_custom_http.go
Normal file
83
object/sms_custom_http.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
// 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"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HttpSmsClient struct {
|
||||||
|
endpoint string
|
||||||
|
method string
|
||||||
|
paramName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHttpSmsClient(endpoint string, method string, paramName string) (*HttpSmsClient, error) {
|
||||||
|
client := &HttpSmsClient{
|
||||||
|
endpoint: endpoint,
|
||||||
|
method: method,
|
||||||
|
paramName: paramName,
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HttpSmsClient) SendMessage(param map[string]string, targetPhoneNumber ...string) error {
|
||||||
|
phoneNumber := targetPhoneNumber[0]
|
||||||
|
content := param["code"]
|
||||||
|
|
||||||
|
var req *http.Request
|
||||||
|
var err error
|
||||||
|
if c.method == "POST" {
|
||||||
|
formValues := url.Values{}
|
||||||
|
formValues.Set("phoneNumber", phoneNumber)
|
||||||
|
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("phoneNumber", phoneNumber)
|
||||||
|
q.Add(c.paramName, content)
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("HttpSmsClient's SendMessage() 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("HttpSmsClient's SendMessage() error, custom HTTP SMS request failed with status: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
@ -72,6 +72,10 @@ func GetTruncatedPath(provider *Provider, fullFilePath string, limit int) string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
||||||
|
if provider.Domain != "" && !strings.HasPrefix(provider.Domain, "http://") && !strings.HasPrefix(provider.Domain, "https://") {
|
||||||
|
provider.Domain = fmt.Sprintf("https://%s", provider.Domain)
|
||||||
|
}
|
||||||
|
|
||||||
escapedPath := util.UrlJoin(provider.PathPrefix, fullFilePath)
|
escapedPath := util.UrlJoin(provider.PathPrefix, fullFilePath)
|
||||||
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
||||||
|
|
||||||
@ -79,9 +83,6 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
|
|||||||
if provider.Type != "Local File System" {
|
if provider.Type != "Local File System" {
|
||||||
// provider.Domain = "https://cdn.casbin.com/casdoor/"
|
// provider.Domain = "https://cdn.casbin.com/casdoor/"
|
||||||
host = util.GetUrlHost(provider.Domain)
|
host = util.GetUrlHost(provider.Domain)
|
||||||
if !strings.HasPrefix(host, "http://") && !strings.HasPrefix(host, "https://") {
|
|
||||||
host = fmt.Sprintf("https://%s", host)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// provider.Domain = "http://localhost:8000" or "https://door.casdoor.com"
|
// provider.Domain = "http://localhost:8000" or "https://door.casdoor.com"
|
||||||
host = util.UrlJoin(provider.Domain, "/files")
|
host = util.UrlJoin(provider.Domain, "/files")
|
||||||
@ -90,9 +91,12 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
|
|||||||
host = util.UrlJoin(host, provider.Bucket)
|
host = util.UrlJoin(host, provider.Bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileUrl := util.UrlJoin(host, escapePath(objectKey))
|
fileUrl := ""
|
||||||
|
if host != "" {
|
||||||
|
fileUrl = util.UrlJoin(host, escapePath(objectKey))
|
||||||
|
}
|
||||||
|
|
||||||
if hasTimestamp {
|
if fileUrl != "" && hasTimestamp {
|
||||||
fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
|
fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +116,10 @@ func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface,
|
|||||||
|
|
||||||
if provider.Domain == "" {
|
if provider.Domain == "" {
|
||||||
provider.Domain = storageProvider.GetEndpoint()
|
provider.Domain = storageProvider.GetEndpoint()
|
||||||
UpdateProvider(provider.GetId(), provider)
|
_, err := UpdateProvider(provider.GetId(), provider)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return storageProvider, nil
|
return storageProvider, nil
|
||||||
@ -126,7 +133,12 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
|
|||||||
|
|
||||||
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
|
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
|
||||||
|
|
||||||
_, err = storageProvider.Put(objectKey, fileBuffer)
|
objectKeyRefined := objectKey
|
||||||
|
if provider.Type == "Google Cloud Storage" {
|
||||||
|
objectKeyRefined = strings.TrimPrefix(objectKeyRefined, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = storageProvider.Put(objectKeyRefined, fileBuffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
126
object/token.go
126
object/token.go
@ -17,6 +17,7 @@ package object
|
|||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -54,6 +55,8 @@ type Token struct {
|
|||||||
Code string `xorm:"varchar(100) index" json:"code"`
|
Code string `xorm:"varchar(100) index" json:"code"`
|
||||||
AccessToken string `xorm:"mediumtext" json:"accessToken"`
|
AccessToken string `xorm:"mediumtext" json:"accessToken"`
|
||||||
RefreshToken string `xorm:"mediumtext" json:"refreshToken"`
|
RefreshToken string `xorm:"mediumtext" json:"refreshToken"`
|
||||||
|
AccessTokenHash string `xorm:"varchar(100) index" json:"accessTokenHash"`
|
||||||
|
RefreshTokenHash string `xorm:"varchar(100) index" json:"refreshTokenHash"`
|
||||||
ExpiresIn int `json:"expiresIn"`
|
ExpiresIn int `json:"expiresIn"`
|
||||||
Scope string `xorm:"varchar(100)" json:"scope"`
|
Scope string `xorm:"varchar(100)" json:"scope"`
|
||||||
TokenType string `xorm:"varchar(100)" json:"tokenType"`
|
TokenType string `xorm:"varchar(100)" json:"tokenType"`
|
||||||
@ -141,6 +144,48 @@ func getTokenByCode(code string) (*Token, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetTokenByAccessToken(accessToken string) (*Token, error) {
|
||||||
|
token := Token{AccessTokenHash: getTokenHash(accessToken)}
|
||||||
|
existed, err := ormer.Engine.Get(&token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !existed {
|
||||||
|
token = Token{AccessToken: accessToken}
|
||||||
|
existed, err = ormer.Engine.Get(&token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !existed {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTokenByRefreshToken(refreshToken string) (*Token, error) {
|
||||||
|
token := Token{RefreshTokenHash: getTokenHash(refreshToken)}
|
||||||
|
existed, err := ormer.Engine.Get(&token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !existed {
|
||||||
|
token = Token{RefreshToken: refreshToken}
|
||||||
|
existed, err = ormer.Engine.Get(&token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !existed {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &token, nil
|
||||||
|
}
|
||||||
|
|
||||||
func updateUsedByCode(token *Token) bool {
|
func updateUsedByCode(token *Token) bool {
|
||||||
affected, err := ormer.Engine.Where("code=?", token.Code).Cols("code_is_used").Update(token)
|
affected, err := ormer.Engine.Where("code=?", token.Code).Cols("code_is_used").Update(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -159,6 +204,24 @@ func (token *Token) GetId() string {
|
|||||||
return fmt.Sprintf("%s/%s", token.Owner, token.Name)
|
return fmt.Sprintf("%s/%s", token.Owner, token.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTokenHash(input string) string {
|
||||||
|
hash := sha256.Sum256([]byte(input))
|
||||||
|
res := hex.EncodeToString(hash[:])
|
||||||
|
if len(res) > 64 {
|
||||||
|
return res[:64]
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (token *Token) popularHashes() {
|
||||||
|
if token.AccessTokenHash == "" && token.AccessToken != "" {
|
||||||
|
token.AccessTokenHash = getTokenHash(token.AccessToken)
|
||||||
|
}
|
||||||
|
if token.RefreshTokenHash == "" && token.RefreshToken != "" {
|
||||||
|
token.RefreshTokenHash = getTokenHash(token.RefreshToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateToken(id string, token *Token) (bool, error) {
|
func UpdateToken(id string, token *Token) (bool, error) {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
if t, err := getToken(owner, name); err != nil {
|
if t, err := getToken(owner, name); err != nil {
|
||||||
@ -167,6 +230,8 @@ func UpdateToken(id string, token *Token) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token.popularHashes()
|
||||||
|
|
||||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(token)
|
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -176,6 +241,8 @@ func UpdateToken(id string, token *Token) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AddToken(token *Token) (bool, error) {
|
func AddToken(token *Token) (bool, error) {
|
||||||
|
token.popularHashes()
|
||||||
|
|
||||||
affected, err := ormer.Engine.Insert(token)
|
affected, err := ormer.Engine.Insert(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -194,18 +261,16 @@ func DeleteToken(token *Token) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token, error) {
|
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token, error) {
|
||||||
token := Token{AccessToken: accessToken}
|
token, err := GetTokenByAccessToken(accessToken)
|
||||||
existed, err := ormer.Engine.Get(&token)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, nil, err
|
return false, nil, nil, err
|
||||||
}
|
}
|
||||||
|
if token == nil {
|
||||||
if !existed {
|
|
||||||
return false, nil, nil, nil
|
return false, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
token.ExpiresIn = 0
|
token.ExpiresIn = 0
|
||||||
affected, err := ormer.Engine.ID(core.PK{token.Owner, token.Name}).Cols("expires_in").Update(&token)
|
affected, err := ormer.Engine.ID(core.PK{token.Owner, token.Name}).Cols("expires_in").Update(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, nil, err
|
return false, nil, nil, err
|
||||||
}
|
}
|
||||||
@ -215,22 +280,7 @@ func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token, e
|
|||||||
return false, nil, nil, err
|
return false, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return affected != 0, application, &token, nil
|
return affected != 0, application, token, nil
|
||||||
}
|
|
||||||
|
|
||||||
func GetTokenByAccessToken(accessToken string) (*Token, error) {
|
|
||||||
// Check if the accessToken is in the database
|
|
||||||
token := Token{AccessToken: accessToken}
|
|
||||||
existed, err := ormer.Engine.Get(&token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !existed {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &token, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTokenByTokenAndApplication(token string, application string) (*Token, error) {
|
func GetTokenByTokenAndApplication(token string, application string) (*Token, error) {
|
||||||
@ -432,16 +482,17 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
ErrorDescription: "client_id is invalid",
|
ErrorDescription: "client_id is invalid",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if clientSecret != "" && application.ClientSecret != clientSecret {
|
if clientSecret != "" && application.ClientSecret != clientSecret {
|
||||||
return &TokenError{
|
return &TokenError{
|
||||||
Error: InvalidClient,
|
Error: InvalidClient,
|
||||||
ErrorDescription: "client_secret is invalid",
|
ErrorDescription: "client_secret is invalid",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether the refresh token is valid, and has not expired.
|
// check whether the refresh token is valid, and has not expired.
|
||||||
token := Token{RefreshToken: refreshToken}
|
token, err := GetTokenByRefreshToken(refreshToken)
|
||||||
existed, err := ormer.Engine.Get(&token)
|
if err != nil || token == nil {
|
||||||
if err != nil || !existed {
|
|
||||||
return &TokenError{
|
return &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
ErrorDescription: "refresh token is invalid, expired or revoked",
|
ErrorDescription: "refresh token is invalid, expired or revoked",
|
||||||
@ -452,6 +503,12 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if cert == nil {
|
||||||
|
return &TokenError{
|
||||||
|
Error: InvalidGrant,
|
||||||
|
ErrorDescription: fmt.Sprintf("cert: %s cannot be found", application.Cert),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err = ParseJwtToken(refreshToken, cert)
|
_, err = ParseJwtToken(refreshToken, cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -460,6 +517,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate a new token
|
// generate a new token
|
||||||
user, err := getUser(application.Organization, token.User)
|
user, err := getUser(application.Organization, token.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -477,6 +535,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newAccessToken, newRefreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
newAccessToken, newRefreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &TokenError{
|
return &TokenError{
|
||||||
@ -504,7 +563,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = DeleteToken(&token)
|
_, err = DeleteToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -517,7 +576,6 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
ExpiresIn: newToken.ExpiresIn,
|
ExpiresIn: newToken.ExpiresIn,
|
||||||
Scope: newToken.Scope,
|
Scope: newToken.Scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenWrapper, nil
|
return tokenWrapper, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -621,25 +679,25 @@ func GetPasswordToken(application *Application, username string, password string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
ErrorDescription: "the user does not exist",
|
ErrorDescription: "the user does not exist",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
var msg string
|
|
||||||
if user.Ldap != "" {
|
if user.Ldap != "" {
|
||||||
msg = checkLdapUserPassword(user, password, "en")
|
err = checkLdapUserPassword(user, password, "en")
|
||||||
} else {
|
} else {
|
||||||
msg = CheckPassword(user, password, "en")
|
err = CheckPassword(user, password, "en")
|
||||||
}
|
}
|
||||||
if msg != "" {
|
if err != nil {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
ErrorDescription: "invalid username or password",
|
ErrorDescription: fmt.Sprintf("invalid username or password: %s", err.Error()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.IsForbidden {
|
if user.IsForbidden {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
@ -729,13 +787,13 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
|
|||||||
|
|
||||||
// GetTokenByUser
|
// GetTokenByUser
|
||||||
// Implicit flow
|
// Implicit flow
|
||||||
func GetTokenByUser(application *Application, user *User, scope string, host string) (*Token, error) {
|
func GetTokenByUser(application *Application, user *User, scope string, nonce string, host string) (*Token, error) {
|
||||||
err := ExtendUserWithRolesAndPermissions(user)
|
err := ExtendUserWithRolesAndPermissions(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, nonce, scope, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,12 @@ type Claims struct {
|
|||||||
type UserShort struct {
|
type UserShort struct {
|
||||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
|
|
||||||
|
Id string `xorm:"varchar(100) index" json:"id"`
|
||||||
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
Avatar string `xorm:"varchar(500)" json:"avatar"`
|
||||||
|
Email string `xorm:"varchar(100) index" json:"email"`
|
||||||
|
Phone string `xorm:"varchar(20) index" json:"phone"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserWithoutThirdIdp struct {
|
type UserWithoutThirdIdp struct {
|
||||||
@ -144,6 +150,12 @@ func getShortUser(user *User) *UserShort {
|
|||||||
res := &UserShort{
|
res := &UserShort{
|
||||||
Owner: user.Owner,
|
Owner: user.Owner,
|
||||||
Name: user.Name,
|
Name: user.Name,
|
||||||
|
|
||||||
|
Id: user.Id,
|
||||||
|
DisplayName: user.DisplayName,
|
||||||
|
Avatar: user.Avatar,
|
||||||
|
Email: user.Email,
|
||||||
|
Phone: user.Phone,
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -24,14 +24,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateRsaKeys(bitSize int, expireInYears int, commonName string, organization string) (string, string) {
|
func generateRsaKeys(bitSize int, expireInYears int, commonName string, organization string) (string, string, error) {
|
||||||
// https://stackoverflow.com/questions/64104586/use-golang-to-get-rsa-key-the-same-way-openssl-genrsa
|
// https://stackoverflow.com/questions/64104586/use-golang-to-get-rsa-key-the-same-way-openssl-genrsa
|
||||||
// https://stackoverflow.com/questions/43822945/golang-can-i-create-x509keypair-using-rsa-key
|
// https://stackoverflow.com/questions/43822945/golang-can-i-create-x509keypair-using-rsa-key
|
||||||
|
|
||||||
// Generate RSA key.
|
// Generate RSA key.
|
||||||
key, err := rsa.GenerateKey(rand.Reader, bitSize)
|
key, err := rsa.GenerateKey(rand.Reader, bitSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode private key to PKCS#1 ASN.1 PEM.
|
// Encode private key to PKCS#1 ASN.1 PEM.
|
||||||
@ -54,9 +54,10 @@ func generateRsaKeys(bitSize int, expireInYears int, commonName string, organiza
|
|||||||
},
|
},
|
||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
|
cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a pem block with the certificate
|
// Generate a pem block with the certificate
|
||||||
@ -65,5 +66,5 @@ func generateRsaKeys(bitSize int, expireInYears int, commonName string, organiza
|
|||||||
Bytes: cert,
|
Bytes: cert,
|
||||||
})
|
})
|
||||||
|
|
||||||
return string(certPem), string(privateKeyPem)
|
return string(certPem), string(privateKeyPem), nil
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,10 @@ import (
|
|||||||
|
|
||||||
func TestGenerateRsaKeys(t *testing.T) {
|
func TestGenerateRsaKeys(t *testing.T) {
|
||||||
fileId := "token_jwt_key"
|
fileId := "token_jwt_key"
|
||||||
certificate, privateKey := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization")
|
certificate, privateKey, err := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Write certificate (aka certificate) to file.
|
// Write certificate (aka certificate) to file.
|
||||||
util.WriteStringToPath(certificate, fmt.Sprintf("%s.pem", fileId))
|
util.WriteStringToPath(certificate, fmt.Sprintf("%s.pem", fileId))
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/go-webauthn/webauthn/webauthn"
|
"github.com/go-webauthn/webauthn/webauthn"
|
||||||
|
"github.com/xorm-io/builder"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -116,6 +117,7 @@ type User struct {
|
|||||||
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
|
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
|
||||||
Apple string `xorm:"apple varchar(100)" json:"apple"`
|
Apple string `xorm:"apple varchar(100)" json:"apple"`
|
||||||
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
|
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
|
||||||
|
AzureADB2c string `xorm:"azureadb2c varchar(100)" json:"azureadb2c"`
|
||||||
Slack string `xorm:"slack varchar(100)" json:"slack"`
|
Slack string `xorm:"slack varchar(100)" json:"slack"`
|
||||||
Steam string `xorm:"steam varchar(100)" json:"steam"`
|
Steam string `xorm:"steam varchar(100)" json:"steam"`
|
||||||
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
|
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
|
||||||
@ -231,6 +233,20 @@ func GetGlobalUsers() ([]*User, error) {
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetGlobalUsersWithFilter(cond builder.Cond) ([]*User, error) {
|
||||||
|
users := []*User{}
|
||||||
|
session := ormer.Engine.Desc("created_time")
|
||||||
|
if cond != nil {
|
||||||
|
session = session.Where(cond)
|
||||||
|
}
|
||||||
|
err := session.Find(&users)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetPaginationGlobalUsers(offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
func GetPaginationGlobalUsers(offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
session := GetSessionForUser("", offset, limit, field, value, sortField, sortOrder)
|
session := GetSessionForUser("", offset, limit, field, value, sortField, sortOrder)
|
||||||
@ -266,9 +282,27 @@ func GetUsers(owner string) ([]*User, error) {
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUsersByTag(owner string, tag string) ([]*User, error) {
|
func GetUsersWithFilter(owner string, cond builder.Cond) ([]*User, error) {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
err := ormer.Engine.Desc("created_time").Find(&users, &User{Owner: owner, Tag: tag})
|
session := ormer.Engine.Desc("created_time")
|
||||||
|
if cond != nil {
|
||||||
|
session = session.Where(cond)
|
||||||
|
}
|
||||||
|
err := session.Find(&users, &User{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUsersByTagWithFilter(owner string, tag string, cond builder.Cond) ([]*User, error) {
|
||||||
|
users := []*User{}
|
||||||
|
session := ormer.Engine.Desc("created_time")
|
||||||
|
if cond != nil {
|
||||||
|
session = session.Where(cond)
|
||||||
|
}
|
||||||
|
err := session.Find(&users, &User{Owner: owner, Tag: tag})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -589,7 +623,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
|||||||
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
||||||
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret",
|
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret",
|
||||||
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
||||||
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
||||||
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
||||||
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
|
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
|
||||||
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
|
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
|
||||||
@ -664,6 +698,8 @@ func UpdateUserForAllFields(id string, user *User) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user.UpdatedTime = util.GetCurrentTime()
|
||||||
|
|
||||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(user)
|
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -850,8 +886,20 @@ func (user *User) GetId() string {
|
|||||||
return fmt.Sprintf("%s/%s", user.Owner, user.Name)
|
return fmt.Sprintf("%s/%s", user.Owner, user.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) GetFriendlyName() string {
|
||||||
|
if user.FirstName != "" && user.LastName != "" {
|
||||||
|
return fmt.Sprintf("%s, %s", user.FirstName, user.LastName)
|
||||||
|
} else if user.DisplayName != "" {
|
||||||
|
return user.DisplayName
|
||||||
|
} else if user.Name != "" {
|
||||||
|
return user.Name
|
||||||
|
} else {
|
||||||
|
return user.Id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func isUserIdGlobalAdmin(userId string) bool {
|
func isUserIdGlobalAdmin(userId string) bool {
|
||||||
return strings.HasPrefix(userId, "built-in/")
|
return strings.HasPrefix(userId, "built-in/") || strings.HasPrefix(userId, "app/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtendUserWithRolesAndPermissions(user *User) (err error) {
|
func ExtendUserWithRolesAndPermissions(user *User) (err error) {
|
||||||
@ -986,7 +1034,10 @@ func GenerateIdForNewUser(application *Application) (string, error) {
|
|||||||
|
|
||||||
lastUserId := -1
|
lastUserId := -1
|
||||||
if lastUser != nil {
|
if lastUser != nil {
|
||||||
lastUserId = util.ParseInt(lastUser.Id)
|
lastUserId, err = util.ParseIntWithError(lastUser.Id)
|
||||||
|
if err != nil {
|
||||||
|
return util.GenerateId(), nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res := strconv.Itoa(lastUserId + 1)
|
res := strconv.Itoa(lastUserId + 1)
|
||||||
|
@ -35,11 +35,7 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
|
|||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("downloadImage() error for url [%s]: %s\n", url, err.Error())
|
fmt.Printf("downloadImage() error for url [%s]: %s\n", url, err.Error())
|
||||||
if strings.Contains(err.Error(), "EOF") || strings.Contains(err.Error(), "no such host") || strings.Contains(err.Error(), "did not properly respond after a period of time") || strings.Contains(err.Error(), "unrecognized name") {
|
|
||||||
return nil, "", nil
|
return nil, "", nil
|
||||||
} else {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
@ -58,6 +54,8 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
|
|||||||
|
|
||||||
if strings.Contains(contentType, "text/html") {
|
if strings.Contains(contentType, "text/html") {
|
||||||
fileExtension = ".html"
|
fileExtension = ".html"
|
||||||
|
} else if contentType == "image/vnd.microsoft.icon" {
|
||||||
|
fileExtension = ".ico"
|
||||||
} else {
|
} else {
|
||||||
fileExtensions, err := mime.ExtensionsByType(contentType)
|
fileExtensions, err := mime.ExtensionsByType(contentType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -186,10 +186,47 @@ func parseSize(sizes string) []int {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var publicEmailDomains = map[string]int{
|
||||||
|
"gmail.com": 1,
|
||||||
|
"163.com": 1,
|
||||||
|
"qq.com": 1,
|
||||||
|
"yahoo.com": 1,
|
||||||
|
"hotmail.com": 1,
|
||||||
|
"outlook.com": 1,
|
||||||
|
"icloud.com": 1,
|
||||||
|
"mail.com": 1,
|
||||||
|
"aol.com": 1,
|
||||||
|
"live.com": 1,
|
||||||
|
"yandex.com": 1,
|
||||||
|
"yahoo.co.jp": 1,
|
||||||
|
"yahoo.co.in": 1,
|
||||||
|
"yahoo.co.uk": 1,
|
||||||
|
"me.com": 1,
|
||||||
|
"msn.com": 1,
|
||||||
|
"comcast.net": 1,
|
||||||
|
"sbcglobal.net": 1,
|
||||||
|
"verizon.net": 1,
|
||||||
|
"earthlink.net": 1,
|
||||||
|
"cox.net": 1,
|
||||||
|
"rediffmail.com": 1,
|
||||||
|
"in.com": 1,
|
||||||
|
"hotmail.co.uk": 1,
|
||||||
|
"hotmail.fr": 1,
|
||||||
|
"zoho.com": 1,
|
||||||
|
"gmx.com": 1,
|
||||||
|
"gmx.de": 1,
|
||||||
|
"gmx.net": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPublicEmailDomain(domain string) bool {
|
||||||
|
_, exists := publicEmailDomains[domain]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, string, error) {
|
func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, string, error) {
|
||||||
tokens := strings.Split(email, "@")
|
tokens := strings.Split(email, "@")
|
||||||
domain := tokens[1]
|
domain := tokens[1]
|
||||||
if domain == "gmail.com" || domain == "163.com" || domain == "qq.com" {
|
if isPublicEmailDomain(domain) {
|
||||||
return nil, "", nil
|
return nil, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,13 +240,13 @@ func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, str
|
|||||||
if buffer != nil {
|
if buffer != nil {
|
||||||
faviconUrl, err = GetFaviconUrl(buffer.String())
|
faviconUrl, err = GetFaviconUrl(buffer.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
fmt.Printf("getFaviconFileBuffer() error, faviconUrl is empty, error = %s\n", err.Error())
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if !strings.HasPrefix(faviconUrl, "http") {
|
if !strings.HasPrefix(faviconUrl, "http") {
|
||||||
faviconUrl = util.UrlJoin(htmlUrl, faviconUrl)
|
faviconUrl = util.UrlJoin(htmlUrl, faviconUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if faviconUrl == "" {
|
if faviconUrl == "" {
|
||||||
faviconUrl = fmt.Sprintf("https://%s/favicon.ico", domain)
|
faviconUrl = fmt.Sprintf("https://%s/favicon.ico", domain)
|
||||||
|
@ -20,7 +20,10 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/idp"
|
"github.com/casdoor/casdoor/idp"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -110,6 +113,10 @@ func SetUserField(user *User, field string, value string) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user != nil {
|
||||||
|
user.UpdatedTime = util.GetCurrentTime()
|
||||||
|
}
|
||||||
|
|
||||||
_, err = ormer.Engine.ID(core.PK{user.Owner, user.Name}).Cols("hash").Update(user)
|
_, err = ormer.Engine.ID(core.PK{user.Owner, user.Name}).Cols("hash").Update(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -137,6 +144,25 @@ func setUserProperty(user *User, field string, value string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUserProperty(user *User, field string) string {
|
||||||
|
if user.Properties == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return user.Properties[field]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserExtraProperty(user *User, providerType, key string) (string, error) {
|
||||||
|
extraJson := getUserProperty(user, fmt.Sprintf("oauth_%s_extra", providerType))
|
||||||
|
if extraJson == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
extra := make(map[string]string)
|
||||||
|
if err := jsoniter.Unmarshal([]byte(extraJson), &extra); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return extra[key], nil
|
||||||
|
}
|
||||||
|
|
||||||
func SetUserOAuthProperties(organization *Organization, user *User, providerType string, userInfo *idp.UserInfo) (bool, error) {
|
func SetUserOAuthProperties(organization *Organization, user *User, providerType string, userInfo *idp.UserInfo) (bool, error) {
|
||||||
if userInfo.Id != "" {
|
if userInfo.Id != "" {
|
||||||
propertyName := fmt.Sprintf("oauth_%s_id", providerType)
|
propertyName := fmt.Sprintf("oauth_%s_id", providerType)
|
||||||
@ -180,6 +206,27 @@ func SetUserOAuthProperties(organization *Organization, user *User, providerType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if userInfo.Extra != nil {
|
||||||
|
// Save extra info as json string
|
||||||
|
propertyName := fmt.Sprintf("oauth_%s_extra", providerType)
|
||||||
|
oldExtraJson := getUserProperty(user, propertyName)
|
||||||
|
extra := make(map[string]string)
|
||||||
|
if oldExtraJson != "" {
|
||||||
|
if err := jsoniter.Unmarshal([]byte(oldExtraJson), &extra); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range userInfo.Extra {
|
||||||
|
extra[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
newExtraJson, err := jsoniter.Marshal(extra)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
setUserProperty(user, propertyName, string(newExtraJson))
|
||||||
|
}
|
||||||
|
|
||||||
return UpdateUserForAllFields(user.GetId(), user)
|
return UpdateUserForAllFields(user.GetId(), user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,9 +82,17 @@ func IsAllowSend(user *User, remoteAddr, recordType string) error {
|
|||||||
func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
|
func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
|
||||||
sender := organization.DisplayName
|
sender := organization.DisplayName
|
||||||
title := provider.Title
|
title := provider.Title
|
||||||
|
|
||||||
code := getRandomCode(6)
|
code := getRandomCode(6)
|
||||||
|
if organization.MasterVerificationCode != "" {
|
||||||
|
code = organization.MasterVerificationCode
|
||||||
|
}
|
||||||
|
|
||||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||||
content := fmt.Sprintf(provider.Content, code)
|
content := strings.Replace(provider.Content, "%s", code, 1)
|
||||||
|
if user != nil {
|
||||||
|
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
|
||||||
|
}
|
||||||
|
|
||||||
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil {
|
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -107,6 +115,10 @@ func SendVerificationCodeToPhone(organization *Organization, user *User, provide
|
|||||||
}
|
}
|
||||||
|
|
||||||
code := getRandomCode(6)
|
code := getRandomCode(6)
|
||||||
|
if organization.MasterVerificationCode != "" {
|
||||||
|
code = organization.MasterVerificationCode
|
||||||
|
}
|
||||||
|
|
||||||
if err := SendSms(provider, code, dest); err != nil {
|
if err := SendSms(provider, code, dest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -156,7 +168,7 @@ func getVerificationRecord(dest string) (*VerificationRecord, error) {
|
|||||||
return &record, nil
|
return &record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckVerificationCode(dest, code, lang string) *VerifyResult {
|
func CheckVerificationCode(dest string, code string, lang string) *VerifyResult {
|
||||||
record, err := getVerificationRecord(dest)
|
record, err := getVerificationRecord(dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -183,32 +195,32 @@ func CheckVerificationCode(dest, code, lang string) *VerifyResult {
|
|||||||
return &VerifyResult{VerificationSuccess, ""}
|
return &VerifyResult{VerificationSuccess, ""}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DisableVerificationCode(dest string) (err error) {
|
func DisableVerificationCode(dest string) error {
|
||||||
record, err := getVerificationRecord(dest)
|
record, err := getVerificationRecord(dest)
|
||||||
if record == nil || err != nil {
|
if record == nil || err != nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
record.IsUsed = true
|
record.IsUsed = true
|
||||||
_, err = ormer.Engine.ID(core.PK{record.Owner, record.Name}).AllCols().Update(record)
|
_, err = ormer.Engine.ID(core.PK{record.Owner, record.Name}).AllCols().Update(record)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckSigninCode(user *User, dest, code, lang string) string {
|
func CheckSigninCode(user *User, dest, code, lang string) error {
|
||||||
// check the login error times
|
// check the login error times
|
||||||
if msg := checkSigninErrorTimes(user, lang); msg != "" {
|
err := checkSigninErrorTimes(user, lang)
|
||||||
return msg
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := CheckVerificationCode(dest, code, lang)
|
result := CheckVerificationCode(dest, code, lang)
|
||||||
switch result.Code {
|
switch result.Code {
|
||||||
case VerificationSuccess:
|
case VerificationSuccess:
|
||||||
resetUserSigninErrorTimes(user)
|
return resetUserSigninErrorTimes(user)
|
||||||
return ""
|
|
||||||
case wrongCodeError:
|
case wrongCodeError:
|
||||||
return recordSigninErrorInfo(user, lang)
|
return recordSigninErrorInfo(user, lang)
|
||||||
default:
|
default:
|
||||||
return result.Msg
|
return fmt.Errorf(result.Msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
pp/alipay.go
20
pp/alipay.go
@ -49,20 +49,24 @@ func NewAlipayPaymentProvider(appId string, appCertificate string, appPrivateKey
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *AlipayPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
// pp.Client.DebugSwitch = gopay.DebugOn
|
// pp.Client.DebugSwitch = gopay.DebugOn
|
||||||
bm := gopay.BodyMap{}
|
bm := gopay.BodyMap{}
|
||||||
pp.Client.SetReturnUrl(returnUrl)
|
pp.Client.SetReturnUrl(r.ReturnUrl)
|
||||||
pp.Client.SetNotifyUrl(notifyUrl)
|
pp.Client.SetNotifyUrl(r.NotifyUrl)
|
||||||
bm.Set("subject", joinAttachString([]string{productName, productDisplayName, providerName}))
|
bm.Set("subject", joinAttachString([]string{r.ProductName, r.ProductDisplayName, r.ProviderName}))
|
||||||
bm.Set("out_trade_no", paymentName)
|
bm.Set("out_trade_no", r.PaymentName)
|
||||||
bm.Set("total_amount", priceFloat64ToString(price))
|
bm.Set("total_amount", priceFloat64ToString(r.Price))
|
||||||
|
|
||||||
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return payUrl, paymentName, nil
|
payResp := &PayResp{
|
||||||
|
PayUrl: payUrl,
|
||||||
|
OrderId: r.PaymentName,
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *AlipayPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *AlipayPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user