mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-04 20:10:35 +08:00
Compare commits
157 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
caa1ffe3c8 | ||
![]() |
c88edc4d3e | ||
![]() |
f5bc76016d | ||
![]() |
8345295d0c | ||
![]() |
3bdc0e0d1b | ||
![]() |
8dcb56ea71 | ||
![]() |
b95f107a60 | ||
![]() |
d6c2d0f3e8 | ||
![]() |
7cfece3019 | ||
![]() |
3efbcc739d | ||
![]() |
99ef329325 | ||
![]() |
8f0995ca34 | ||
![]() |
e64f181e28 | ||
![]() |
db56f54b8c | ||
![]() |
bf642b35d4 | ||
![]() |
5ee5299a68 | ||
![]() |
e7f395cfd4 | ||
![]() |
29d512d316 | ||
![]() |
5814ae6baf | ||
![]() |
52145abdc8 | ||
![]() |
157e515310 | ||
![]() |
47ed2e903c | ||
![]() |
822ad14ea9 | ||
![]() |
3355f8644e | ||
![]() |
00f06930ba | ||
![]() |
755d912f61 | ||
![]() |
95f2a3b311 | ||
![]() |
cb625b3fa2 | ||
![]() |
16cc64f08d | ||
![]() |
318cf52b33 | ||
![]() |
07f9a9ee96 | ||
![]() |
c2110ef59d | ||
![]() |
2f70e77e53 | ||
![]() |
98f6cc0085 | ||
![]() |
370e835499 | ||
![]() |
f43d01c5c2 | ||
![]() |
4ca5f4b196 | ||
![]() |
cf9e628a3e | ||
![]() |
726e4e3dc1 | ||
![]() |
0adb9b0047 | ||
![]() |
00ab156453 | ||
![]() |
589c0404d2 | ||
![]() |
b2f2674d3e | ||
![]() |
a555d27dd2 | ||
![]() |
8d8c662e58 | ||
![]() |
94c78593fc | ||
![]() |
f4265d015a | ||
![]() |
96e2f286ee | ||
![]() |
29807b82e1 | ||
![]() |
e0b7286882 | ||
![]() |
1762d19787 | ||
![]() |
2f71d9743b | ||
![]() |
6ba658ac60 | ||
![]() |
cc47f3b65d | ||
![]() |
eca1d23e35 | ||
![]() |
6947ebd152 | ||
![]() |
967113689d | ||
![]() |
b73b9a65b6 | ||
![]() |
70a550d8bc | ||
![]() |
113398c36b | ||
![]() |
667158f585 | ||
![]() |
dc9d2389a5 | ||
![]() |
c8b8488797 | ||
![]() |
07fa438348 | ||
![]() |
d2565e03c8 | ||
![]() |
cc2797ed27 | ||
![]() |
746967e18a | ||
![]() |
14c4f60a40 | ||
![]() |
79fd6ff5d3 | ||
![]() |
04bc8628a8 | ||
![]() |
d6c9ee508c | ||
![]() |
d224e728a3 | ||
![]() |
36b7993994 | ||
![]() |
c10ccd8106 | ||
![]() |
a04702a8d0 | ||
![]() |
e888ff8475 | ||
![]() |
7923bffa6d | ||
![]() |
bfd5d0172a | ||
![]() |
4e92a8273c | ||
![]() |
c46925dbe8 | ||
![]() |
7f39aee9c4 | ||
![]() |
b86087b2af | ||
![]() |
bddd57cda8 | ||
![]() |
44b59d866a | ||
![]() |
95600414d9 | ||
![]() |
9eb09b7db0 | ||
![]() |
91c0282040 | ||
![]() |
2b6f397bb9 | ||
![]() |
db7f4a4af9 | ||
![]() |
825de2bdaa | ||
![]() |
0b3742b0b1 | ||
![]() |
1394dce306 | ||
![]() |
64fc810359 | ||
![]() |
abba56a6f3 | ||
![]() |
cc933cf5f3 | ||
![]() |
37829062ad | ||
![]() |
f5fdf0af6a | ||
![]() |
21f433d278 | ||
![]() |
102e22f2c7 | ||
![]() |
8d6756fe9a | ||
![]() |
63f33d0ad9 | ||
![]() |
1c2e9064fe | ||
![]() |
54ef2ec09f | ||
![]() |
d84ddda607 | ||
![]() |
30a2fdef37 | ||
![]() |
87e6fb63e1 | ||
![]() |
e9e0721b34 | ||
![]() |
5ec678fa28 | ||
![]() |
77fffcacac | ||
![]() |
0e71e603ac | ||
![]() |
cbf973882d | ||
![]() |
467d709b8e | ||
![]() |
4d71725bf5 | ||
![]() |
9e920181d2 | ||
![]() |
db892333fe | ||
![]() |
acaee2e892 | ||
![]() |
5fd681e971 | ||
![]() |
b1db47bad1 | ||
![]() |
7520b71198 | ||
![]() |
7792f4589d | ||
![]() |
609e9785e4 | ||
![]() |
b7f2f9056f | ||
![]() |
ebc5fe454c | ||
![]() |
2e1b51910f | ||
![]() |
5526286ad8 | ||
![]() |
795240687d | ||
![]() |
6c30ccfb14 | ||
![]() |
802df55009 | ||
![]() |
353bf46daf | ||
![]() |
122970bb54 | ||
![]() |
fa3681ed75 | ||
![]() |
08e59796c8 | ||
![]() |
568372d077 | ||
![]() |
4f2668cd90 | ||
![]() |
8cb1291f6f | ||
![]() |
15786070bb | ||
![]() |
ba0d089589 | ||
![]() |
9a71bb02e3 | ||
![]() |
a1b5282da9 | ||
![]() |
80d2738863 | ||
![]() |
964b60da29 | ||
![]() |
ea8017dd4b | ||
![]() |
391bc3ebc9 | ||
![]() |
2326d55f73 | ||
![]() |
890030b3ef | ||
![]() |
725ee3393e | ||
![]() |
e61e46f1e5 | ||
![]() |
63c720985b | ||
![]() |
7582ba0b6f | ||
![]() |
a04f669580 | ||
![]() |
8cb96142db | ||
![]() |
d62a5fd7de | ||
![]() |
c7a3ad901b | ||
![]() |
0ef87632f4 | ||
![]() |
331862a94c | ||
![]() |
08eaf298c1 | ||
![]() |
69794fe29d |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*.go linguist-detectable=true
|
||||||
|
*.js linguist-detectable=false
|
59
.github/workflows/build.yml
vendored
59
.github/workflows/build.yml
vendored
@@ -14,6 +14,7 @@ jobs:
|
|||||||
- run: yarn install && CI=false yarn run build
|
- run: yarn install && CI=false yarn run build
|
||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
|
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
name: Back-end
|
name: Back-end
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -28,10 +29,10 @@ jobs:
|
|||||||
go build -race -ldflags "-extldflags '-static'"
|
go build -race -ldflags "-extldflags '-static'"
|
||||||
working-directory: ./
|
working-directory: ./
|
||||||
|
|
||||||
release:
|
release-and-push:
|
||||||
name: Release
|
name: Release And Push
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push'
|
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
|
||||||
needs: [ frontend, backend ]
|
needs: [ frontend, backend ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -42,26 +43,60 @@ jobs:
|
|||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 12
|
||||||
|
|
||||||
|
- name: Fetch Previous version
|
||||||
|
id: get-previous-tag
|
||||||
|
uses: actions-ecosystem/action-get-latest-tag@v1
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
run: yarn global add semantic-release@17.4.4 && semantic-release
|
run: yarn global add semantic-release@17.4.4 && semantic-release
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
|
GH_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
|
||||||
|
|
||||||
publish:
|
- name: Fetch Current version
|
||||||
name: Publish
|
id: get-current-tag
|
||||||
runs-on: ubuntu-latest
|
uses: actions-ecosystem/action-get-latest-tag@v1
|
||||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push'
|
|
||||||
needs: release
|
- name: Decide Should_Push Or Not
|
||||||
steps:
|
id: should_push
|
||||||
- name: Check out the repo
|
run: |
|
||||||
uses: actions/checkout@v2
|
old_version=${{steps.get-previous-tag.outputs.tag}}
|
||||||
|
new_version=${{steps.get-current-tag.outputs.tag }}
|
||||||
|
|
||||||
|
old_array=(${old_version//\./ })
|
||||||
|
new_array=(${new_version//\./ })
|
||||||
|
|
||||||
|
if [ ${old_array[0]} != ${new_array[0]} ]
|
||||||
|
then
|
||||||
|
echo ::set-output name=push::'true'
|
||||||
|
elif [ ${old_array[1]} != ${new_array[1]} ]
|
||||||
|
then
|
||||||
|
echo ::set-output name=push::'true'
|
||||||
|
|
||||||
|
else
|
||||||
|
echo ::set-output name=push::'false'
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
- 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'
|
||||||
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@v2
|
uses: docker/build-push-action@v2
|
||||||
|
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
tags: casbin/casdoor:latest
|
tags: casbin/casdoor:${{steps.get-current-tag.outputs.tag }},casbin/casdoor:latest
|
||||||
|
|
||||||
|
- name: Push All In One Version to Docker Hub
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||||
|
with:
|
||||||
|
target: ALLINONE
|
||||||
|
push: true
|
||||||
|
tags: casbin/casdoor-all-in-one:${{steps.get-current-tag.outputs.tag }},casbin/casdoor-all-in-one:latest
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
*.iml
|
*.iml
|
||||||
|
.vscode/
|
||||||
|
|
||||||
tmp/
|
tmp/
|
||||||
tmpFiles/
|
tmpFiles/
|
||||||
|
25
Dockerfile
25
Dockerfile
@@ -1,19 +1,36 @@
|
|||||||
FROM golang:1.16 AS BACK
|
FROM golang:1.17.5 AS BACK
|
||||||
WORKDIR /go/src/casdoor
|
WORKDIR /go/src/casdoor
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOPROXY=https://goproxy.cn,direct go build -ldflags="-w -s" -o server . \
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOPROXY=https://goproxy.cn,direct go build -ldflags="-w -s" -o server . \
|
||||||
&& apt update && apt install wait-for-it && chmod +x /usr/bin/wait-for-it
|
&& apt update && apt install wait-for-it && chmod +x /usr/bin/wait-for-it
|
||||||
|
|
||||||
FROM node:14.17.4 AS FRONT
|
FROM node:16.13.0 AS FRONT
|
||||||
WORKDIR /web
|
WORKDIR /web
|
||||||
COPY ./web .
|
COPY ./web .
|
||||||
RUN npm install && npm run build
|
RUN yarn config set registry https://registry.npm.taobao.org
|
||||||
|
RUN yarn install && yarn run build
|
||||||
|
|
||||||
|
|
||||||
|
FROM debian:latest AS ALLINONE
|
||||||
|
RUN apt update && apt install -y mariadb-server mariadb-client&&mkdir -p web/build && chmod 777 /tmp
|
||||||
|
LABEL MAINTAINER="https://casdoor.org/"
|
||||||
|
COPY --from=BACK /go/src/casdoor/ ./
|
||||||
|
COPY --from=BACK /usr/bin/wait-for-it ./
|
||||||
|
COPY --from=FRONT /web/build /web/build
|
||||||
|
CMD chmod 777 /tmp && service mariadb start&&\
|
||||||
|
if [ "${MYSQL_ROOT_PASSWORD}" = "" ] ;then MYSQL_ROOT_PASSWORD=123456 ; fi&&\
|
||||||
|
mysqladmin -u root password ${MYSQL_ROOT_PASSWORD} &&\
|
||||||
|
./wait-for-it localhost:3306 -- ./server
|
||||||
|
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
RUN sed -i 's/https/http/' /etc/apk/repositories
|
||||||
|
RUN apk add curl
|
||||||
LABEL MAINTAINER="https://casdoor.org/"
|
LABEL MAINTAINER="https://casdoor.org/"
|
||||||
|
|
||||||
COPY --from=BACK /go/src/casdoor/ ./
|
COPY --from=BACK /go/src/casdoor/ ./
|
||||||
COPY --from=BACK /usr/bin/wait-for-it ./
|
COPY --from=BACK /usr/bin/wait-for-it ./
|
||||||
RUN mkdir -p web/build && apk add --no-cache bash coreutils
|
RUN mkdir -p web/build && apk add --no-cache bash coreutils
|
||||||
COPY --from=FRONT /web/build /web/build
|
COPY --from=FRONT /web/build /web/build
|
||||||
CMD ./wait-for-it db:3306 -- ./server
|
CMD ./wait-for-it db:3306 -- ./server
|
||||||
|
|
||||||
|
14
README.md
14
README.md
@@ -69,6 +69,7 @@ We provide two start up methods for all kinds of users.
|
|||||||
### Manual
|
### Manual
|
||||||
|
|
||||||
#### Simple configuration
|
#### Simple configuration
|
||||||
|
Casdoor requires a running Relational database to be operational.Thus you need to modify configuration to point out the location of database.
|
||||||
|
|
||||||
Edit `conf/app.conf`, modify `dataSourceName` to correct database info, which follows this format:
|
Edit `conf/app.conf`, modify `dataSourceName` to correct database info, which follows this format:
|
||||||
|
|
||||||
@@ -85,9 +86,9 @@ Casdoor provides two run modes, the difference is binary size and user prompt.
|
|||||||
Edit `conf/app.conf`, set `runmode=dev`. Firstly build front-end files:
|
Edit `conf/app.conf`, set `runmode=dev`. Firstly build front-end files:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd web/ && npm install && npm run start
|
cd web/ && yarn && yarn run start
|
||||||
```
|
```
|
||||||
*❗ A word of caution ❗: the `npm` commands above need a recommended system RAM of at least 4GB. It has a potential failure during building the files if your RAM is not sufficient.*
|
*❗ A word of caution ❗: Casdoor's front-end is built using yarn. You should use `yarn` instead of `npm`. It has a potential failure during building the files if you use `npm`.*
|
||||||
|
|
||||||
Then build back-end binary file, change directory to root(Relative to casdoor):
|
Then build back-end binary file, change directory to root(Relative to casdoor):
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ That's it! Try to visit http://127.0.0.1:7001/. :small_airplane:
|
|||||||
Edit `conf/app.conf`, set `runmode=prod`. Firstly build front-end files:
|
Edit `conf/app.conf`, set `runmode=prod`. Firstly build front-end files:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd web/ && npm install && npm run build
|
cd web/ && yarn && yarn run build
|
||||||
```
|
```
|
||||||
|
|
||||||
Then build back-end binary file, change directory to root(Relative to casdoor):
|
Then build back-end binary file, change directory to root(Relative to casdoor):
|
||||||
@@ -119,11 +120,12 @@ go build main.go && sudo ./main
|
|||||||
This method requires [docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) to be installed first.
|
This method requires [docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/) to be installed first.
|
||||||
|
|
||||||
#### Simple configuration
|
#### Simple configuration
|
||||||
|
For the convenience of your first attempt, docker-compose.yml contains commands to start a database via docker.
|
||||||
|
|
||||||
Edit `conf/app.conf`, modify `dataSourceName` to the fixed content:
|
Thus edit `conf/app.conf` to point out the location of database(db:3306), modify `dataSourceName` to the fixed content:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dataSourceName = root:123@tcp(db:3306)/
|
dataSourceName = root:123456@tcp(db:3306)/
|
||||||
```
|
```
|
||||||
|
|
||||||
> If you need to modify `conf/app.conf`, you need to re-run `docker-compose up`.
|
> If you need to modify `conf/app.conf`, you need to re-run `docker-compose up`.
|
||||||
@@ -158,7 +160,7 @@ These all use casdoor as a centralized authentication platform.
|
|||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
For casdoor, if you have any questions, you can give Issues, and you can also directly Pull Requests(but we recommend give issues first to communicate with the community).
|
For casdoor, if you have any questions, you can give Issues, or you can also directly start Pull Requests(but we recommend giving issues first to communicate with the community).
|
||||||
|
|
||||||
### I18n notice
|
### I18n notice
|
||||||
|
|
||||||
|
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/casbin/casbin/v2"
|
"github.com/casbin/casbin/v2"
|
||||||
"github.com/casbin/casbin/v2/model"
|
"github.com/casbin/casbin/v2/model"
|
||||||
|
"github.com/casbin/casdoor/conf"
|
||||||
xormadapter "github.com/casbin/xorm-adapter/v2"
|
xormadapter "github.com/casbin/xorm-adapter/v2"
|
||||||
stringadapter "github.com/qiangmzsx/string-adapter/v2"
|
stringadapter "github.com/qiangmzsx/string-adapter/v2"
|
||||||
)
|
)
|
||||||
@@ -27,7 +28,7 @@ var Enforcer *casbin.Enforcer
|
|||||||
func InitAuthz() {
|
func InitAuthz() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
a, err := xormadapter.NewAdapter(beego.AppConfig.String("driverName"), beego.AppConfig.String("dataSourceName")+beego.AppConfig.String("dbName"), true)
|
a, err := xormadapter.NewAdapter(beego.AppConfig.String("driverName"), conf.GetBeegoConfDataSourceName()+beego.AppConfig.String("dbName"), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -93,9 +94,10 @@ p, *, *, POST, /api/send-verification-code, *, *
|
|||||||
p, *, *, GET, /api/get-human-check, *, *
|
p, *, *, GET, /api/get-human-check, *, *
|
||||||
p, *, *, POST, /api/reset-email-or-phone, *, *
|
p, *, *, POST, /api/reset-email-or-phone, *, *
|
||||||
p, *, *, POST, /api/upload-resource, *, *
|
p, *, *, POST, /api/upload-resource, *, *
|
||||||
p, *, *, POST, /api/paypal, *, *
|
p, *, *, GET, /.well-known/openid-configuration, *, *
|
||||||
p, *, *, GET, /api/success-pay, *, *
|
p, *, *, *, /api/certs, *, *
|
||||||
p, *, *, GET, /api/get-application-clientId, *, *
|
p, *, *, GET, /api/get-saml-login, *, *
|
||||||
|
p, *, *, POST, /api/acs, *, *
|
||||||
`
|
`
|
||||||
|
|
||||||
sa := stringadapter.NewAdapter(ruleText)
|
sa := stringadapter.NewAdapter(ruleText)
|
||||||
|
@@ -4,11 +4,14 @@ runmode = dev
|
|||||||
SessionOn = true
|
SessionOn = true
|
||||||
copyrequestbody = true
|
copyrequestbody = true
|
||||||
driverName = mysql
|
driverName = mysql
|
||||||
dataSourceName = root:123@tcp(localhost:3306)/
|
dataSourceName = root:123456@tcp(localhost:3306)/
|
||||||
dbName = casdoor
|
dbName = casdoor
|
||||||
redisEndpoint =
|
redisEndpoint =
|
||||||
defaultStorageProvider =
|
defaultStorageProvider =
|
||||||
|
isCloudIntranet = false
|
||||||
authState = "casdoor"
|
authState = "casdoor"
|
||||||
httpProxy = "127.0.0.1:10808"
|
httpProxy = "127.0.0.1:10808"
|
||||||
verificationCodeTimeout = 10
|
verificationCodeTimeout = 10
|
||||||
initScore = 2000
|
initScore = 2000
|
||||||
|
logPostOnly = true
|
||||||
|
origin = "https://door.casbin.com"
|
33
conf/conf.go
Normal file
33
conf/conf.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2021 The casbin 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 conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetBeegoConfDataSourceName() string {
|
||||||
|
dataSourceName := beego.AppConfig.String("dataSourceName")
|
||||||
|
|
||||||
|
runningInDocker := os.Getenv("RUNNING_IN_DOCKER")
|
||||||
|
if runningInDocker == "true" {
|
||||||
|
dataSourceName = strings.ReplaceAll(dataSourceName, "localhost", "host.docker.internal")
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataSourceName
|
||||||
|
}
|
@@ -20,7 +20,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
"github.com/casbin/casdoor/original"
|
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,11 +52,15 @@ type RequestForm struct {
|
|||||||
PhonePrefix string `json:"phonePrefix"`
|
PhonePrefix string `json:"phonePrefix"`
|
||||||
|
|
||||||
AutoSignin bool `json:"autoSignin"`
|
AutoSignin bool `json:"autoSignin"`
|
||||||
|
|
||||||
|
RelayState string `json:"relayState"`
|
||||||
|
SamlResponse string `json:"samlResponse"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Msg string `json:"msg"`
|
Msg string `json:"msg"`
|
||||||
|
Sub string `json:"sub"`
|
||||||
Data interface{} `json:"data"`
|
Data interface{} `json:"data"`
|
||||||
Data2 interface{} `json:"data2"`
|
Data2 interface{} `json:"data2"`
|
||||||
}
|
}
|
||||||
@@ -71,6 +74,7 @@ type HumanCheck struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Signup
|
// Signup
|
||||||
|
// @Tag Login API
|
||||||
// @Title Signup
|
// @Title Signup
|
||||||
// @Description sign up a new user
|
// @Description sign up a new user
|
||||||
// @Param username formData string true "The username to sign up"
|
// @Param username formData string true "The username to sign up"
|
||||||
@@ -95,26 +99,6 @@ func (c *ApiController) Signup() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if application.IsSignupItemEnabled("Email") {
|
|
||||||
checkResult := object.CheckVerificationCode(form.Email, form.EmailCode)
|
|
||||||
if len(checkResult) != 0 {
|
|
||||||
c.ResponseError(fmt.Sprintf("Email%s", checkResult))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var checkPhone string
|
|
||||||
if application.IsSignupItemEnabled("Phone") {
|
|
||||||
checkPhone = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Phone)
|
|
||||||
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode)
|
|
||||||
if len(checkResult) != 0 {
|
|
||||||
c.ResponseError(fmt.Sprintf("Phone%s", checkResult))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
userId := fmt.Sprintf("%s/%s", form.Organization, form.Username)
|
|
||||||
|
|
||||||
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", form.Organization))
|
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", form.Organization))
|
||||||
msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.Email, form.Phone, form.Affiliation)
|
msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.Email, form.Phone, form.Affiliation)
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
@@ -122,6 +106,26 @@ func (c *ApiController) Signup() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if application.IsSignupItemVisible("Email") && form.Email != "" {
|
||||||
|
checkResult := object.CheckVerificationCode(form.Email, form.EmailCode)
|
||||||
|
if len(checkResult) != 0 {
|
||||||
|
c.ResponseError(fmt.Sprintf("Email: %s", checkResult))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var checkPhone string
|
||||||
|
if application.IsSignupItemVisible("Phone") && form.Phone != "" {
|
||||||
|
checkPhone = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Phone)
|
||||||
|
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode)
|
||||||
|
if len(checkResult) != 0 {
|
||||||
|
c.ResponseError(fmt.Sprintf("Phone: %s", checkResult))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userId := fmt.Sprintf("%s/%s", form.Organization, form.Username)
|
||||||
|
|
||||||
id := util.GenerateId()
|
id := util.GenerateId()
|
||||||
if application.GetSignupItemRule("ID") == "Incremental" {
|
if application.GetSignupItemRule("ID") == "Incremental" {
|
||||||
lastUser := object.GetLastUser(form.Organization)
|
lastUser := object.GetLastUser(form.Organization)
|
||||||
@@ -152,15 +156,19 @@ func (c *ApiController) Signup() {
|
|||||||
IsAdmin: false,
|
IsAdmin: false,
|
||||||
IsGlobalAdmin: false,
|
IsGlobalAdmin: false,
|
||||||
IsForbidden: false,
|
IsForbidden: false,
|
||||||
|
IsDeleted: false,
|
||||||
SignupApplication: application.Name,
|
SignupApplication: application.Name,
|
||||||
Properties: map[string]string{},
|
Properties: map[string]string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
affected := object.AddUser(user)
|
affected := object.AddUser(user)
|
||||||
if affected {
|
if !affected {
|
||||||
original.AddUserToOriginalDatabase(user)
|
c.ResponseError(fmt.Sprintf("Failed to create user, user information is invalid: %s", util.StructToJson(user)))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object.AddUserToOriginalDatabase(user)
|
||||||
|
|
||||||
if application.HasPromptPage() {
|
if application.HasPromptPage() {
|
||||||
// The prompt page needs the user to be signed in
|
// The prompt page needs the user to be signed in
|
||||||
c.SetSessionUsername(user.GetId())
|
c.SetSessionUsername(user.GetId())
|
||||||
@@ -176,6 +184,7 @@ func (c *ApiController) Signup() {
|
|||||||
|
|
||||||
// Logout
|
// Logout
|
||||||
// @Title Logout
|
// @Title Logout
|
||||||
|
// @Tag Login API
|
||||||
// @Description logout the current user
|
// @Description logout the current user
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /logout [post]
|
// @router /logout [post]
|
||||||
@@ -191,6 +200,7 @@ func (c *ApiController) Logout() {
|
|||||||
|
|
||||||
// GetAccount
|
// GetAccount
|
||||||
// @Title GetAccount
|
// @Title GetAccount
|
||||||
|
// @Tag Account API
|
||||||
// @Description get the details of the current account
|
// @Description get the details of the current account
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
// @router /get-account [get]
|
// @router /get-account [get]
|
||||||
@@ -206,12 +216,21 @@ func (c *ApiController) GetAccount() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
organization := object.GetOrganizationByUser(user)
|
organization := object.GetMaskedOrganization(object.GetOrganizationByUser(user))
|
||||||
|
resp := Response{
|
||||||
c.ResponseOk(user, organization)
|
Status: "ok",
|
||||||
|
Sub: user.Id,
|
||||||
|
Data: user,
|
||||||
|
Data2: organization,
|
||||||
|
}
|
||||||
|
c.Data["json"] = resp
|
||||||
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHumanCheck ...
|
// GetHumanCheck ...
|
||||||
|
// @Tag Login API
|
||||||
|
// @Title GetHumancheck
|
||||||
|
// @router /api/get-human-check [get]
|
||||||
func (c *ApiController) GetHumanCheck() {
|
func (c *ApiController) GetHumanCheck() {
|
||||||
c.Data["json"] = HumanCheck{Type: "none"}
|
c.Data["json"] = HumanCheck{Type: "none"}
|
||||||
|
|
||||||
|
@@ -17,24 +17,36 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/utils/pagination"
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetApplications
|
// GetApplications
|
||||||
// @Title GetApplications
|
// @Title GetApplications
|
||||||
|
// @Tag Application API
|
||||||
// @Description get all applications
|
// @Description get all applications
|
||||||
// @Param owner query string true "The owner of applications."
|
// @Param owner query string true "The owner of applications."
|
||||||
// @Success 200 {array} object.Application The Response object
|
// @Success 200 {array} object.Application The Response object
|
||||||
// @router /get-applications [get]
|
// @router /get-applications [get]
|
||||||
func (c *ApiController) GetApplications() {
|
func (c *ApiController) GetApplications() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
|
limit := c.Input().Get("pageSize")
|
||||||
c.Data["json"] = object.GetApplications(owner)
|
page := c.Input().Get("p")
|
||||||
c.ServeJSON()
|
if limit == "" || page == "" {
|
||||||
|
c.Data["json"] = object.GetApplications(owner)
|
||||||
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetApplicationCount(owner)))
|
||||||
|
applications := object.GetPaginationApplications(owner, paginator.Offset(), limit)
|
||||||
|
c.ResponseOk(applications, paginator.Nums())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetApplication
|
// GetApplication
|
||||||
// @Title GetApplication
|
// @Title GetApplication
|
||||||
|
// @Tag Application API
|
||||||
// @Description get the detail of an application
|
// @Description get the detail of an application
|
||||||
// @Param id query string true "The id of the application."
|
// @Param id query string true "The id of the application."
|
||||||
// @Success 200 {object} object.Application The Response object
|
// @Success 200 {object} object.Application The Response object
|
||||||
@@ -48,6 +60,7 @@ func (c *ApiController) GetApplication() {
|
|||||||
|
|
||||||
// GetUserApplication
|
// GetUserApplication
|
||||||
// @Title GetUserApplication
|
// @Title GetUserApplication
|
||||||
|
// @Tag Application API
|
||||||
// @Description get the detail of the user's application
|
// @Description get the detail of the user's application
|
||||||
// @Param id query string true "The id of the user"
|
// @Param id query string true "The id of the user"
|
||||||
// @Success 200 {object} object.Application The Response object
|
// @Success 200 {object} object.Application The Response object
|
||||||
@@ -66,6 +79,7 @@ func (c *ApiController) GetUserApplication() {
|
|||||||
|
|
||||||
// UpdateApplication
|
// UpdateApplication
|
||||||
// @Title UpdateApplication
|
// @Title UpdateApplication
|
||||||
|
// @Tag Application API
|
||||||
// @Description update an application
|
// @Description update an application
|
||||||
// @Param id query string true "The id of the application"
|
// @Param id query string true "The id of the application"
|
||||||
// @Param body body object.Application true "The details of the application"
|
// @Param body body object.Application true "The details of the application"
|
||||||
@@ -86,6 +100,7 @@ func (c *ApiController) UpdateApplication() {
|
|||||||
|
|
||||||
// AddApplication
|
// AddApplication
|
||||||
// @Title AddApplication
|
// @Title AddApplication
|
||||||
|
// @Tag Application API
|
||||||
// @Description add an application
|
// @Description add an application
|
||||||
// @Param body body object.Application true "The details of the application"
|
// @Param body body object.Application true "The details of the application"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
@@ -103,6 +118,7 @@ func (c *ApiController) AddApplication() {
|
|||||||
|
|
||||||
// DeleteApplication
|
// DeleteApplication
|
||||||
// @Title DeleteApplication
|
// @Title DeleteApplication
|
||||||
|
// @Tag Application API
|
||||||
// @Description delete an application
|
// @Description delete an application
|
||||||
// @Param body body object.Application true "The details of the application"
|
// @Param body body object.Application true "The details of the application"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
@@ -117,10 +133,3 @@ func (c *ApiController) DeleteApplication() {
|
|||||||
c.Data["json"] = wrapActionResponse(object.DeleteApplication(&application))
|
c.Data["json"] = wrapActionResponse(object.DeleteApplication(&application))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) GetApplicationByClientId() {
|
|
||||||
clientId := c.Input().Get("clientId")
|
|
||||||
|
|
||||||
c.Data["json"] = object.GetApplicationByClientId(clientId)
|
|
||||||
c.ServeJSON()
|
|
||||||
}
|
|
||||||
|
@@ -15,8 +15,10 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -49,8 +51,8 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
|||||||
redirectUri := c.Input().Get("redirectUri")
|
redirectUri := c.Input().Get("redirectUri")
|
||||||
scope := c.Input().Get("scope")
|
scope := c.Input().Get("scope")
|
||||||
state := c.Input().Get("state")
|
state := c.Input().Get("state")
|
||||||
|
nonce := c.Input().Get("nonce")
|
||||||
code := object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state)
|
code := object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce)
|
||||||
resp = codeToResponse(code)
|
resp = codeToResponse(code)
|
||||||
|
|
||||||
if application.HasPromptPage() {
|
if application.HasPromptPage() {
|
||||||
@@ -75,6 +77,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
|||||||
|
|
||||||
// GetApplicationLogin ...
|
// GetApplicationLogin ...
|
||||||
// @Title GetApplicationLogin
|
// @Title GetApplicationLogin
|
||||||
|
// @Tag Login API
|
||||||
// @Description get application login
|
// @Description get application login
|
||||||
// @Param clientId query string true "client id"
|
// @Param clientId query string true "client id"
|
||||||
// @Param responseType query string true "response type"
|
// @Param responseType query string true "response type"
|
||||||
@@ -108,6 +111,7 @@ func setHttpClient(idProvider idp.IdProvider, providerType string) {
|
|||||||
|
|
||||||
// Login ...
|
// Login ...
|
||||||
// @Title Login
|
// @Title Login
|
||||||
|
// @Tag Login API
|
||||||
// @Description login
|
// @Description login
|
||||||
// @Param oAuthParams query string true "oAuth parameters"
|
// @Param oAuthParams query string true "oAuth parameters"
|
||||||
// @Param body body RequestForm true "Login information"
|
// @Param body body RequestForm true "Login information"
|
||||||
@@ -136,53 +140,35 @@ func (c *ApiController) Login() {
|
|||||||
|
|
||||||
if form.Password == "" {
|
if form.Password == "" {
|
||||||
var verificationCodeType string
|
var verificationCodeType string
|
||||||
|
var checkResult string
|
||||||
|
|
||||||
// check result through Email or Phone
|
// check result through Email or Phone
|
||||||
if strings.Contains(form.Email, "@") {
|
if strings.Contains(form.Username, "@") {
|
||||||
verificationCodeType = "email"
|
verificationCodeType = "email"
|
||||||
checkResult := object.CheckVerificationCode(form.Email, form.EmailCode)
|
checkResult = object.CheckVerificationCode(form.Username, form.Code)
|
||||||
if len(checkResult) != 0 {
|
|
||||||
responseText := fmt.Sprintf("Email%s", checkResult)
|
|
||||||
c.ResponseError(responseText)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
verificationCodeType = "phone"
|
verificationCodeType = "phone"
|
||||||
checkPhone := fmt.Sprintf("+%s%s", form.PhonePrefix, form.Email)
|
if len(form.PhonePrefix) == 0 {
|
||||||
checkResult := object.CheckVerificationCode(checkPhone, form.EmailCode)
|
responseText := fmt.Sprintf("%s%s", verificationCodeType, "No phone prefix")
|
||||||
if len(checkResult) != 0 {
|
|
||||||
responseText := fmt.Sprintf("Phone%s", checkResult)
|
|
||||||
c.ResponseError(responseText)
|
c.ResponseError(responseText)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
checkPhone := fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
|
||||||
|
checkResult = object.CheckVerificationCode(checkPhone, form.Code)
|
||||||
}
|
}
|
||||||
|
if len(checkResult) != 0 {
|
||||||
// get user
|
responseText := fmt.Sprintf("%s%s", verificationCodeType, checkResult)
|
||||||
var userId string
|
c.ResponseError(responseText)
|
||||||
if form.Username == "" {
|
|
||||||
userId, _ = c.RequireSignedIn()
|
|
||||||
} else {
|
|
||||||
userId = fmt.Sprintf("%s/%s", form.Organization, form.Username)
|
|
||||||
}
|
|
||||||
|
|
||||||
user = object.GetUser(userId)
|
|
||||||
if user == nil {
|
|
||||||
c.ResponseError("No such user.")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable the verification code
|
// disable the verification code
|
||||||
switch verificationCodeType {
|
object.DisableVerificationCode(form.Username)
|
||||||
case "email":
|
|
||||||
if user.Email != form.Email {
|
user = object.GetUserByFields(form.Organization, form.Username)
|
||||||
c.ResponseError("wrong email!")
|
if user == nil {
|
||||||
}
|
c.ResponseError("No such user.")
|
||||||
object.DisableVerificationCode(form.Email)
|
return
|
||||||
case "phone":
|
|
||||||
if user.Phone != form.Email {
|
|
||||||
c.ResponseError("wrong phone!")
|
|
||||||
}
|
|
||||||
object.DisableVerificationCode(form.Email)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
password := form.Password
|
password := form.Password
|
||||||
@@ -195,11 +181,10 @@ func (c *ApiController) Login() {
|
|||||||
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
|
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
|
||||||
resp = c.HandleLoggedIn(application, user, &form)
|
resp = c.HandleLoggedIn(application, user, &form)
|
||||||
|
|
||||||
record := util.Records(c.Ctx)
|
record := object.NewRecord(c.Ctx)
|
||||||
record.Organization = application.Organization
|
record.Organization = application.Organization
|
||||||
record.Username = user.Name
|
record.User = user.Name
|
||||||
|
go object.AddRecord(record)
|
||||||
object.AddRecord(record)
|
|
||||||
}
|
}
|
||||||
} else if form.Provider != "" {
|
} else if form.Provider != "" {
|
||||||
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
|
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
|
||||||
@@ -211,47 +196,71 @@ func (c *ApiController) Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
idProvider := idp.GetIdProvider(provider.Type, provider.ClientId, provider.ClientSecret, form.RedirectUri)
|
userInfo := &idp.UserInfo{}
|
||||||
if idProvider == nil {
|
if provider.Category == "SAML" {
|
||||||
c.ResponseError(fmt.Sprintf("The provider type: %s is not supported", provider.Type))
|
// SAML
|
||||||
return
|
userInfo.Id, err = object.ParseSamlResponse(form.SamlResponse, provider.Type)
|
||||||
}
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if provider.Category == "OAuth" {
|
||||||
|
// OAuth
|
||||||
|
|
||||||
setHttpClient(idProvider, provider.Type)
|
clientId := provider.ClientId
|
||||||
|
clientSecret := provider.ClientSecret
|
||||||
|
if provider.Type == "WeChat" && strings.Contains(c.Ctx.Request.UserAgent(), "MicroMessenger") {
|
||||||
|
clientId = provider.ClientId2
|
||||||
|
clientSecret = provider.ClientSecret2
|
||||||
|
}
|
||||||
|
|
||||||
if form.State != beego.AppConfig.String("authState") && form.State != application.Name {
|
idProvider := idp.GetIdProvider(provider.Type, clientId, clientSecret, form.RedirectUri)
|
||||||
c.ResponseError(fmt.Sprintf("state expected: \"%s\", but got: \"%s\"", beego.AppConfig.String("authState"), form.State))
|
if idProvider == nil {
|
||||||
return
|
c.ResponseError(fmt.Sprintf("The provider type: %s is not supported", provider.Type))
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/golang/oauth2/issues/123#issuecomment-103715338
|
setHttpClient(idProvider, provider.Type)
|
||||||
token, err := idProvider.GetToken(form.Code)
|
|
||||||
if err != nil {
|
|
||||||
c.ResponseError(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !token.Valid() {
|
if form.State != beego.AppConfig.String("authState") && form.State != application.Name {
|
||||||
c.ResponseError("Invalid token")
|
c.ResponseError(fmt.Sprintf("state expected: \"%s\", but got: \"%s\"", beego.AppConfig.String("authState"), form.State))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userInfo, err := idProvider.GetUserInfo(token)
|
// https://github.com/golang/oauth2/issues/123#issuecomment-103715338
|
||||||
if err != nil {
|
token, err := idProvider.GetToken(form.Code)
|
||||||
c.ResponseError(fmt.Sprintf("Failed to login in: %s", err.Error()))
|
if err != nil {
|
||||||
return
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !token.Valid() {
|
||||||
|
c.ResponseError("Invalid token")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfo, err = idProvider.GetUserInfo(token)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(fmt.Sprintf("Failed to login in: %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Method == "signup" {
|
if form.Method == "signup" {
|
||||||
user := object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
|
user := &object.User{}
|
||||||
if user == nil {
|
if provider.Category == "SAML" {
|
||||||
user = object.GetUserByField(application.Organization, provider.Type, userInfo.Username)
|
user = object.GetUser(fmt.Sprintf("%s/%s", application.Organization, userInfo.Id))
|
||||||
}
|
} else if provider.Category == "OAuth" {
|
||||||
if user == nil {
|
user = object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
|
||||||
user = object.GetUserByField(application.Organization, "name", userInfo.Username)
|
if user == nil {
|
||||||
|
user = object.GetUserByField(application.Organization, provider.Type, userInfo.Username)
|
||||||
|
}
|
||||||
|
if user == nil {
|
||||||
|
user = object.GetUserByField(application.Organization, "name", userInfo.Username)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if user != nil {
|
if user != nil && user.IsDeleted == false {
|
||||||
// Sign in via OAuth (want to sign up but already have account)
|
// Sign in via OAuth (want to sign up but already have account)
|
||||||
|
|
||||||
if user.IsForbidden {
|
if user.IsForbidden {
|
||||||
@@ -260,12 +269,11 @@ func (c *ApiController) Login() {
|
|||||||
|
|
||||||
resp = c.HandleLoggedIn(application, user, &form)
|
resp = c.HandleLoggedIn(application, user, &form)
|
||||||
|
|
||||||
record := util.Records(c.Ctx)
|
record := object.NewRecord(c.Ctx)
|
||||||
record.Organization = application.Organization
|
record.Organization = application.Organization
|
||||||
record.Username = user.Name
|
record.User = user.Name
|
||||||
|
go object.AddRecord(record)
|
||||||
object.AddRecord(record)
|
} else if provider.Category == "OAuth" {
|
||||||
} else {
|
|
||||||
// Sign up via OAuth
|
// Sign up via OAuth
|
||||||
if !application.EnableSignUp {
|
if !application.EnableSignUp {
|
||||||
c.ResponseError(fmt.Sprintf("The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support", provider.Type, userInfo.Username, userInfo.DisplayName))
|
c.ResponseError(fmt.Sprintf("The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support", provider.Type, userInfo.Username, userInfo.DisplayName))
|
||||||
@@ -279,7 +287,7 @@ func (c *ApiController) Login() {
|
|||||||
|
|
||||||
properties := map[string]string{}
|
properties := map[string]string{}
|
||||||
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
|
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
|
||||||
user := &object.User{
|
user = &object.User{
|
||||||
Owner: application.Organization,
|
Owner: application.Organization,
|
||||||
Name: userInfo.Username,
|
Name: userInfo.Username,
|
||||||
CreatedTime: util.GetCurrentTime(),
|
CreatedTime: util.GetCurrentTime(),
|
||||||
@@ -293,23 +301,29 @@ func (c *ApiController) Login() {
|
|||||||
IsAdmin: false,
|
IsAdmin: false,
|
||||||
IsGlobalAdmin: false,
|
IsGlobalAdmin: false,
|
||||||
IsForbidden: false,
|
IsForbidden: false,
|
||||||
|
IsDeleted: false,
|
||||||
SignupApplication: application.Name,
|
SignupApplication: application.Name,
|
||||||
Properties: properties,
|
Properties: properties,
|
||||||
}
|
}
|
||||||
object.AddUser(user)
|
|
||||||
|
|
||||||
// sync info from 3rd-party if possible
|
// sync info from 3rd-party if possible
|
||||||
object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
|
object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
|
||||||
|
|
||||||
|
affected := object.AddUser(user)
|
||||||
|
if !affected {
|
||||||
|
c.ResponseError(fmt.Sprintf("Failed to create user, user information is invalid: %s", util.StructToJson(user)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
object.LinkUserAccount(user, provider.Type, userInfo.Id)
|
object.LinkUserAccount(user, provider.Type, userInfo.Id)
|
||||||
|
|
||||||
resp = c.HandleLoggedIn(application, user, &form)
|
resp = c.HandleLoggedIn(application, user, &form)
|
||||||
|
|
||||||
record := util.Records(c.Ctx)
|
record := object.NewRecord(c.Ctx)
|
||||||
record.Organization = application.Organization
|
record.Organization = application.Organization
|
||||||
record.Username = user.Name
|
record.User = user.Name
|
||||||
|
go object.AddRecord(record)
|
||||||
object.AddRecord(record)
|
} else if provider.Category == "SAML" {
|
||||||
|
resp = &Response{Status: "error", Msg: "The account does not exist"}
|
||||||
}
|
}
|
||||||
//resp = &Response{Status: "ok", Msg: "", Data: res}
|
//resp = &Response{Status: "ok", Msg: "", Data: res}
|
||||||
} else { // form.Method != "signup"
|
} else { // form.Method != "signup"
|
||||||
@@ -341,10 +355,42 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.ResponseError(fmt.Sprintf("unknown authentication type (not password or provider), form = %s", util.StructToJson(form)))
|
if c.GetSessionUsername() != "" {
|
||||||
return
|
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
|
||||||
|
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
|
||||||
|
user := c.getCurrentUser()
|
||||||
|
resp = c.HandleLoggedIn(application, user, &form)
|
||||||
|
} else {
|
||||||
|
c.ResponseError(fmt.Sprintf("unknown authentication type (not password or provider), form = %s", util.StructToJson(form)))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = resp
|
c.Data["json"] = resp
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ApiController) GetSamlLogin() {
|
||||||
|
providerId := c.Input().Get("id")
|
||||||
|
relayState := c.Input().Get("relayState")
|
||||||
|
authURL, method, err := object.GenerateSamlLoginUrl(providerId, relayState)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
}
|
||||||
|
c.ResponseOk(authURL, method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ApiController) HandleSamlLogin() {
|
||||||
|
relayState := c.Input().Get("RelayState")
|
||||||
|
samlResponse := c.Input().Get("SAMLResponse")
|
||||||
|
decode, err := base64.StdEncoding.DecodeString(relayState)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
}
|
||||||
|
slice := strings.Split(string(decode), "&")
|
||||||
|
relayState = url.QueryEscape(relayState)
|
||||||
|
samlResponse = url.QueryEscape(samlResponse)
|
||||||
|
targetUrl := fmt.Sprintf("%s?relayState=%s&samlResponse=%s",
|
||||||
|
slice[4], relayState, samlResponse)
|
||||||
|
c.Redirect(targetUrl, 303)
|
||||||
|
}
|
||||||
|
@@ -21,10 +21,16 @@ import (
|
|||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// controller for handlers under /api uri
|
||||||
type ApiController struct {
|
type ApiController struct {
|
||||||
beego.Controller
|
beego.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// controller for handlers directly under / (root)
|
||||||
|
type RootController struct {
|
||||||
|
ApiController
|
||||||
|
}
|
||||||
|
|
||||||
type SessionData struct {
|
type SessionData struct {
|
||||||
ExpireTime int64
|
ExpireTime int64
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
@@ -43,6 +44,9 @@ type LdapSyncResp struct {
|
|||||||
Failed []object.LdapRespUser `json:"failed"`
|
Failed []object.LdapRespUser `json:"failed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Account API
|
||||||
|
// @Title GetLdapser
|
||||||
|
// @router /get-ldap-user [post]
|
||||||
func (c *ApiController) GetLdapUser() {
|
func (c *ApiController) GetLdapUser() {
|
||||||
ldapServer := LdapServer{}
|
ldapServer := LdapServer{}
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer)
|
||||||
@@ -96,6 +100,9 @@ func (c *ApiController) GetLdapUser() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Account API
|
||||||
|
// @Title GetLdaps
|
||||||
|
// @router /get-ldaps [post]
|
||||||
func (c *ApiController) GetLdaps() {
|
func (c *ApiController) GetLdaps() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
|
|
||||||
@@ -103,6 +110,9 @@ func (c *ApiController) GetLdaps() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Account API
|
||||||
|
// @Title GetLdap
|
||||||
|
// @router /get-ldap [post]
|
||||||
func (c *ApiController) GetLdap() {
|
func (c *ApiController) GetLdap() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
@@ -115,6 +125,9 @@ func (c *ApiController) GetLdap() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Account API
|
||||||
|
// @Title AddLdap
|
||||||
|
// @router /add-ldap [post]
|
||||||
func (c *ApiController) AddLdap() {
|
func (c *ApiController) AddLdap() {
|
||||||
var ldap object.Ldap
|
var ldap object.Ldap
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
|
||||||
@@ -138,11 +151,17 @@ func (c *ApiController) AddLdap() {
|
|||||||
if affected {
|
if affected {
|
||||||
resp.Data2 = ldap
|
resp.Data2 = ldap
|
||||||
}
|
}
|
||||||
|
if ldap.AutoSync != 0 {
|
||||||
|
object.GetLdapAutoSynchronizer().StartAutoSync(ldap.Id)
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = resp
|
c.Data["json"] = resp
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Account API
|
||||||
|
// @Title UpdateLdap
|
||||||
|
// @router /update-ldap [post]
|
||||||
func (c *ApiController) UpdateLdap() {
|
func (c *ApiController) UpdateLdap() {
|
||||||
var ldap object.Ldap
|
var ldap object.Ldap
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
|
||||||
@@ -156,11 +175,17 @@ func (c *ApiController) UpdateLdap() {
|
|||||||
if affected {
|
if affected {
|
||||||
resp.Data2 = ldap
|
resp.Data2 = ldap
|
||||||
}
|
}
|
||||||
|
if ldap.AutoSync != 0 {
|
||||||
|
object.GetLdapAutoSynchronizer().StartAutoSync(ldap.Id)
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = resp
|
c.Data["json"] = resp
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Account API
|
||||||
|
// @Title DeleteLdap
|
||||||
|
// @router /delete-ldap [post]
|
||||||
func (c *ApiController) DeleteLdap() {
|
func (c *ApiController) DeleteLdap() {
|
||||||
var ldap object.Ldap
|
var ldap object.Ldap
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
|
||||||
@@ -168,10 +193,14 @@ func (c *ApiController) DeleteLdap() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object.GetLdapAutoSynchronizer().StopAutoSync(ldap.Id)
|
||||||
c.Data["json"] = wrapActionResponse(object.DeleteLdap(&ldap))
|
c.Data["json"] = wrapActionResponse(object.DeleteLdap(&ldap))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Account API
|
||||||
|
// @Title SyncLdapUsers
|
||||||
|
// @router /sync-ldap-users [post]
|
||||||
func (c *ApiController) SyncLdapUsers() {
|
func (c *ApiController) SyncLdapUsers() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
ldapId := c.Input().Get("ldapId")
|
ldapId := c.Input().Get("ldapId")
|
||||||
@@ -191,6 +220,9 @@ func (c *ApiController) SyncLdapUsers() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Account API
|
||||||
|
// @Title CheckLdapUserExist
|
||||||
|
// @router /check-ldap-users-exist [post]
|
||||||
func (c *ApiController) CheckLdapUsersExist() {
|
func (c *ApiController) CheckLdapUsersExist() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
var uuids []string
|
var uuids []string
|
||||||
|
@@ -25,6 +25,8 @@ type LinkForm struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unlink ...
|
// Unlink ...
|
||||||
|
// @router /unlink [post]
|
||||||
|
// @Tag Login API
|
||||||
func (c *ApiController) Unlink() {
|
func (c *ApiController) Unlink() {
|
||||||
userId, ok := c.RequireSignedIn()
|
userId, ok := c.RequireSignedIn()
|
||||||
if !ok {
|
if !ok {
|
||||||
|
38
controllers/oidc_discovery.go
Normal file
38
controllers/oidc_discovery.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import "github.com/casbin/casdoor/object"
|
||||||
|
|
||||||
|
// @Title GetOidcDiscovery
|
||||||
|
// @Tag OIDC API
|
||||||
|
// @router /.well-known/openid-configuration [get]
|
||||||
|
func (c *RootController) GetOidcDiscovery() {
|
||||||
|
c.Data["json"] = object.GetOidcDiscovery()
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Title GetOidcCert
|
||||||
|
// @Tag OIDC API
|
||||||
|
// @router /api/certs [get]
|
||||||
|
func (c *RootController) GetOidcCert() {
|
||||||
|
jwks, err := object.GetJSONWebKeySet()
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Data["json"] = jwks
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
@@ -17,24 +17,36 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/utils/pagination"
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetOrganizations ...
|
// GetOrganizations ...
|
||||||
// @Title GetOrganizations
|
// @Title GetOrganizations
|
||||||
|
// @Tag Organization API
|
||||||
// @Description get organizations
|
// @Description get organizations
|
||||||
// @Param owner query string true "owner"
|
// @Param owner query string true "owner"
|
||||||
// @Success 200 {array} object.Organization The Response object
|
// @Success 200 {array} object.Organization The Response object
|
||||||
// @router /get-organizations [get]
|
// @router /get-organizations [get]
|
||||||
func (c *ApiController) GetOrganizations() {
|
func (c *ApiController) GetOrganizations() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
|
limit := c.Input().Get("pageSize")
|
||||||
c.Data["json"] = object.GetOrganizations(owner)
|
page := c.Input().Get("p")
|
||||||
c.ServeJSON()
|
if limit == "" || page == "" {
|
||||||
|
c.Data["json"] = object.GetMaskedOrganizations(object.GetOrganizations(owner))
|
||||||
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetOrganizationCount(owner)))
|
||||||
|
organizations := object.GetMaskedOrganizations(object.GetPaginationOrganizations(owner, paginator.Offset(), limit))
|
||||||
|
c.ResponseOk(organizations, paginator.Nums())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrganization ...
|
// GetOrganization ...
|
||||||
// @Title GetOrganization
|
// @Title GetOrganization
|
||||||
|
// @Tag Organization API
|
||||||
// @Description get organization
|
// @Description get organization
|
||||||
// @Param id query string true "organization id"
|
// @Param id query string true "organization id"
|
||||||
// @Success 200 {object} object.Organization The Response object
|
// @Success 200 {object} object.Organization The Response object
|
||||||
@@ -42,12 +54,13 @@ func (c *ApiController) GetOrganizations() {
|
|||||||
func (c *ApiController) GetOrganization() {
|
func (c *ApiController) GetOrganization() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
c.Data["json"] = object.GetOrganization(id)
|
c.Data["json"] = object.GetMaskedOrganization(object.GetOrganization(id))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOrganization ...
|
// UpdateOrganization ...
|
||||||
// @Title UpdateOrganization
|
// @Title UpdateOrganization
|
||||||
|
// @Tag Organization API
|
||||||
// @Description update organization
|
// @Description update organization
|
||||||
// @Param id query string true "The id of the organization"
|
// @Param id query string true "The id of the organization"
|
||||||
// @Param body body object.Organization true "The details of the organization"
|
// @Param body body object.Organization true "The details of the organization"
|
||||||
@@ -68,6 +81,7 @@ func (c *ApiController) UpdateOrganization() {
|
|||||||
|
|
||||||
// AddOrganization ...
|
// AddOrganization ...
|
||||||
// @Title AddOrganization
|
// @Title AddOrganization
|
||||||
|
// @Tag Organization API
|
||||||
// @Description add organization
|
// @Description add organization
|
||||||
// @Param body body object.Organization true "The details of the organization"
|
// @Param body body object.Organization true "The details of the organization"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
@@ -85,6 +99,7 @@ func (c *ApiController) AddOrganization() {
|
|||||||
|
|
||||||
// DeleteOrganization ...
|
// DeleteOrganization ...
|
||||||
// @Title DeleteOrganization
|
// @Title DeleteOrganization
|
||||||
|
// @Tag Organization API
|
||||||
// @Description delete organization
|
// @Description delete organization
|
||||||
// @Param body body object.Organization true "The details of the organization"
|
// @Param body body object.Organization true "The details of the organization"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
@@ -1,45 +0,0 @@
|
|||||||
package controllers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/casbin/casdoor/object"
|
|
||||||
"github.com/casbin/casdoor/payment"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *ApiController) PaypalPay() {
|
|
||||||
clientId := c.Input().Get("clientId")
|
|
||||||
redirectUri := c.Input().Get("redirectUri")
|
|
||||||
var payItem object.PayItem
|
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &payItem)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := payment.Paypal(payItem, clientId, redirectUri)
|
|
||||||
|
|
||||||
c.Data["json"] = msg
|
|
||||||
c.ServeJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ApiController) GetPayments() {
|
|
||||||
c.Data["json"] = object.GetPayments()
|
|
||||||
c.ServeJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ApiController) DeletePayment() {
|
|
||||||
var payment object.Payment
|
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Data["json"] = wrapActionResponse(object.DeletePayment(&payment))
|
|
||||||
c.ServeJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ApiController) SuccessPay() {
|
|
||||||
token := c.Input().Get("paymentId")
|
|
||||||
c.Data["json"] = payment.SuccessPay(token)
|
|
||||||
c.ServeJSON()
|
|
||||||
}
|
|
@@ -17,23 +17,35 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/utils/pagination"
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetProviders
|
// GetProviders
|
||||||
// @Title GetProviders
|
// @Title GetProviders
|
||||||
|
// @Tag Provider API
|
||||||
// @Description get providers
|
// @Description get providers
|
||||||
// @Param owner query string true "The owner of providers"
|
// @Param owner query string true "The owner of providers"
|
||||||
// @Success 200 {array} object.Provider The Response object
|
// @Success 200 {array} object.Provider The Response object
|
||||||
// @router /get-providers [get]
|
// @router /get-providers [get]
|
||||||
func (c *ApiController) GetProviders() {
|
func (c *ApiController) GetProviders() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
|
limit := c.Input().Get("pageSize")
|
||||||
c.Data["json"] = object.GetProviders(owner)
|
page := c.Input().Get("p")
|
||||||
c.ServeJSON()
|
if limit == "" || page == "" {
|
||||||
|
c.Data["json"] = object.GetMaskedProviders(object.GetProviders(owner))
|
||||||
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetProviderCount(owner)))
|
||||||
|
providers := object.GetMaskedProviders(object.GetPaginationProviders(owner, paginator.Offset(), limit))
|
||||||
|
c.ResponseOk(providers, paginator.Nums())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title GetProvider
|
// @Title GetProvider
|
||||||
|
// @Tag Provider API
|
||||||
// @Description get provider
|
// @Description get provider
|
||||||
// @Param id query string true "The id of the provider"
|
// @Param id query string true "The id of the provider"
|
||||||
// @Success 200 {object} object.Provider The Response object
|
// @Success 200 {object} object.Provider The Response object
|
||||||
@@ -41,11 +53,12 @@ func (c *ApiController) GetProviders() {
|
|||||||
func (c *ApiController) GetProvider() {
|
func (c *ApiController) GetProvider() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
c.Data["json"] = object.GetProvider(id)
|
c.Data["json"] = object.GetMaskedProvider(object.GetProvider(id))
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Title UpdateProvider
|
// @Title UpdateProvider
|
||||||
|
// @Tag Provider API
|
||||||
// @Description update provider
|
// @Description update provider
|
||||||
// @Param id query string true "The id of the provider"
|
// @Param id query string true "The id of the provider"
|
||||||
// @Param body body object.Provider true "The details of the provider"
|
// @Param body body object.Provider true "The details of the provider"
|
||||||
@@ -65,6 +78,7 @@ func (c *ApiController) UpdateProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Title AddProvider
|
// @Title AddProvider
|
||||||
|
// @Tag Provider API
|
||||||
// @Description add provider
|
// @Description add provider
|
||||||
// @Param body body object.Provider true "The details of the provider"
|
// @Param body body object.Provider true "The details of the provider"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
@@ -81,6 +95,7 @@ func (c *ApiController) AddProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @Title DeleteProvider
|
// @Title DeleteProvider
|
||||||
|
// @Tag Provider API
|
||||||
// @Description delete provider
|
// @Description delete provider
|
||||||
// @Param body body object.Provider true "The details of the provider"
|
// @Param body body object.Provider true "The details of the provider"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
@@ -15,34 +15,49 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"github.com/astaxie/beego/utils/pagination"
|
||||||
|
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetRecords
|
// GetRecords
|
||||||
// @Title GetRecords
|
// @Title GetRecords
|
||||||
|
// @Tag Record API
|
||||||
// @Description get all records
|
// @Description get all records
|
||||||
|
// @Param pageSize query string true "The size of each page"
|
||||||
|
// @Param p query string true "The number of the page"
|
||||||
// @Success 200 {array} object.Records The Response object
|
// @Success 200 {array} object.Records The Response object
|
||||||
// @router /get-records [get]
|
// @router /get-records [get]
|
||||||
func (c *ApiController) GetRecords() {
|
func (c *ApiController) GetRecords() {
|
||||||
c.Data["json"] = object.GetRecords()
|
limit := c.Input().Get("pageSize")
|
||||||
c.ServeJSON()
|
page := c.Input().Get("p")
|
||||||
|
if limit == "" || page == "" {
|
||||||
|
c.Data["json"] = object.GetRecords()
|
||||||
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetRecordCount()))
|
||||||
|
records := object.GetPaginationRecords(paginator.Offset(), limit)
|
||||||
|
c.ResponseOk(records, paginator.Nums())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRecordsByFilter
|
// GetRecordsByFilter
|
||||||
|
// @Tag Record API
|
||||||
// @Title GetRecordsByFilter
|
// @Title GetRecordsByFilter
|
||||||
// @Description get records by filter
|
// @Description get records by filter
|
||||||
// @Param body body object.Records true "filter Record message"
|
// @Param body body object.Records true "filter Record message"
|
||||||
// @Success 200 {array} object.Records The Response object
|
// @Success 200 {array} object.Records The Response object
|
||||||
// @router /get-records-filter [post]
|
// @router /get-records-filter [post]
|
||||||
func (c *ApiController) GetRecordsByFilter() {
|
func (c *ApiController) GetRecordsByFilter() {
|
||||||
var record object.Records
|
body := string(c.Ctx.Input.RequestBody)
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &record)
|
|
||||||
|
record := &object.Record{}
|
||||||
|
err := util.JsonToStruct(body, record)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = object.GetRecordsByField(&record)
|
c.Data["json"] = object.GetRecordsByField(record)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
@@ -22,18 +22,33 @@ import (
|
|||||||
"mime"
|
"mime"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/utils/pagination"
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// @router /get-resources [get]
|
||||||
|
// @Tag Resource API
|
||||||
|
// @Title GetResources
|
||||||
func (c *ApiController) GetResources() {
|
func (c *ApiController) GetResources() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
user := c.Input().Get("user")
|
user := c.Input().Get("user")
|
||||||
|
limit := c.Input().Get("pageSize")
|
||||||
c.Data["json"] = object.GetResources(owner, user)
|
page := c.Input().Get("p")
|
||||||
c.ServeJSON()
|
if limit == "" || page == "" {
|
||||||
|
c.Data["json"] = object.GetResources(owner, user)
|
||||||
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetResourceCount(owner, user)))
|
||||||
|
resources := object.GetPaginationResources(owner, user, paginator.Offset(), limit)
|
||||||
|
c.ResponseOk(resources, paginator.Nums())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Resource API
|
||||||
|
// @Title GetResource
|
||||||
|
// @router /get-resource [get]
|
||||||
func (c *ApiController) GetResource() {
|
func (c *ApiController) GetResource() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
@@ -41,6 +56,9 @@ func (c *ApiController) GetResource() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Resource API
|
||||||
|
// @Title UpdateResource
|
||||||
|
// @router /update-resource [post]
|
||||||
func (c *ApiController) UpdateResource() {
|
func (c *ApiController) UpdateResource() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
@@ -54,6 +72,9 @@ func (c *ApiController) UpdateResource() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Resource API
|
||||||
|
// @Title AddResource
|
||||||
|
// @router /add-resource [post]
|
||||||
func (c *ApiController) AddResource() {
|
func (c *ApiController) AddResource() {
|
||||||
var resource object.Resource
|
var resource object.Resource
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &resource)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &resource)
|
||||||
@@ -65,6 +86,9 @@ func (c *ApiController) AddResource() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Resource API
|
||||||
|
// @Title DeleteResource
|
||||||
|
// @router /delete-resource [post]
|
||||||
func (c *ApiController) DeleteResource() {
|
func (c *ApiController) DeleteResource() {
|
||||||
var resource object.Resource
|
var resource object.Resource
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &resource)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &resource)
|
||||||
@@ -87,6 +111,9 @@ func (c *ApiController) DeleteResource() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Tag Resource API
|
||||||
|
// @Title UploadResource
|
||||||
|
// @router /upload-resource [post]
|
||||||
func (c *ApiController) UploadResource() {
|
func (c *ApiController) UploadResource() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
username := c.Input().Get("user")
|
username := c.Input().Get("user")
|
||||||
@@ -94,6 +121,8 @@ func (c *ApiController) UploadResource() {
|
|||||||
tag := c.Input().Get("tag")
|
tag := c.Input().Get("tag")
|
||||||
parent := c.Input().Get("parent")
|
parent := c.Input().Get("parent")
|
||||||
fullFilePath := c.Input().Get("fullFilePath")
|
fullFilePath := c.Input().Get("fullFilePath")
|
||||||
|
createdTime := c.Input().Get("createdTime")
|
||||||
|
description := c.Input().Get("description")
|
||||||
|
|
||||||
file, header, err := c.GetFile("file")
|
file, header, err := c.GetFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -102,6 +131,11 @@ func (c *ApiController) UploadResource() {
|
|||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
|
if username == "" || fullFilePath == "" {
|
||||||
|
c.ResponseError(fmt.Sprintf("username or fullFilePath is empty: username = %s, fullFilePath = %s", username, fullFilePath))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
filename := filepath.Base(fullFilePath)
|
filename := filepath.Base(fullFilePath)
|
||||||
fileBuffer := bytes.NewBuffer(nil)
|
fileBuffer := bytes.NewBuffer(nil)
|
||||||
if _, err = io.Copy(fileBuffer, file); err != nil {
|
if _, err = io.Copy(fileBuffer, file); err != nil {
|
||||||
@@ -124,18 +158,21 @@ func (c *ApiController) UploadResource() {
|
|||||||
fileType, _ = util.GetOwnerAndNameFromId(mimeType)
|
fileType, _ = util.GetOwnerAndNameFromId(mimeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileUrl, objectKey, err := object.UploadFile(provider, fullFilePath, fileBuffer)
|
fileUrl, objectKey, err := object.UploadFileSafe(provider, fullFilePath, fileBuffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if createdTime == "" {
|
||||||
|
createdTime = util.GetCurrentTime()
|
||||||
|
}
|
||||||
fileFormat := filepath.Ext(fullFilePath)
|
fileFormat := filepath.Ext(fullFilePath)
|
||||||
fileSize := int(header.Size)
|
fileSize := int(header.Size)
|
||||||
resource := &object.Resource{
|
resource := &object.Resource{
|
||||||
Owner: owner,
|
Owner: owner,
|
||||||
Name: objectKey,
|
Name: objectKey,
|
||||||
CreatedTime: util.GetCurrentTime(),
|
CreatedTime: createdTime,
|
||||||
User: username,
|
User: username,
|
||||||
Provider: provider.Name,
|
Provider: provider.Name,
|
||||||
Application: application,
|
Application: application,
|
||||||
@@ -146,18 +183,22 @@ func (c *ApiController) UploadResource() {
|
|||||||
FileFormat: fileFormat,
|
FileFormat: fileFormat,
|
||||||
FileSize: fileSize,
|
FileSize: fileSize,
|
||||||
Url: fileUrl,
|
Url: fileUrl,
|
||||||
|
Description: description,
|
||||||
}
|
}
|
||||||
object.AddOrUpdateResource(resource)
|
object.AddOrUpdateResource(resource)
|
||||||
|
|
||||||
switch tag {
|
switch tag {
|
||||||
case "avatar":
|
case "avatar":
|
||||||
if user == nil {
|
if user == nil {
|
||||||
c.ResponseError("user is nil for tag: \"avatar\"")
|
user = object.GetUserNoCheck(username)
|
||||||
return
|
if user == nil {
|
||||||
|
c.ResponseError("user is nil for tag: \"avatar\"")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Avatar = fileUrl
|
user.Avatar = fileUrl
|
||||||
object.UpdateUser(user.GetId(), user)
|
object.UpdateUser(user.GetId(), user, []string{"avatar"})
|
||||||
case "termsOfUse":
|
case "termsOfUse":
|
||||||
applicationId := fmt.Sprintf("admin/%s", parent)
|
applicationId := fmt.Sprintf("admin/%s", parent)
|
||||||
app := object.GetApplication(applicationId)
|
app := object.GetApplication(applicationId)
|
||||||
|
@@ -27,6 +27,7 @@ import (
|
|||||||
|
|
||||||
// SendEmail
|
// SendEmail
|
||||||
// @Title SendEmail
|
// @Title SendEmail
|
||||||
|
// @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 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"
|
||||||
@@ -81,6 +82,7 @@ func (c *ApiController) SendEmail() {
|
|||||||
|
|
||||||
// SendSms
|
// SendSms
|
||||||
// @Title SendSms
|
// @Title SendSms
|
||||||
|
// @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 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"
|
||||||
@@ -96,6 +98,7 @@ func (c *ApiController) SendSms() {
|
|||||||
var smsForm struct {
|
var smsForm struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Receivers []string `json:"receivers"`
|
Receivers []string `json:"receivers"`
|
||||||
|
OrgId string `json:"organizationId"` // e.g. "admin/built-in"
|
||||||
}
|
}
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &smsForm)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &smsForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -103,10 +106,13 @@ func (c *ApiController) SendSms() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
org := object.GetOrganization(smsForm.OrgId)
|
||||||
var invalidReceivers []string
|
var invalidReceivers []string
|
||||||
for _, receiver := range smsForm.Receivers {
|
for idx, receiver := range smsForm.Receivers {
|
||||||
if !util.IsPhoneCnValid(receiver) {
|
if !util.IsPhoneCnValid(receiver) {
|
||||||
invalidReceivers = append(invalidReceivers, receiver)
|
invalidReceivers = append(invalidReceivers, receiver)
|
||||||
|
} else {
|
||||||
|
smsForm.Receivers[idx] = fmt.Sprintf("+%s%s", org.PhonePrefix, receiver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
112
controllers/syncer.go
Normal file
112
controllers/syncer.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/utils/pagination"
|
||||||
|
"github.com/casbin/casdoor/object"
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetSyncers
|
||||||
|
// @Title GetSyncers
|
||||||
|
// @Tag Syncer API
|
||||||
|
// @Description get syncers
|
||||||
|
// @Param owner query string true "The owner of syncers"
|
||||||
|
// @Success 200 {array} object.Syncer The Response object
|
||||||
|
// @router /get-syncers [get]
|
||||||
|
func (c *ApiController) GetSyncers() {
|
||||||
|
owner := c.Input().Get("owner")
|
||||||
|
limit := c.Input().Get("pageSize")
|
||||||
|
page := c.Input().Get("p")
|
||||||
|
if limit == "" || page == "" {
|
||||||
|
c.Data["json"] = object.GetSyncers(owner)
|
||||||
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetSyncerCount(owner)))
|
||||||
|
syncers := object.GetPaginationSyncers(owner, paginator.Offset(), limit)
|
||||||
|
c.ResponseOk(syncers, paginator.Nums())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Title GetSyncer
|
||||||
|
// @Tag Syncer API
|
||||||
|
// @Description get syncer
|
||||||
|
// @Param id query string true "The id of the syncer"
|
||||||
|
// @Success 200 {object} object.Syncer The Response object
|
||||||
|
// @router /get-syncer [get]
|
||||||
|
func (c *ApiController) GetSyncer() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
|
c.Data["json"] = object.GetSyncer(id)
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Title UpdateSyncer
|
||||||
|
// @Tag Syncer API
|
||||||
|
// @Description update syncer
|
||||||
|
// @Param id query string true "The id of the syncer"
|
||||||
|
// @Param body body object.Syncer true "The details of the syncer"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /update-syncer [post]
|
||||||
|
func (c *ApiController) UpdateSyncer() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
|
var syncer object.Syncer
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &syncer)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.UpdateSyncer(id, &syncer))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Title AddSyncer
|
||||||
|
// @Tag Syncer API
|
||||||
|
// @Description add syncer
|
||||||
|
// @Param body body object.Syncer true "The details of the syncer"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /add-syncer [post]
|
||||||
|
func (c *ApiController) AddSyncer() {
|
||||||
|
var syncer object.Syncer
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &syncer)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.AddSyncer(&syncer))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Title DeleteSyncer
|
||||||
|
// @Tag Syncer API
|
||||||
|
// @Description delete syncer
|
||||||
|
// @Param body body object.Syncer true "The details of the syncer"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /delete-syncer [post]
|
||||||
|
func (c *ApiController) DeleteSyncer() {
|
||||||
|
var syncer object.Syncer
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &syncer)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.DeleteSyncer(&syncer))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
@@ -17,24 +17,38 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/utils/pagination"
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetTokens
|
// GetTokens
|
||||||
// @Title GetTokens
|
// @Title GetTokens
|
||||||
|
// @Tag Token API
|
||||||
// @Description get tokens
|
// @Description get tokens
|
||||||
// @Param owner query string true "The owner of tokens"
|
// @Param owner query string true "The owner of tokens"
|
||||||
|
// @Param pageSize query string true "The size of each page"
|
||||||
|
// @Param p query string true "The number of the page"
|
||||||
// @Success 200 {array} object.Token The Response object
|
// @Success 200 {array} object.Token The Response object
|
||||||
// @router /get-tokens [get]
|
// @router /get-tokens [get]
|
||||||
func (c *ApiController) GetTokens() {
|
func (c *ApiController) GetTokens() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
|
limit := c.Input().Get("pageSize")
|
||||||
c.Data["json"] = object.GetTokens(owner)
|
page := c.Input().Get("p")
|
||||||
c.ServeJSON()
|
if limit == "" || page == "" {
|
||||||
|
c.Data["json"] = object.GetTokens(owner)
|
||||||
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetTokenCount(owner)))
|
||||||
|
tokens := object.GetPaginationTokens(owner, paginator.Offset(), limit)
|
||||||
|
c.ResponseOk(tokens, paginator.Nums())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetToken
|
// GetToken
|
||||||
// @Title GetToken
|
// @Title GetToken
|
||||||
|
// @Tag Token API
|
||||||
// @Description get token
|
// @Description get token
|
||||||
// @Param id query string true "The id of token"
|
// @Param id query string true "The id of token"
|
||||||
// @Success 200 {object} object.Token The Response object
|
// @Success 200 {object} object.Token The Response object
|
||||||
@@ -48,6 +62,7 @@ func (c *ApiController) GetToken() {
|
|||||||
|
|
||||||
// UpdateToken
|
// UpdateToken
|
||||||
// @Title UpdateToken
|
// @Title UpdateToken
|
||||||
|
// @Tag Token API
|
||||||
// @Description update token
|
// @Description update token
|
||||||
// @Param id query string true "The id of token"
|
// @Param id query string true "The id of token"
|
||||||
// @Param body body object.Token true "Details of the token"
|
// @Param body body object.Token true "Details of the token"
|
||||||
@@ -68,6 +83,7 @@ func (c *ApiController) UpdateToken() {
|
|||||||
|
|
||||||
// AddToken
|
// AddToken
|
||||||
// @Title AddToken
|
// @Title AddToken
|
||||||
|
// @Tag Token API
|
||||||
// @Description add token
|
// @Description add token
|
||||||
// @Param body body object.Token true "Details of the token"
|
// @Param body body object.Token true "Details of the token"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
@@ -84,6 +100,7 @@ func (c *ApiController) AddToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DeleteToken
|
// DeleteToken
|
||||||
|
// @Tag Token API
|
||||||
// @Title DeleteToken
|
// @Title DeleteToken
|
||||||
// @Description delete token
|
// @Description delete token
|
||||||
// @Param body body object.Token true "Details of the token"
|
// @Param body body object.Token true "Details of the token"
|
||||||
@@ -100,13 +117,39 @@ func (c *ApiController) DeleteToken() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOAuthCode
|
||||||
|
// @Title GetOAuthCode
|
||||||
|
// @Tag Token API
|
||||||
|
// @Description get OAuth code
|
||||||
|
// @Param user_id query string true "The id of user"
|
||||||
|
// @Param client_id query string true "OAuth client id"
|
||||||
|
// @Param response_type query string true "OAuth response type"
|
||||||
|
// @Param redirect_uri query string true "OAuth redirect URI"
|
||||||
|
// @Param scope query string true "OAuth scope"
|
||||||
|
// @Param state query string true "OAuth state"
|
||||||
|
// @Success 200 {object} object.TokenWrapper The Response object
|
||||||
|
// @router /login/oauth/code [post]
|
||||||
|
func (c *ApiController) GetOAuthCode() {
|
||||||
|
userId := c.Input().Get("user_id")
|
||||||
|
clientId := c.Input().Get("client_id")
|
||||||
|
responseType := c.Input().Get("response_type")
|
||||||
|
redirectUri := c.Input().Get("redirect_uri")
|
||||||
|
scope := c.Input().Get("scope")
|
||||||
|
state := c.Input().Get("state")
|
||||||
|
nonce := c.Input().Get("nonce")
|
||||||
|
|
||||||
|
c.Data["json"] = object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce)
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
// GetOAuthToken
|
// GetOAuthToken
|
||||||
// @Title GetOAuthToken
|
// @Title GetOAuthToken
|
||||||
// @Description get oAuth token
|
// @Tag Token API
|
||||||
// @Param grant_type query string true "oAuth grant type"
|
// @Description get OAuth access token
|
||||||
// @Param client_id query string true "oAuth client id"
|
// @Param grant_type query string true "OAuth grant type"
|
||||||
// @Param client_secret query string true "oAuth client secret"
|
// @Param client_id query string true "OAuth client id"
|
||||||
// @Param code query string true "oAuth code"
|
// @Param client_secret query string true "OAuth client secret"
|
||||||
|
// @Param code query string true "OAuth code"
|
||||||
// @Success 200 {object} object.TokenWrapper The Response object
|
// @Success 200 {object} object.TokenWrapper The Response object
|
||||||
// @router /login/oauth/access_token [post]
|
// @router /login/oauth/access_token [post]
|
||||||
func (c *ApiController) GetOAuthToken() {
|
func (c *ApiController) GetOAuthToken() {
|
||||||
@@ -115,6 +158,31 @@ func (c *ApiController) GetOAuthToken() {
|
|||||||
clientSecret := c.Input().Get("client_secret")
|
clientSecret := c.Input().Get("client_secret")
|
||||||
code := c.Input().Get("code")
|
code := c.Input().Get("code")
|
||||||
|
|
||||||
|
if clientId == "" && clientSecret == "" {
|
||||||
|
clientId, clientSecret, _ = c.Ctx.Request.BasicAuth()
|
||||||
|
}
|
||||||
|
|
||||||
c.Data["json"] = object.GetOAuthToken(grantType, clientId, clientSecret, code)
|
c.Data["json"] = object.GetOAuthToken(grantType, clientId, clientSecret, code)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshToken
|
||||||
|
// @Title RefreshToken
|
||||||
|
// @Description refresh OAuth access token
|
||||||
|
// @Param grant_type query string true "OAuth grant type"
|
||||||
|
// @Param refresh_token query string true "OAuth refresh token"
|
||||||
|
// @Param scope query string true "OAuth scope"
|
||||||
|
// @Param client_id query string true "OAuth client id"
|
||||||
|
// @Param client_secret query string true "OAuth client secret"
|
||||||
|
// @Success 200 {object} object.TokenWrapper The Response object
|
||||||
|
// @router /login/oauth/refresh_token [post]
|
||||||
|
func (c *ApiController) RefreshToken() {
|
||||||
|
grantType := c.Input().Get("grant_type")
|
||||||
|
refreshToken := c.Input().Get("refresh_token")
|
||||||
|
scope := c.Input().Get("scope")
|
||||||
|
clientId := c.Input().Get("client_id")
|
||||||
|
clientSecret := c.Input().Get("client_secret")
|
||||||
|
|
||||||
|
c.Data["json"] = object.RefreshToken(grantType, refreshToken, scope, clientId, clientSecret)
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
@@ -19,48 +19,79 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/utils/pagination"
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
"github.com/casbin/casdoor/original"
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetGlobalUsers
|
// GetGlobalUsers
|
||||||
// @Title GetGlobalUsers
|
// @Title GetGlobalUsers
|
||||||
|
// @Tag User API
|
||||||
// @Description get global users
|
// @Description get global users
|
||||||
// @Success 200 {array} object.User The Response object
|
// @Success 200 {array} object.User The Response object
|
||||||
// @router /get-global-users [get]
|
// @router /get-global-users [get]
|
||||||
func (c *ApiController) GetGlobalUsers() {
|
func (c *ApiController) GetGlobalUsers() {
|
||||||
c.Data["json"] = object.GetMaskedUsers(object.GetGlobalUsers())
|
limit := c.Input().Get("pageSize")
|
||||||
c.ServeJSON()
|
page := c.Input().Get("p")
|
||||||
|
if limit == "" || page == "" {
|
||||||
|
c.Data["json"] = object.GetMaskedUsers(object.GetGlobalUsers())
|
||||||
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetGlobalUserCount()))
|
||||||
|
users := object.GetPaginationGlobalUsers(paginator.Offset(), limit)
|
||||||
|
c.ResponseOk(users, paginator.Nums())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUsers
|
// GetUsers
|
||||||
// @Title GetUsers
|
// @Title GetUsers
|
||||||
|
// @Tag User API
|
||||||
// @Description
|
// @Description
|
||||||
// @Param owner query string true "The owner of users"
|
// @Param owner query string true "The owner of users"
|
||||||
// @Success 200 {array} object.User The Response object
|
// @Success 200 {array} object.User The Response object
|
||||||
// @router /get-users [get]
|
// @router /get-users [get]
|
||||||
func (c *ApiController) GetUsers() {
|
func (c *ApiController) GetUsers() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
|
limit := c.Input().Get("pageSize")
|
||||||
c.Data["json"] = object.GetMaskedUsers(object.GetUsers(owner))
|
page := c.Input().Get("p")
|
||||||
c.ServeJSON()
|
if limit == "" || page == "" {
|
||||||
|
c.Data["json"] = object.GetMaskedUsers(object.GetUsers(owner))
|
||||||
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetUserCount(owner)))
|
||||||
|
users := object.GetPaginationUsers(owner, paginator.Offset(), limit)
|
||||||
|
c.ResponseOk(users, paginator.Nums())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUser
|
// GetUser
|
||||||
// @Title GetUser
|
// @Title GetUser
|
||||||
|
// @Tag User API
|
||||||
// @Description get user
|
// @Description get user
|
||||||
// @Param id query string true "The id of the user"
|
// @Param id query string true "The id of the user"
|
||||||
// @Success 200 {object} object.User The Response object
|
// @Success 200 {object} object.User The Response object
|
||||||
// @router /get-user [get]
|
// @router /get-user [get]
|
||||||
func (c *ApiController) GetUser() {
|
func (c *ApiController) GetUser() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
owner := c.Input().Get("owner")
|
||||||
|
email := c.Input().Get("email")
|
||||||
|
|
||||||
c.Data["json"] = object.GetMaskedUser(object.GetUser(id))
|
var user *object.User
|
||||||
|
if email == "" {
|
||||||
|
user = object.GetUser(id)
|
||||||
|
} else {
|
||||||
|
user = object.GetUserByEmail(owner, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = object.GetMaskedUser(user)
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUser
|
// UpdateUser
|
||||||
// @Title UpdateUser
|
// @Title UpdateUser
|
||||||
|
// @Tag User API
|
||||||
// @Description update user
|
// @Description update user
|
||||||
// @Param id query string true "The id of the user"
|
// @Param id query string true "The id of the user"
|
||||||
// @Param body body object.User true "The details of the user"
|
// @Param body body object.User true "The details of the user"
|
||||||
@@ -68,6 +99,7 @@ func (c *ApiController) GetUser() {
|
|||||||
// @router /update-user [post]
|
// @router /update-user [post]
|
||||||
func (c *ApiController) UpdateUser() {
|
func (c *ApiController) UpdateUser() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
columnsStr := c.Input().Get("columns")
|
||||||
|
|
||||||
var user object.User
|
var user object.User
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
||||||
@@ -80,10 +112,14 @@ func (c *ApiController) UpdateUser() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
affected := object.UpdateUser(id, &user)
|
columns := []string{}
|
||||||
|
if columnsStr != "" {
|
||||||
|
columns = strings.Split(columnsStr, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
affected := object.UpdateUser(id, &user, columns)
|
||||||
if affected {
|
if affected {
|
||||||
newUser := object.GetUser(user.GetId())
|
object.UpdateUserToOriginalDatabase(&user)
|
||||||
original.UpdateUserToOriginalDatabase(newUser)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = wrapActionResponse(affected)
|
c.Data["json"] = wrapActionResponse(affected)
|
||||||
@@ -92,6 +128,7 @@ func (c *ApiController) UpdateUser() {
|
|||||||
|
|
||||||
// AddUser
|
// AddUser
|
||||||
// @Title AddUser
|
// @Title AddUser
|
||||||
|
// @Tag User API
|
||||||
// @Description add user
|
// @Description add user
|
||||||
// @Param body body object.User true "The details of the user"
|
// @Param body body object.User true "The details of the user"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
@@ -109,6 +146,7 @@ func (c *ApiController) AddUser() {
|
|||||||
|
|
||||||
// DeleteUser
|
// DeleteUser
|
||||||
// @Title DeleteUser
|
// @Title DeleteUser
|
||||||
|
// @Tag User API
|
||||||
// @Description delete user
|
// @Description delete user
|
||||||
// @Param body body object.User true "The details of the user"
|
// @Param body body object.User true "The details of the user"
|
||||||
// @Success 200 {object} controllers.Response The Response object
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
@@ -126,6 +164,7 @@ func (c *ApiController) DeleteUser() {
|
|||||||
|
|
||||||
// GetEmailAndPhone
|
// GetEmailAndPhone
|
||||||
// @Title GetEmailAndPhone
|
// @Title GetEmailAndPhone
|
||||||
|
// @Tag User API
|
||||||
// @Description get email and phone by username
|
// @Description get email and phone by username
|
||||||
// @Param username formData string true "The username of the user"
|
// @Param username formData string true "The username of the user"
|
||||||
// @Param organization formData string true "The organization of the user"
|
// @Param organization formData string true "The organization of the user"
|
||||||
@@ -160,6 +199,7 @@ func (c *ApiController) GetEmailAndPhone() {
|
|||||||
|
|
||||||
// SetPassword
|
// SetPassword
|
||||||
// @Title SetPassword
|
// @Title SetPassword
|
||||||
|
// @Tag Account API
|
||||||
// @Description set password
|
// @Description set password
|
||||||
// @Param userOwner formData string true "The owner of the user"
|
// @Param userOwner formData string true "The owner of the user"
|
||||||
// @Param userName formData string true "The name of the user"
|
// @Param userName formData string true "The name of the user"
|
||||||
@@ -187,7 +227,7 @@ func (c *ApiController) SetPassword() {
|
|||||||
userId := fmt.Sprintf("%s/%s", userOwner, userName)
|
userId := fmt.Sprintf("%s/%s", userOwner, userName)
|
||||||
targetUser := object.GetUser(userId)
|
targetUser := object.GetUser(userId)
|
||||||
if targetUser == nil {
|
if targetUser == nil {
|
||||||
c.ResponseError("Invalid user id.")
|
c.ResponseError(fmt.Sprintf("The user: %s doesn't exist", userId))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,6 +272,9 @@ func (c *ApiController) SetPassword() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Title CheckUserPassword
|
||||||
|
// @router /check-user-password [post]
|
||||||
|
// @Tag User API
|
||||||
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)
|
||||||
@@ -246,3 +289,44 @@ func (c *ApiController) CheckUserPassword() {
|
|||||||
c.ResponseError(msg)
|
c.ResponseError(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSortedUsers
|
||||||
|
// @Title GetSortedUsers
|
||||||
|
// @Tag User API
|
||||||
|
// @Description
|
||||||
|
// @Param owner query string true "The owner of users"
|
||||||
|
// @Param sorter query string true "The DB column name to sort by, e.g., created_time"
|
||||||
|
// @Param limit query string true "The count of users to return, e.g., 25"
|
||||||
|
// @Success 200 {array} object.User The Response object
|
||||||
|
// @router /get-sorted-users [get]
|
||||||
|
func (c *ApiController) GetSortedUsers() {
|
||||||
|
owner := c.Input().Get("owner")
|
||||||
|
sorter := c.Input().Get("sorter")
|
||||||
|
limit := util.ParseInt(c.Input().Get("limit"))
|
||||||
|
|
||||||
|
c.Data["json"] = object.GetMaskedUsers(object.GetSortedUsers(owner, sorter, limit))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserCount
|
||||||
|
// @Title GetUserCount
|
||||||
|
// @Tag User API
|
||||||
|
// @Description
|
||||||
|
// @Param owner query string true "The owner of users"
|
||||||
|
// @Param isOnline query string true "The filter for query, 1 for online, 0 for offline, empty string for all users"
|
||||||
|
// @Success 200 {int} int The count of filtered users for an organization
|
||||||
|
// @router /get-user-count [get]
|
||||||
|
func (c *ApiController) GetUserCount() {
|
||||||
|
owner := c.Input().Get("owner")
|
||||||
|
isOnline := c.Input().Get("isOnline")
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
if isOnline == "" {
|
||||||
|
count = object.GetUserCount(owner)
|
||||||
|
} else {
|
||||||
|
count = object.GetOnlineUserCount(owner, util.ParseInt(isOnline))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = count
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
@@ -35,6 +35,9 @@ func (c *ApiController) getCurrentUser() *object.User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SendVerificationCode ...
|
// SendVerificationCode ...
|
||||||
|
// @Title SendVerificationCode
|
||||||
|
// @Tag Verification API
|
||||||
|
// @router /send-verification-code [post]
|
||||||
func (c *ApiController) SendVerificationCode() {
|
func (c *ApiController) SendVerificationCode() {
|
||||||
destType := c.Ctx.Request.Form.Get("type")
|
destType := c.Ctx.Request.Form.Get("type")
|
||||||
dest := c.Ctx.Request.Form.Get("dest")
|
dest := c.Ctx.Request.Form.Get("dest")
|
||||||
@@ -42,6 +45,7 @@ func (c *ApiController) SendVerificationCode() {
|
|||||||
checkType := c.Ctx.Request.Form.Get("checkType")
|
checkType := c.Ctx.Request.Form.Get("checkType")
|
||||||
checkId := c.Ctx.Request.Form.Get("checkId")
|
checkId := c.Ctx.Request.Form.Get("checkId")
|
||||||
checkKey := c.Ctx.Request.Form.Get("checkKey")
|
checkKey := c.Ctx.Request.Form.Get("checkKey")
|
||||||
|
checkUser := c.Ctx.Request.Form.Get("checkUser")
|
||||||
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
|
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
|
||||||
|
|
||||||
if len(destType) == 0 || len(dest) == 0 || len(orgId) == 0 || !strings.Contains(orgId, "/") || len(checkType) == 0 || len(checkId) == 0 || len(checkKey) == 0 {
|
if len(destType) == 0 || len(dest) == 0 || len(orgId) == 0 || !strings.Contains(orgId, "/") || len(checkType) == 0 || len(checkId) == 0 || len(checkKey) == 0 {
|
||||||
@@ -63,7 +67,13 @@ func (c *ApiController) SendVerificationCode() {
|
|||||||
user := c.getCurrentUser()
|
user := c.getCurrentUser()
|
||||||
organization := object.GetOrganization(orgId)
|
organization := object.GetOrganization(orgId)
|
||||||
application := object.GetApplicationByOrganizationName(organization.Name)
|
application := object.GetApplicationByOrganizationName(organization.Name)
|
||||||
|
|
||||||
|
if checkUser == "true" && user == nil &&
|
||||||
|
object.GetUserByFields(organization.Name, dest) == nil {
|
||||||
|
c.ResponseError("No such user.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
sendResp := errors.New("Invalid dest type.")
|
sendResp := errors.New("Invalid dest type.")
|
||||||
switch destType {
|
switch destType {
|
||||||
case "email":
|
case "email":
|
||||||
@@ -90,16 +100,19 @@ func (c *ApiController) SendVerificationCode() {
|
|||||||
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, remoteAddr, dest)
|
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, remoteAddr, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
status := "ok"
|
|
||||||
if sendResp != nil {
|
if sendResp != nil {
|
||||||
status = "error"
|
c.Data["json"] = Response{Status: "error", Msg: sendResp.Error()}
|
||||||
|
} else {
|
||||||
|
c.Data["json"] = Response{Status: "ok"}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = Response{Status: status, Msg: sendResp.Error()}
|
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetEmailOrPhone ...
|
// ResetEmailOrPhone ...
|
||||||
|
// @Tag Account API
|
||||||
|
// @Title ResetEmailOrPhone
|
||||||
|
// @router /api/reset-email-or-phone [post]
|
||||||
func (c *ApiController) ResetEmailOrPhone() {
|
func (c *ApiController) ResetEmailOrPhone() {
|
||||||
userId, ok := c.RequireSignedIn()
|
userId, ok := c.RequireSignedIn()
|
||||||
if !ok {
|
if !ok {
|
||||||
|
112
controllers/webhook.go
Normal file
112
controllers/webhook.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/utils/pagination"
|
||||||
|
"github.com/casbin/casdoor/object"
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetWebhooks
|
||||||
|
// @Title GetWebhooks
|
||||||
|
// @Tag Webhook API
|
||||||
|
// @Description get webhooks
|
||||||
|
// @Param owner query string true "The owner of webhooks"
|
||||||
|
// @Success 200 {array} object.Webhook The Response object
|
||||||
|
// @router /get-webhooks [get]
|
||||||
|
func (c *ApiController) GetWebhooks() {
|
||||||
|
owner := c.Input().Get("owner")
|
||||||
|
limit := c.Input().Get("pageSize")
|
||||||
|
page := c.Input().Get("p")
|
||||||
|
if limit == "" || page == "" {
|
||||||
|
c.Data["json"] = object.GetWebhooks(owner)
|
||||||
|
c.ServeJSON()
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetWebhookCount(owner)))
|
||||||
|
webhooks := object.GetPaginationWebhooks(owner, paginator.Offset(), limit)
|
||||||
|
c.ResponseOk(webhooks, paginator.Nums())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Title GetWebhook
|
||||||
|
// @Tag Webhook API
|
||||||
|
// @Description get webhook
|
||||||
|
// @Param id query string true "The id of the webhook"
|
||||||
|
// @Success 200 {object} object.Webhook The Response object
|
||||||
|
// @router /get-webhook [get]
|
||||||
|
func (c *ApiController) GetWebhook() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
|
c.Data["json"] = object.GetWebhook(id)
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Title UpdateWebhook
|
||||||
|
// @Tag Webhook API
|
||||||
|
// @Description update webhook
|
||||||
|
// @Param id query string true "The id of the webhook"
|
||||||
|
// @Param body body object.Webhook true "The details of the webhook"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /update-webhook [post]
|
||||||
|
func (c *ApiController) UpdateWebhook() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
|
var webhook object.Webhook
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &webhook)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.UpdateWebhook(id, &webhook))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Title AddWebhook
|
||||||
|
// @Tag Webhook API
|
||||||
|
// @Description add webhook
|
||||||
|
// @Param body body object.Webhook true "The details of the webhook"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /add-webhook [post]
|
||||||
|
func (c *ApiController) AddWebhook() {
|
||||||
|
var webhook object.Webhook
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &webhook)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.AddWebhook(&webhook))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Title DeleteWebhook
|
||||||
|
// @Tag Webhook API
|
||||||
|
// @Description delete webhook
|
||||||
|
// @Param body body object.Webhook true "The details of the webhook"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /delete-webhook [post]
|
||||||
|
func (c *ApiController) DeleteWebhook() {
|
||||||
|
var webhook object.Webhook
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &webhook)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.DeleteWebhook(&webhook))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
23
cred/bcrypt.go
Normal file
23
cred/bcrypt.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package cred
|
||||||
|
|
||||||
|
import "golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
|
type BcryptCredManager struct{}
|
||||||
|
|
||||||
|
func NewBcryptCredManager() *BcryptCredManager {
|
||||||
|
cm := &BcryptCredManager{}
|
||||||
|
return cm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *BcryptCredManager) GetHashedPassword(password string, userSalt string, organizationSalt string) string {
|
||||||
|
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *BcryptCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
|
||||||
|
err := bcrypt.CompareHashAndPassword([]byte(hashedPwd), []byte(plainPwd))
|
||||||
|
return err == nil
|
||||||
|
}
|
33
cred/manager.go
Normal file
33
cred/manager.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cred
|
||||||
|
|
||||||
|
type CredManager interface {
|
||||||
|
GetHashedPassword(password string, userSalt string, organizationSalt string) string
|
||||||
|
IsPasswordCorrect(password string, passwordHash string, userSalt string, organizationSalt string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCredManager(passwordType string) CredManager {
|
||||||
|
if passwordType == "plain" {
|
||||||
|
return NewPlainCredManager()
|
||||||
|
} else if passwordType == "salt" {
|
||||||
|
return NewSha256SaltCredManager()
|
||||||
|
} else if passwordType == "md5-salt" {
|
||||||
|
return NewMd5UserSaltCredManager()
|
||||||
|
} else if passwordType == "bcrypt" {
|
||||||
|
return NewBcryptCredManager()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
48
cred/md5-user-salt.go
Normal file
48
cred/md5-user-salt.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cred
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Md5UserSaltCredManager struct{}
|
||||||
|
|
||||||
|
func getMd5(data []byte) []byte {
|
||||||
|
hash := md5.Sum(data)
|
||||||
|
return hash[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMd5HexDigest(s string) string {
|
||||||
|
b := getMd5([]byte(s))
|
||||||
|
res := hex.EncodeToString(b)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMd5UserSaltCredManager() *Sha256SaltCredManager {
|
||||||
|
cm := &Sha256SaltCredManager{}
|
||||||
|
return cm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *Md5UserSaltCredManager) GetHashedPassword(password string, userSalt string, organizationSalt string) string {
|
||||||
|
hash := getMd5HexDigest(password)
|
||||||
|
res := getMd5HexDigest(hash + userSalt)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *Md5UserSaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
|
||||||
|
return hashedPwd == cm.GetHashedPassword(plainPwd, userSalt, organizationSalt)
|
||||||
|
}
|
30
cred/plain.go
Normal file
30
cred/plain.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cred
|
||||||
|
|
||||||
|
type PlainCredManager struct{}
|
||||||
|
|
||||||
|
func NewPlainCredManager() *PlainCredManager {
|
||||||
|
cm := &PlainCredManager{}
|
||||||
|
return cm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *PlainCredManager) GetHashedPassword(password string, userSalt string, organizationSalt string) string {
|
||||||
|
return password
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *PlainCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
|
||||||
|
return hashedPwd == plainPwd
|
||||||
|
}
|
48
cred/sha256-salt.go
Normal file
48
cred/sha256-salt.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cred
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Sha256SaltCredManager struct{}
|
||||||
|
|
||||||
|
func getSha256(data []byte) []byte {
|
||||||
|
hash := sha256.Sum256(data)
|
||||||
|
return hash[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSha256HexDigest(s string) string {
|
||||||
|
b := getSha256([]byte(s))
|
||||||
|
res := hex.EncodeToString(b)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSha256SaltCredManager() *Sha256SaltCredManager {
|
||||||
|
cm := &Sha256SaltCredManager{}
|
||||||
|
return cm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *Sha256SaltCredManager) GetHashedPassword(password string, userSalt string, organizationSalt string) string {
|
||||||
|
hash := getSha256HexDigest(password)
|
||||||
|
res := getSha256HexDigest(hash + organizationSalt)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *Sha256SaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
|
||||||
|
return hashedPwd == cm.GetHashedPassword(plainPwd, userSalt, organizationSalt)
|
||||||
|
}
|
@@ -12,11 +12,16 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package original
|
package cred
|
||||||
|
|
||||||
var dbName = "dbName"
|
import (
|
||||||
var userTableName = "userTableName"
|
"fmt"
|
||||||
var affiliationTableName = "affiliationTableName"
|
"testing"
|
||||||
var avatarBaseUrl = "https://cdn.example.com/"
|
)
|
||||||
|
|
||||||
var orgName = "orgName"
|
func TestGetSaltedPassword(t *testing.T) {
|
||||||
|
password := "123456"
|
||||||
|
salt := "123"
|
||||||
|
cm := NewSha256SaltCredManager()
|
||||||
|
fmt.Printf("%s -> %s\n", password, cm.GetHashedPassword(password, "", salt))
|
||||||
|
}
|
@@ -8,6 +8,8 @@ services:
|
|||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
environment:
|
||||||
|
RUNNING_IN_DOCKER: "true"
|
||||||
volumes:
|
volumes:
|
||||||
- ./conf:/conf/
|
- ./conf:/conf/
|
||||||
db:
|
db:
|
||||||
@@ -16,6 +18,6 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "3306:3306"
|
- "3306:3306"
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: 123
|
MYSQL_ROOT_PASSWORD: 123456
|
||||||
volumes:
|
volumes:
|
||||||
- /usr/local/docker/mysql:/var/lib/mysql
|
- /usr/local/docker/mysql:/var/lib/mysql
|
||||||
|
16
go.mod
16
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/casbin/casdoor
|
module github.com/casbin/casdoor
|
||||||
|
|
||||||
go 1.15
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible // indirect
|
github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible // indirect
|
||||||
@@ -9,27 +9,31 @@ require (
|
|||||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
|
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
|
||||||
github.com/casbin/casbin/v2 v2.30.1
|
github.com/casbin/casbin/v2 v2.30.1
|
||||||
github.com/casbin/xorm-adapter/v2 v2.3.1
|
github.com/casbin/xorm-adapter/v2 v2.3.1
|
||||||
github.com/casdoor/go-sms-sender v0.0.4
|
github.com/casdoor/go-sms-sender v0.0.5
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
|
||||||
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
|
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
|
||||||
github.com/go-ldap/ldap/v3 v3.3.0
|
github.com/go-ldap/ldap/v3 v3.3.0
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.1.0
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
github.com/jinzhu/configor v1.2.1 // indirect
|
github.com/jinzhu/configor v1.2.1 // indirect
|
||||||
|
github.com/markbates/goth v1.68.1-0.20211006204042-9dc8905b41c8
|
||||||
github.com/mileusna/crontab v1.0.1
|
github.com/mileusna/crontab v1.0.1
|
||||||
github.com/plutov/paypal/v4 v4.3.7
|
|
||||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
||||||
github.com/qor/oss v0.0.0-20191031055114-aef9ba66bf76
|
github.com/qor/oss v0.0.0-20191031055114-aef9ba66bf76
|
||||||
|
github.com/russellhaering/gosaml2 v0.6.0
|
||||||
|
github.com/russellhaering/goxmldsig v1.1.1
|
||||||
github.com/satori/go.uuid v1.2.0 // indirect
|
github.com/satori/go.uuid v1.2.0 // indirect
|
||||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||||
github.com/thanhpk/randstr v1.0.4
|
github.com/thanhpk/randstr v1.0.4
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
|
||||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
||||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
xorm.io/core v0.7.2
|
xorm.io/core v0.7.2
|
||||||
xorm.io/xorm v1.0.6
|
xorm.io/xorm v1.0.3
|
||||||
)
|
)
|
||||||
|
100
go.sum
100
go.sum
@@ -13,6 +13,8 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
|
|||||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
|
cloud.google.com/go v0.67.0 h1:YIkzmqUfVGiGPpT98L8sVvUIkDno6UlrDxw4NR6z5ak=
|
||||||
|
cloud.google.com/go v0.67.0/go.mod h1:YNan/mUhNZFrYUor0vqrsQ0Ffl7Xtm/ACOy/vsTS858=
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
@@ -62,6 +64,8 @@ github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7
|
|||||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||||
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
|
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
|
||||||
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
|
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
|
||||||
|
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
|
||||||
|
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
@@ -75,8 +79,8 @@ github.com/casbin/casbin/v2 v2.30.1 h1:P5HWadDL7olwUXNdcuKUBk+x75Y2eitFxYTcLNKeK
|
|||||||
github.com/casbin/casbin/v2 v2.30.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
github.com/casbin/casbin/v2 v2.30.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
||||||
github.com/casbin/xorm-adapter/v2 v2.3.1 h1:RVGsM6KYFP9s4OQJXrP/gv56Wmt5P40mzvcyXgv5xeg=
|
github.com/casbin/xorm-adapter/v2 v2.3.1 h1:RVGsM6KYFP9s4OQJXrP/gv56Wmt5P40mzvcyXgv5xeg=
|
||||||
github.com/casbin/xorm-adapter/v2 v2.3.1/go.mod h1:GZ+nlIdasVFunQ71SlvkL/HcQQBvFncphDf+2Yl167c=
|
github.com/casbin/xorm-adapter/v2 v2.3.1/go.mod h1:GZ+nlIdasVFunQ71SlvkL/HcQQBvFncphDf+2Yl167c=
|
||||||
github.com/casdoor/go-sms-sender v0.0.4 h1:UekC70YueeA5E2LrKJVQKCGntdTlYwal/7og4vao66U=
|
github.com/casdoor/go-sms-sender v0.0.5 h1:9qhlMM+UoSOvvY7puUULqSHBBA7fbe02Px/tzchQboo=
|
||||||
github.com/casdoor/go-sms-sender v0.0.4/go.mod h1:TMM/BsZQAa+7JVDXl2KqgxnzZgCjmHEX5MBN662mM5M=
|
github.com/casdoor/go-sms-sender v0.0.5/go.mod h1:TMM/BsZQAa+7JVDXl2KqgxnzZgCjmHEX5MBN662mM5M=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
@@ -89,6 +93,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
|||||||
github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
|
github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
|
||||||
github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
|
github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
|
||||||
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
|
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
|
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -96,8 +101,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
|
||||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||||
github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
|
github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
|
||||||
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
|
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
|
||||||
@@ -106,7 +109,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
|
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||||
@@ -129,6 +131,10 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
|
|||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
|
||||||
|
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
|
||||||
|
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
@@ -170,8 +176,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
@@ -182,6 +189,7 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
|||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
@@ -190,6 +198,14 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
|
|||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||||
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
|
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
|
||||||
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
|
||||||
|
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
|
||||||
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
|
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||||
@@ -197,6 +213,7 @@ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
|
|||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
|
||||||
github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko=
|
github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko=
|
||||||
github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
|
github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
@@ -204,6 +221,9 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
|
|||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
|
github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||||
|
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||||
|
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||||
@@ -218,14 +238,26 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
|||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
|
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
|
||||||
|
github.com/lestrrat-go/jwx v0.9.0 h1:Fnd0EWzTm0kFrBPzE/PEPp9nzllES5buMkksPMjEKpM=
|
||||||
|
github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/YNA/UnBk=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
|
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
|
||||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
|
github.com/markbates/going v1.0.0 h1:DQw0ZP7NbNlFGcKbcE/IVSOAFzScxRtLpd0rLMzLhq0=
|
||||||
|
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
|
||||||
|
github.com/markbates/goth v1.68.1-0.20211006204042-9dc8905b41c8 h1:JibQrkJapVsb0pweJ5T14jZuuYZZTjll0PZBw4XfSCI=
|
||||||
|
github.com/markbates/goth v1.68.1-0.20211006204042-9dc8905b41c8/go.mod h1:V2VcDMzDiMHW+YmqYl7i0cMiAUeCkAe4QE6jRKBhXZw=
|
||||||
|
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911 h1:erppMjjp69Rertg1zlgRbLJH1u+eCmRPxKjMZ5I8/Ro=
|
||||||
|
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||||
@@ -240,8 +272,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
|||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA=
|
||||||
|
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -253,11 +286,11 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
|
|||||||
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/plutov/paypal/v4 v4.3.7 h1:wPvhAJ3RkDkV+UDrGX/UivXAl5JEPOOJuzsdgnTMJHc=
|
|
||||||
github.com/plutov/paypal/v4 v4.3.7/go.mod h1:D56boafCRGcF/fEM0w282kj0fCDKIyrwOPX/Te1jCmw=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
@@ -281,6 +314,14 @@ github.com/qiangmzsx/string-adapter/v2 v2.1.0/go.mod h1:PElPB7b7HnGKTsuADAffFpOQ
|
|||||||
github.com/qor/oss v0.0.0-20191031055114-aef9ba66bf76 h1:J2Xj92efYLxPl3BiibgEDEUiMsCBzwTurE/8JjD8CG4=
|
github.com/qor/oss v0.0.0-20191031055114-aef9ba66bf76 h1:J2Xj92efYLxPl3BiibgEDEUiMsCBzwTurE/8JjD8CG4=
|
||||||
github.com/qor/oss v0.0.0-20191031055114-aef9ba66bf76/go.mod h1:JhtPzUhP5KGtCB2yksmxuYAD4hEWw4qGQJpucjsm3U0=
|
github.com/qor/oss v0.0.0-20191031055114-aef9ba66bf76/go.mod h1:JhtPzUhP5KGtCB2yksmxuYAD4hEWw4qGQJpucjsm3U0=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
|
github.com/russellhaering/gosaml2 v0.6.0 h1:OED8FLgczXxXAPlKhnJHQfmEig52tDX2qeXdPtZRIKc=
|
||||||
|
github.com/russellhaering/gosaml2 v0.6.0/go.mod h1:CtzxpPr4+bevsATaqR0rw3aqrNlX274b+3C6vFTLCk8=
|
||||||
|
github.com/russellhaering/goxmldsig v1.1.0/go.mod h1:QK8GhXPB3+AfuCrfo0oRISa9NfzeCpWmxeGnqEpDF9o=
|
||||||
|
github.com/russellhaering/goxmldsig v1.1.1 h1:vI0r2osGF1A9PLvsGdPUAGwEIrKa4Pj5sesSBsebIxM=
|
||||||
|
github.com/russellhaering/goxmldsig v1.1.1/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
|
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
|
||||||
@@ -302,7 +343,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||||
@@ -320,6 +360,7 @@ github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqI
|
|||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
|
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
|
||||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
@@ -334,8 +375,9 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
|
||||||
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -396,14 +438,17 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
|||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 h1:3B43BWw0xEBsLZ/NO1VALz6fppU3481pik+2Ksv45z8=
|
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 h1:3B43BWw0xEBsLZ/NO1VALz6fppU3481pik+2Ksv45z8=
|
||||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -446,17 +491,20 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@@ -503,6 +551,8 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
|||||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||||
|
golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -524,6 +574,7 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
|||||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
|
google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
@@ -561,6 +612,8 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
|||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200929141702-51c3e5b607fe/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
@@ -573,6 +626,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
|
|||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@@ -590,8 +645,9 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod
|
|||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
@@ -601,6 +657,8 @@ gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|||||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
@@ -609,8 +667,9 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
@@ -625,6 +684,5 @@ xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
|
|||||||
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||||
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
|
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
|
||||||
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
||||||
|
xorm.io/xorm v1.0.3 h1:3dALAohvINu2mfEix5a5x5ZmSVGSljinoSGgvGbaZp0=
|
||||||
xorm.io/xorm v1.0.3/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
xorm.io/xorm v1.0.3/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
||||||
xorm.io/xorm v1.0.6 h1:7eco1c8QUpGz+3dztpLDj9gU1bTiQdFC/KtmPaLxUJk=
|
|
||||||
xorm.io/xorm v1.0.6/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
|
|
||||||
|
97
i18n/generate.go
Normal file
97
i18n/generate.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// Copyright 2021 The casbin 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 i18n
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type I18nData map[string]map[string]string
|
||||||
|
|
||||||
|
var reI18n *regexp.Regexp
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
reI18n, _ = regexp.Compile("i18next.t\\(\"(.*?)\"\\)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllI18nStrings(fileContent string) []string {
|
||||||
|
res := []string{}
|
||||||
|
|
||||||
|
matches := reI18n.FindAllStringSubmatch(fileContent, -1)
|
||||||
|
if matches == nil {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, match := range matches {
|
||||||
|
res = append(res, match[1])
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllJsFilePaths() []string {
|
||||||
|
path := "../web/src"
|
||||||
|
|
||||||
|
res := []string{}
|
||||||
|
err := filepath.Walk(path,
|
||||||
|
func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasSuffix(info.Name(), ".js") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, path)
|
||||||
|
fmt.Println(path, info.Name())
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseToData() *I18nData {
|
||||||
|
allWords := []string{}
|
||||||
|
paths := getAllJsFilePaths()
|
||||||
|
for _, path := range paths {
|
||||||
|
fileContent := util.ReadStringFromPath(path)
|
||||||
|
words := getAllI18nStrings(fileContent)
|
||||||
|
allWords = append(allWords, words...)
|
||||||
|
}
|
||||||
|
fmt.Printf("%v\n", allWords)
|
||||||
|
|
||||||
|
data := I18nData{}
|
||||||
|
for _, word := range allWords {
|
||||||
|
tokens := strings.Split(word, ":")
|
||||||
|
namespace := tokens[0]
|
||||||
|
key := tokens[1]
|
||||||
|
|
||||||
|
if _, ok := data[namespace]; !ok {
|
||||||
|
data[namespace] = map[string]string{}
|
||||||
|
}
|
||||||
|
data[namespace][key] = key
|
||||||
|
}
|
||||||
|
|
||||||
|
return &data
|
||||||
|
}
|
37
i18n/generate_test.go
Normal file
37
i18n/generate_test.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2021 The casbin 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 i18n
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func applyToOtherLanguage(dataEn *I18nData, lang string) {
|
||||||
|
dataOther := readI18nFile(lang)
|
||||||
|
println(dataOther)
|
||||||
|
|
||||||
|
applyData(dataEn, dataOther)
|
||||||
|
writeI18nFile(lang, dataEn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateI18nStrings(t *testing.T) {
|
||||||
|
dataEn := parseToData()
|
||||||
|
writeI18nFile("en", dataEn)
|
||||||
|
|
||||||
|
applyToOtherLanguage(dataEn, "de")
|
||||||
|
applyToOtherLanguage(dataEn, "fr")
|
||||||
|
applyToOtherLanguage(dataEn, "ja")
|
||||||
|
applyToOtherLanguage(dataEn, "ko")
|
||||||
|
applyToOtherLanguage(dataEn, "ru")
|
||||||
|
applyToOtherLanguage(dataEn, "zh")
|
||||||
|
}
|
63
i18n/util.go
Normal file
63
i18n/util.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2021 The casbin 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 i18n
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getI18nFilePath(language string) string {
|
||||||
|
return fmt.Sprintf("../web/src/locales/%s/data.json", language)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readI18nFile(language string) *I18nData {
|
||||||
|
s := util.ReadStringFromPath(getI18nFilePath(language))
|
||||||
|
|
||||||
|
data := &I18nData{}
|
||||||
|
err := util.JsonToStruct(s, data)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeI18nFile(language string, data *I18nData) {
|
||||||
|
s := util.StructToJsonFormatted(data)
|
||||||
|
s = strings.ReplaceAll(s, "\\u0026", "&")
|
||||||
|
println(s)
|
||||||
|
|
||||||
|
util.WriteStringToPath(s, getI18nFilePath(language))
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyData(data1 *I18nData, data2 *I18nData) {
|
||||||
|
for namespace, pairs2 := range *data2 {
|
||||||
|
if _, ok := (*data1)[namespace]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs1 := (*data1)[namespace]
|
||||||
|
|
||||||
|
for key, value := range pairs2 {
|
||||||
|
if _, ok := pairs1[key]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs1[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -22,7 +22,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -132,7 +131,7 @@ func (idp *DingTalkIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
}
|
}
|
||||||
}(resp.Body)
|
}(resp.Body)
|
||||||
|
|
||||||
body, _ := ioutil.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
info := InfoResp{}
|
info := InfoResp{}
|
||||||
_ = json.Unmarshal(body, &info)
|
_ = json.Unmarshal(body, &info)
|
||||||
errCode := info.Errcode
|
errCode := info.Errcode
|
||||||
@@ -148,7 +147,7 @@ func (idp *DingTalkIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}(resp.Body)
|
}(resp.Body)
|
||||||
body, _ = ioutil.ReadAll(resp.Body)
|
body, _ = io.ReadAll(resp.Body)
|
||||||
tokenResp := DingTalkAccessToken{}
|
tokenResp := DingTalkAccessToken{}
|
||||||
_ = json.Unmarshal(body, &tokenResp)
|
_ = json.Unmarshal(body, &tokenResp)
|
||||||
if tokenResp.ErrCode != 0 {
|
if tokenResp.ErrCode != 0 {
|
||||||
|
@@ -19,7 +19,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -93,7 +92,7 @@ func (idp *GiteeIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rbs, err := ioutil.ReadAll(resp.Body)
|
rbs, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ package idp
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@@ -172,7 +172,7 @@ func (idp *GithubIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
|||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ package idp
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -85,7 +85,7 @@ func (idp *GitlabIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
data, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -204,18 +204,18 @@ type GitlabUserInfo struct {
|
|||||||
|
|
||||||
// GetUserInfo use GitlabProviderToken gotten before return GitlabUserInfo
|
// GetUserInfo use GitlabProviderToken gotten before return GitlabUserInfo
|
||||||
func (idp *GitlabIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
func (idp *GitlabIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||||
resp, err := idp.Client.Get("https://gitlab.com/api/v4/user?access_token="+token.AccessToken)
|
resp, err := idp.Client.Get("https://gitlab.com/api/v4/user?access_token=" + token.AccessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
data, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
guser := GitlabUserInfo{}
|
guser := GitlabUserInfo{}
|
||||||
if err = json.Unmarshal(data, &guser);err != nil {
|
if err = json.Unmarshal(data, &guser); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
@@ -95,7 +95,7 @@ func (idp *GoogleIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
254
idp/goth.go
Normal file
254
idp/goth.go
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
// Copyright 2021 The casbin 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 (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/markbates/goth"
|
||||||
|
"github.com/markbates/goth/providers/amazon"
|
||||||
|
"github.com/markbates/goth/providers/apple"
|
||||||
|
"github.com/markbates/goth/providers/azuread"
|
||||||
|
"github.com/markbates/goth/providers/bitbucket"
|
||||||
|
"github.com/markbates/goth/providers/digitalocean"
|
||||||
|
"github.com/markbates/goth/providers/discord"
|
||||||
|
"github.com/markbates/goth/providers/dropbox"
|
||||||
|
"github.com/markbates/goth/providers/facebook"
|
||||||
|
"github.com/markbates/goth/providers/gitea"
|
||||||
|
"github.com/markbates/goth/providers/github"
|
||||||
|
"github.com/markbates/goth/providers/gitlab"
|
||||||
|
"github.com/markbates/goth/providers/google"
|
||||||
|
"github.com/markbates/goth/providers/heroku"
|
||||||
|
"github.com/markbates/goth/providers/instagram"
|
||||||
|
"github.com/markbates/goth/providers/kakao"
|
||||||
|
"github.com/markbates/goth/providers/line"
|
||||||
|
"github.com/markbates/goth/providers/linkedin"
|
||||||
|
"github.com/markbates/goth/providers/microsoftonline"
|
||||||
|
"github.com/markbates/goth/providers/paypal"
|
||||||
|
"github.com/markbates/goth/providers/salesforce"
|
||||||
|
"github.com/markbates/goth/providers/shopify"
|
||||||
|
"github.com/markbates/goth/providers/slack"
|
||||||
|
"github.com/markbates/goth/providers/tumblr"
|
||||||
|
"github.com/markbates/goth/providers/twitter"
|
||||||
|
"github.com/markbates/goth/providers/yahoo"
|
||||||
|
"github.com/markbates/goth/providers/yandex"
|
||||||
|
"github.com/markbates/goth/providers/zoom"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GothIdProvider struct {
|
||||||
|
Provider goth.Provider
|
||||||
|
Session goth.Session
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGothIdProvider(providerType string, clientId string, clientSecret string, redirectUrl string) *GothIdProvider {
|
||||||
|
var idp GothIdProvider
|
||||||
|
switch providerType {
|
||||||
|
case "Amazon":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: amazon.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &amazon.Session{},
|
||||||
|
}
|
||||||
|
case "Apple":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: apple.New(clientId, clientSecret, redirectUrl, nil),
|
||||||
|
Session: &apple.Session{},
|
||||||
|
}
|
||||||
|
case "AzureAD":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: azuread.New(clientId, clientSecret, redirectUrl, nil),
|
||||||
|
Session: &azuread.Session{},
|
||||||
|
}
|
||||||
|
case "Bitbucket":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: bitbucket.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &bitbucket.Session{},
|
||||||
|
}
|
||||||
|
case "DigitalOcean":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: digitalocean.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &digitalocean.Session{},
|
||||||
|
}
|
||||||
|
case "Discord":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: discord.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &discord.Session{},
|
||||||
|
}
|
||||||
|
case "Dropbox":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: dropbox.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &dropbox.Session{},
|
||||||
|
}
|
||||||
|
case "Facebook":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: facebook.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &facebook.Session{},
|
||||||
|
}
|
||||||
|
case "Gitea":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: gitea.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &gitea.Session{},
|
||||||
|
}
|
||||||
|
case "GitHub":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: github.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &github.Session{},
|
||||||
|
}
|
||||||
|
case "GitLab":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: gitlab.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &gitlab.Session{},
|
||||||
|
}
|
||||||
|
case "Google":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: google.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &google.Session{},
|
||||||
|
}
|
||||||
|
case "Heroku":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: heroku.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &heroku.Session{},
|
||||||
|
}
|
||||||
|
case "Instagram":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: instagram.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &instagram.Session{},
|
||||||
|
}
|
||||||
|
case "Kakao":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: kakao.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &kakao.Session{},
|
||||||
|
}
|
||||||
|
case "Linkedin":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: linkedin.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &linkedin.Session{},
|
||||||
|
}
|
||||||
|
case "Line":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: line.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &line.Session{},
|
||||||
|
}
|
||||||
|
case "MicrosoftOnline":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: microsoftonline.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: µsoftonline.Session{},
|
||||||
|
}
|
||||||
|
case "Paypal":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: paypal.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &paypal.Session{},
|
||||||
|
}
|
||||||
|
case "SalesForce":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: salesforce.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &salesforce.Session{},
|
||||||
|
}
|
||||||
|
case "Shopify":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: shopify.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &shopify.Session{},
|
||||||
|
}
|
||||||
|
case "Slack":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: slack.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &slack.Session{},
|
||||||
|
}
|
||||||
|
case "Tumblr":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: tumblr.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &tumblr.Session{},
|
||||||
|
}
|
||||||
|
case "Twitter":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: twitter.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &twitter.Session{},
|
||||||
|
}
|
||||||
|
case "Yahoo":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: yahoo.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &yahoo.Session{},
|
||||||
|
}
|
||||||
|
case "Yandex":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: yandex.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &yandex.Session{},
|
||||||
|
}
|
||||||
|
case "Zoom":
|
||||||
|
idp = GothIdProvider{
|
||||||
|
Provider: zoom.New(clientId, clientSecret, redirectUrl),
|
||||||
|
Session: &zoom.Session{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &idp
|
||||||
|
}
|
||||||
|
|
||||||
|
//Goth's idp all implement the Client method, but since the goth.Provider interface does not provide to modify idp's client method, reflection is required
|
||||||
|
func (idp *GothIdProvider) SetHttpClient(client *http.Client) {
|
||||||
|
idpClient := reflect.ValueOf(idp.Provider).Elem().FieldByName("HTTPClient")
|
||||||
|
idpClient.Set(reflect.ValueOf(client))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||||
|
var expireAt time.Time
|
||||||
|
//Need to construct variables supported by goth
|
||||||
|
//to call the function to obtain accessToken
|
||||||
|
value := url.Values{}
|
||||||
|
value.Add("code", code)
|
||||||
|
accessToken, err := idp.Session.Authorize(idp.Provider, value)
|
||||||
|
//Get ExpiresAt's value
|
||||||
|
valueOfExpire := reflect.ValueOf(idp.Session).Elem().FieldByName("ExpiresAt")
|
||||||
|
if valueOfExpire.IsValid() {
|
||||||
|
expireAt = valueOfExpire.Interface().(time.Time)
|
||||||
|
}
|
||||||
|
token := oauth2.Token{
|
||||||
|
AccessToken: accessToken,
|
||||||
|
Expiry: expireAt,
|
||||||
|
}
|
||||||
|
return &token, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (idp *GothIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||||
|
gothUser, err := idp.Provider.FetchUser(idp.Session)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return getUser(gothUser), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUser(gothUser goth.User) *UserInfo {
|
||||||
|
user := UserInfo{
|
||||||
|
Id: gothUser.UserID,
|
||||||
|
Username: gothUser.Name,
|
||||||
|
DisplayName: gothUser.NickName,
|
||||||
|
Email: gothUser.Email,
|
||||||
|
AvatarUrl: gothUser.AvatarURL,
|
||||||
|
}
|
||||||
|
//Some idp return an empty Name
|
||||||
|
//so construct the Name with firstname and lastname or nickname
|
||||||
|
if user.Username == "" {
|
||||||
|
user.Username = fmt.Sprintf("%v%v", gothUser.FirstName, gothUser.LastName)
|
||||||
|
}
|
||||||
|
if user.Username == "" {
|
||||||
|
user.Username = gothUser.NickName
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user
|
||||||
|
}
|
@@ -17,7 +17,6 @@ package idp
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -169,7 +168,7 @@ func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
|||||||
req.Header.Set("Authorization", "Bearer "+token.AccessToken)
|
req.Header.Set("Authorization", "Bearer "+token.AccessToken)
|
||||||
|
|
||||||
resp, err := idp.Client.Do(req)
|
resp, err := idp.Client.Do(req)
|
||||||
data, err = ioutil.ReadAll(resp.Body)
|
data, err = io.ReadAll(resp.Body)
|
||||||
err = resp.Body.Close()
|
err = resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -201,7 +200,7 @@ func (idp *LarkIdProvider) postWithBody(body interface{}, url string) ([]byte, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
data, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -85,7 +84,7 @@ func (idp *LinkedInIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rbs, err := ioutil.ReadAll(resp.Body)
|
rbs, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -323,7 +322,7 @@ func (idp *LinkedInIdProvider) GetUrlRespWithAuthorization(url, token string) ([
|
|||||||
}
|
}
|
||||||
}(resp.Body)
|
}(resp.Body)
|
||||||
|
|
||||||
bs, err := ioutil.ReadAll(resp.Body)
|
bs, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ package idp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
@@ -59,7 +60,20 @@ func GetIdProvider(providerType string, clientId string, clientSecret string, re
|
|||||||
return NewLarkIdProvider(clientId, clientSecret, redirectUrl)
|
return NewLarkIdProvider(clientId, clientSecret, redirectUrl)
|
||||||
} else if providerType == "GitLab" {
|
} else if providerType == "GitLab" {
|
||||||
return NewGitlabIdProvider(clientId, clientSecret, redirectUrl)
|
return NewGitlabIdProvider(clientId, clientSecret, redirectUrl)
|
||||||
|
} else if isGothSupport(providerType) {
|
||||||
|
return NewGothIdProvider(providerType, clientId, clientSecret, redirectUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var gothList = []string{"Apple", "AzureAd", "Slack"}
|
||||||
|
|
||||||
|
func isGothSupport(provider string) bool {
|
||||||
|
for _, value := range gothList {
|
||||||
|
if strings.EqualFold(value, provider) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@@ -18,7 +18,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -75,7 +75,7 @@ func (idp *QqIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
tokenContent, err := ioutil.ReadAll(resp.Body)
|
tokenContent, err := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
re := regexp.MustCompile("token=(.*?)&")
|
re := regexp.MustCompile("token=(.*?)&")
|
||||||
matched := re.FindAllStringSubmatch(string(tokenContent), -1)
|
matched := re.FindAllStringSubmatch(string(tokenContent), -1)
|
||||||
@@ -145,7 +145,7 @@ func (idp *QqIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
openIdBody, err := ioutil.ReadAll(resp.Body)
|
openIdBody, err := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
re := regexp.MustCompile("\"openid\":\"(.*?)\"}")
|
re := regexp.MustCompile("\"openid\":\"(.*?)\"}")
|
||||||
matched := re.FindAllStringSubmatch(string(openIdBody), -1)
|
matched := re.FindAllStringSubmatch(string(openIdBody), -1)
|
||||||
@@ -161,7 +161,7 @@ func (idp *QqIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
userInfoBody, err := ioutil.ReadAll(resp.Body)
|
userInfoBody, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -179,6 +179,7 @@ func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
|||||||
|
|
||||||
userInfo := UserInfo{
|
userInfo := UserInfo{
|
||||||
Id: id,
|
Id: id,
|
||||||
|
Username: wechatUserInfo.Nickname,
|
||||||
DisplayName: wechatUserInfo.Nickname,
|
DisplayName: wechatUserInfo.Nickname,
|
||||||
AvatarUrl: wechatUserInfo.Headimgurl,
|
AvatarUrl: wechatUserInfo.Headimgurl,
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -195,7 +194,7 @@ func (idp *WeComIdProvider) postWithBody(body interface{}, url string) ([]byte,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
data, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -92,7 +91,7 @@ func (idp *WeiBoIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}(resp.Body)
|
}(resp.Body)
|
||||||
bs, err := ioutil.ReadAll(resp.Body)
|
bs, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
9
main.go
9
main.go
@@ -31,9 +31,12 @@ func main() {
|
|||||||
object.InitAdapter()
|
object.InitAdapter()
|
||||||
object.InitDb()
|
object.InitDb()
|
||||||
object.InitDefaultStorageProvider()
|
object.InitDefaultStorageProvider()
|
||||||
|
object.InitLdapAutoSynchronizer()
|
||||||
proxy.InitHttpClient()
|
proxy.InitHttpClient()
|
||||||
authz.InitAuthz()
|
authz.InitAuthz()
|
||||||
|
|
||||||
|
go object.RunSyncUsersJob()
|
||||||
|
|
||||||
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
|
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
|
||||||
AllowOrigins: []string{"*"},
|
AllowOrigins: []string{"*"},
|
||||||
AllowMethods: []string{"GET", "PUT", "PATCH"},
|
AllowMethods: []string{"GET", "PUT", "PATCH"},
|
||||||
@@ -61,14 +64,14 @@ func main() {
|
|||||||
beego.BConfig.WebConfig.Session.SessionProvider = "redis"
|
beego.BConfig.WebConfig.Session.SessionProvider = "redis"
|
||||||
beego.BConfig.WebConfig.Session.SessionProviderConfig = beego.AppConfig.String("redisEndpoint")
|
beego.BConfig.WebConfig.Session.SessionProviderConfig = beego.AppConfig.String("redisEndpoint")
|
||||||
}
|
}
|
||||||
beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600 * 24 * 30
|
beego.BConfig.WebConfig.Session.SessionCookieLifeTime = 3600 * 24 * 30
|
||||||
//beego.BConfig.WebConfig.Session.SessionCookieSameSite = http.SameSiteNoneMode
|
//beego.BConfig.WebConfig.Session.SessionCookieSameSite = http.SameSiteNoneMode
|
||||||
|
|
||||||
err := logs.SetLogger("file", `{"filename":"logs/casdoor.log","maxdays":99999}`)
|
err := logs.SetLogger("file", `{"filename":"logs/casdoor.log","maxdays":99999,"perm":"0770"}`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
logs.SetLevel(logs.LevelInformational)
|
//logs.SetLevel(logs.LevelInformational)
|
||||||
logs.SetLogFuncCall(false)
|
logs.SetLogFuncCall(false)
|
||||||
|
|
||||||
beego.Run()
|
beego.Run()
|
||||||
|
@@ -19,6 +19,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
|
"github.com/casbin/casdoor/conf"
|
||||||
_ "github.com/go-sql-driver/mysql" // db = mysql
|
_ "github.com/go-sql-driver/mysql" // db = mysql
|
||||||
//_ "github.com/lib/pq" // db = postgres
|
//_ "github.com/lib/pq" // db = postgres
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
@@ -36,7 +37,7 @@ func InitConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InitAdapter() {
|
func InitAdapter() {
|
||||||
adapter = NewAdapter(beego.AppConfig.String("driverName"), beego.AppConfig.String("dataSourceName"), beego.AppConfig.String("dbName"))
|
adapter = NewAdapter(beego.AppConfig.String("driverName"), conf.GetBeegoConfDataSourceName(), beego.AppConfig.String("dbName"))
|
||||||
adapter.createTable()
|
adapter.createTable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +139,18 @@ func (a *Adapter) createTable() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
err = a.Engine.Sync2(new(Records))
|
|
||||||
|
err = a.Engine.Sync2(new(Record))
|
||||||
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -147,9 +159,4 @@ func (a *Adapter) createTable() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Payment))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,8 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
"xorm.io/core"
|
"xorm.io/core"
|
||||||
)
|
)
|
||||||
@@ -24,26 +26,40 @@ type Application struct {
|
|||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
Logo string `xorm:"varchar(100)" json:"logo"`
|
Logo string `xorm:"varchar(100)" json:"logo"`
|
||||||
HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
|
HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
|
||||||
Description string `xorm:"varchar(100)" json:"description"`
|
Description string `xorm:"varchar(100)" json:"description"`
|
||||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||||
EnablePassword bool `json:"enablePassword"`
|
EnablePassword bool `json:"enablePassword"`
|
||||||
EnableSignUp bool `json:"enableSignUp"`
|
EnableSignUp bool `json:"enableSignUp"`
|
||||||
Providers []*ProviderItem `xorm:"varchar(10000)" json:"providers"`
|
EnableCodeSignin bool `json:"enableCodeSignin"`
|
||||||
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
|
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
|
||||||
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
|
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
|
||||||
|
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
|
||||||
|
|
||||||
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
||||||
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
||||||
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
|
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
|
||||||
ExpireInHours int `json:"expireInHours"`
|
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
|
||||||
SignupUrl string `xorm:"varchar(100)" json:"signupUrl"`
|
ExpireInHours int `json:"expireInHours"`
|
||||||
SigninUrl string `xorm:"varchar(100)" json:"signinUrl"`
|
RefreshExpireInHours int `json:"refreshExpireInHours"`
|
||||||
ForgetUrl string `xorm:"varchar(100)" json:"forgetUrl"`
|
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
|
||||||
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
|
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
|
||||||
TermsOfUse string `xorm:"varchar(1000)" json:"termsOfUse"`
|
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
|
||||||
|
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
|
||||||
|
TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"`
|
||||||
|
SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
|
||||||
|
SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetApplicationCount(owner string) int {
|
||||||
|
count, err := adapter.Engine.Count(&Application{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApplications(owner string) []*Application {
|
func GetApplications(owner string) []*Application {
|
||||||
@@ -56,6 +72,26 @@ func GetApplications(owner string) []*Application {
|
|||||||
return applications
|
return applications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetPaginationApplications(owner string, offset, limit int) []*Application {
|
||||||
|
applications := []*Application{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Limit(limit, offset).Find(&applications, &Application{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return applications
|
||||||
|
}
|
||||||
|
|
||||||
|
func getApplicationsByOrganizationName(owner string, organization string) []*Application {
|
||||||
|
applications := []*Application{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner, Organization: organization})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return applications
|
||||||
|
}
|
||||||
|
|
||||||
func getProviderMap(owner string) map[string]*Provider {
|
func getProviderMap(owner string) map[string]*Provider {
|
||||||
providers := GetProviders(owner)
|
providers := GetProviders(owner)
|
||||||
m := map[string]*Provider{}
|
m := map[string]*Provider{}
|
||||||
@@ -64,7 +100,7 @@ func getProviderMap(owner string) map[string]*Provider {
|
|||||||
// continue
|
// continue
|
||||||
//}
|
//}
|
||||||
|
|
||||||
m[provider.Name] = getMaskedProvider(provider)
|
m[provider.Name] = GetMaskedProvider(provider)
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
@@ -182,6 +218,10 @@ func UpdateApplication(id string, application *Application) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if name == "app-built-in" {
|
||||||
|
application.Name = name
|
||||||
|
}
|
||||||
|
|
||||||
for _, providerItem := range application.Providers {
|
for _, providerItem := range application.Providers {
|
||||||
providerItem.Provider = nil
|
providerItem.Provider = nil
|
||||||
}
|
}
|
||||||
@@ -210,6 +250,10 @@ func AddApplication(application *Application) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeleteApplication(application *Application) bool {
|
func DeleteApplication(application *Application) bool {
|
||||||
|
if application.Name == "app-built-in" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := adapter.Engine.ID(core.PK{application.Owner, application.Name}).Delete(&Application{})
|
affected, err := adapter.Engine.ID(core.PK{application.Owner, application.Name}).Delete(&Application{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -217,3 +261,7 @@ func DeleteApplication(application *Application) bool {
|
|||||||
|
|
||||||
return affected != 0
|
return affected != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (application *Application) GetId() string {
|
||||||
|
return fmt.Sprintf("%s/%s", application.Owner, application.Name)
|
||||||
|
}
|
||||||
|
@@ -46,10 +46,6 @@ func (application *Application) GetStorageProvider() *Provider {
|
|||||||
return application.GetProviderByCategory("Storage")
|
return application.GetProviderByCategory("Storage")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (application *Application) GetPayProvider() *Provider {
|
|
||||||
return application.GetProviderByCategory("Pay")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (application *Application) getSignupItem(itemName string) *SignupItem {
|
func (application *Application) getSignupItem(itemName string) *SignupItem {
|
||||||
for _, signupItem := range application.SignupItems {
|
for _, signupItem := range application.SignupItems {
|
||||||
if signupItem.Name == itemName {
|
if signupItem.Name == itemName {
|
||||||
@@ -59,10 +55,6 @@ func (application *Application) getSignupItem(itemName string) *SignupItem {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (application *Application) IsSignupItemEnabled(itemName string) bool {
|
|
||||||
return application.getSignupItem(itemName) != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (application *Application) IsSignupItemVisible(itemName string) bool {
|
func (application *Application) IsSignupItemVisible(itemName string) bool {
|
||||||
signupItem := application.getSignupItem(itemName)
|
signupItem := application.getSignupItem(itemName)
|
||||||
if signupItem == nil {
|
if signupItem == nil {
|
||||||
@@ -72,6 +64,15 @@ func (application *Application) IsSignupItemVisible(itemName string) bool {
|
|||||||
return signupItem.Visible
|
return signupItem.Visible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (application *Application) IsSignupItemRequired(itemName string) bool {
|
||||||
|
signupItem := application.getSignupItem(itemName)
|
||||||
|
if signupItem == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return signupItem.Required
|
||||||
|
}
|
||||||
|
|
||||||
func (application *Application) GetSignupItemRule(itemName string) string {
|
func (application *Application) GetSignupItemRule(itemName string) string {
|
||||||
signupItem := application.getSignupItem(itemName)
|
signupItem := application.getSignupItem(itemName)
|
||||||
if signupItem == nil {
|
if signupItem == nil {
|
||||||
|
@@ -26,7 +26,7 @@ import (
|
|||||||
var defaultStorageProvider *Provider = nil
|
var defaultStorageProvider *Provider = nil
|
||||||
|
|
||||||
func InitDefaultStorageProvider() {
|
func InitDefaultStorageProvider() {
|
||||||
defaultStorageProviderStr := beego.AppConfig.String("defaultStorageProvider")
|
defaultStorageProviderStr := beego.AppConfig.String("defaultStorageProvider")
|
||||||
if defaultStorageProviderStr != "" {
|
if defaultStorageProviderStr != "" {
|
||||||
defaultStorageProvider = getProvider("admin", defaultStorageProviderStr)
|
defaultStorageProvider = getProvider("admin", defaultStorageProviderStr)
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ func getPermanentAvatarUrl(organization string, username string, url string) str
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _, err = UploadFile(defaultStorageProvider, fullFilePath, fileBuffer)
|
_, _, err = UploadFileSafe(defaultStorageProvider, fullFilePath, fileBuffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/casbin/casdoor/cred"
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
|
goldap "github.com/go-ldap/ldap/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var reWhiteSpace *regexp.Regexp
|
var reWhiteSpace *regexp.Regexp
|
||||||
@@ -47,6 +49,14 @@ func CheckUserSignup(application *Application, organization *Organization, usern
|
|||||||
}
|
}
|
||||||
|
|
||||||
if application.IsSignupItemVisible("Email") {
|
if application.IsSignupItemVisible("Email") {
|
||||||
|
if email == "" {
|
||||||
|
if application.IsSignupItemRequired("Email") {
|
||||||
|
return "email cannot be empty"
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if HasUserByField(organization.Name, "email", email) {
|
if HasUserByField(organization.Name, "email", email) {
|
||||||
return "email already exists"
|
return "email already exists"
|
||||||
} else if !util.IsEmailValid(email) {
|
} else if !util.IsEmailValid(email) {
|
||||||
@@ -55,6 +65,14 @@ func CheckUserSignup(application *Application, organization *Organization, usern
|
|||||||
}
|
}
|
||||||
|
|
||||||
if application.IsSignupItemVisible("Phone") {
|
if application.IsSignupItemVisible("Phone") {
|
||||||
|
if phone == "" {
|
||||||
|
if application.IsSignupItemRequired("Phone") {
|
||||||
|
return "phone cannot be empty"
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if HasUserByField(organization.Name, "phone", phone) {
|
if HasUserByField(organization.Name, "phone", phone) {
|
||||||
return "phone already exists"
|
return "phone already exists"
|
||||||
} else if organization.PhonePrefix == "86" && !util.IsPhoneCnValid(phone) {
|
} else if organization.PhonePrefix == "86" && !util.IsPhoneCnValid(phone) {
|
||||||
@@ -86,14 +104,16 @@ func CheckPassword(user *User, password string) string {
|
|||||||
if organization == nil {
|
if organization == nil {
|
||||||
return "organization does not exist"
|
return "organization does not exist"
|
||||||
}
|
}
|
||||||
|
|
||||||
if organization.PasswordType == "plain" {
|
credManager := cred.GetCredManager(organization.PasswordType)
|
||||||
if password == user.Password {
|
if credManager != nil {
|
||||||
return ""
|
if organization.MasterPassword != "" {
|
||||||
|
if credManager.IsPasswordCorrect(password, organization.MasterPassword, "", organization.PasswordSalt) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return "password incorrect"
|
|
||||||
} else if organization.PasswordType == "salt" {
|
if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) {
|
||||||
if password == user.Password || getSaltedPassword(password, organization.PasswordSalt) == user.Password {
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return "password incorrect"
|
return "password incorrect"
|
||||||
@@ -102,15 +122,55 @@ func CheckPassword(user *User, password string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkLdapUserPassword(user *User, password string) (*User, string) {
|
||||||
|
ldaps := GetLdaps(user.Owner)
|
||||||
|
ldapLoginSuccess := false
|
||||||
|
for _, ldapServer := range ldaps {
|
||||||
|
conn, err := GetLdapConn(ldapServer.Host, ldapServer.Port, ldapServer.Admin, ldapServer.Passwd)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
SearchFilter := fmt.Sprintf("(&(objectClass=posixAccount)(uid=%s))", user.Name)
|
||||||
|
searchReq := goldap.NewSearchRequest(ldapServer.BaseDn,
|
||||||
|
goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
|
||||||
|
SearchFilter, []string{}, nil)
|
||||||
|
searchResult, err := conn.Conn.Search(searchReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(searchResult.Entries) == 0 {
|
||||||
|
continue
|
||||||
|
} else if len(searchResult.Entries) > 1 {
|
||||||
|
return nil, "Error: multiple accounts with same uid, please check your ldap server"
|
||||||
|
}
|
||||||
|
|
||||||
|
dn := searchResult.Entries[0].DN
|
||||||
|
if err := conn.Conn.Bind(dn, password); err == nil {
|
||||||
|
ldapLoginSuccess = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ldapLoginSuccess {
|
||||||
|
return nil, "ldap user name or password incorrect"
|
||||||
|
}
|
||||||
|
return user, ""
|
||||||
|
}
|
||||||
|
|
||||||
func CheckUserPassword(organization string, username string, password string) (*User, string) {
|
func CheckUserPassword(organization string, username string, password string) (*User, string) {
|
||||||
user := GetUserByFields(organization, username)
|
user := GetUserByFields(organization, username)
|
||||||
if user == nil {
|
if user == nil || user.IsDeleted == true {
|
||||||
return nil, "the user does not exist, please sign up first"
|
return nil, "the user does not exist, please sign up first"
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.IsForbidden {
|
if user.IsForbidden {
|
||||||
return nil, "the user is forbidden to sign in, please contact the administrator"
|
return nil, "the user is forbidden to sign in, please contact the administrator"
|
||||||
}
|
}
|
||||||
|
//for ldap users
|
||||||
|
if user.Ldap != "" {
|
||||||
|
return checkLdapUserPassword(user, password)
|
||||||
|
}
|
||||||
|
|
||||||
msg := CheckPassword(user, password)
|
msg := CheckPassword(user, password)
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
|
@@ -64,9 +64,11 @@ func initBuiltInUser() {
|
|||||||
Affiliation: "Example Inc.",
|
Affiliation: "Example Inc.",
|
||||||
Tag: "staff",
|
Tag: "staff",
|
||||||
Score: 2000,
|
Score: 2000,
|
||||||
|
Ranking: 1,
|
||||||
IsAdmin: true,
|
IsAdmin: true,
|
||||||
IsGlobalAdmin: true,
|
IsGlobalAdmin: true,
|
||||||
IsForbidden: false,
|
IsForbidden: false,
|
||||||
|
IsDeleted: false,
|
||||||
Properties: make(map[string]string),
|
Properties: make(map[string]string),
|
||||||
}
|
}
|
||||||
AddUser(user)
|
AddUser(user)
|
||||||
@@ -89,9 +91,18 @@ func initBuiltInApplication() {
|
|||||||
EnablePassword: true,
|
EnablePassword: true,
|
||||||
EnableSignUp: true,
|
EnableSignUp: true,
|
||||||
Providers: []*ProviderItem{},
|
Providers: []*ProviderItem{},
|
||||||
SignupItems: []*SignupItem{},
|
SignupItems: []*SignupItem{
|
||||||
RedirectUris: []string{},
|
{Name: "ID", Visible: false, Required: true, Prompted: false, Rule: "Random"},
|
||||||
ExpireInHours: 168,
|
{Name: "Username", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||||
|
{Name: "Display name", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||||
|
{Name: "Password", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||||
|
{Name: "Confirm password", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||||
|
{Name: "Email", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||||
|
{Name: "Phone", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||||
|
{Name: "Agreement", Visible: true, Required: true, Prompted: false, Rule: "None"},
|
||||||
|
},
|
||||||
|
RedirectUris: []string{},
|
||||||
|
ExpireInHours: 168,
|
||||||
}
|
}
|
||||||
AddApplication(application)
|
AddApplication(application)
|
||||||
}
|
}
|
||||||
|
@@ -17,10 +17,11 @@ package object
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
goldap "github.com/go-ldap/ldap/v3"
|
goldap "github.com/go-ldap/ldap/v3"
|
||||||
"github.com/thanhpk/randstr"
|
"github.com/thanhpk/randstr"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Ldap struct {
|
type Ldap struct {
|
||||||
@@ -77,6 +78,32 @@ type LdapRespUser struct {
|
|||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LdapUsersToLdapRespUsers(users []ldapUser) []LdapRespUser {
|
||||||
|
returnAnyNotEmpty := func(strs ...string) string {
|
||||||
|
for _, str := range strs {
|
||||||
|
if str != "" {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
res := make([]LdapRespUser, 0)
|
||||||
|
for _, user := range users {
|
||||||
|
res = append(res, LdapRespUser{
|
||||||
|
UidNumber: user.UidNumber,
|
||||||
|
Uid: user.Uid,
|
||||||
|
Cn: user.Cn,
|
||||||
|
GroupId: user.GidNumber,
|
||||||
|
Uuid: user.Uuid,
|
||||||
|
Email: returnAnyNotEmpty(user.Email, user.EmailAddress, user.Mail),
|
||||||
|
Phone: returnAnyNotEmpty(user.Mobile, user.MobileTelephoneNumber, user.TelephoneNumber),
|
||||||
|
Address: returnAnyNotEmpty(user.PostalAddress, user.RegisteredAddress),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*ldapConn, error) {
|
func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*ldapConn, error) {
|
||||||
conn, err := goldap.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
|
conn, err := goldap.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -285,15 +312,16 @@ func SyncLdapUsers(owner string, users []LdapRespUser) (*[]LdapRespUser, *[]Ldap
|
|||||||
existUuids := CheckLdapUuidExist(owner, uuids)
|
existUuids := CheckLdapUuidExist(owner, uuids)
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
|
found := false
|
||||||
if len(existUuids) > 0 {
|
if len(existUuids) > 0 {
|
||||||
for index, existUuid := range existUuids {
|
for _, existUuid := range existUuids {
|
||||||
if user.Uuid == existUuid {
|
if user.Uuid == existUuid {
|
||||||
existUsers = append(existUsers, user)
|
existUsers = append(existUsers, user)
|
||||||
existUuids = append(existUuids[:index], existUuids[index+1:]...)
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !AddUser(&User{
|
if !found && !AddUser(&User{
|
||||||
Owner: owner,
|
Owner: owner,
|
||||||
Name: buildLdapUserName(user.Uid, user.UidNumber),
|
Name: buildLdapUserName(user.Uid, user.UidNumber),
|
||||||
CreatedTime: util.GetCurrentTime(),
|
CreatedTime: util.GetCurrentTime(),
|
||||||
@@ -326,6 +354,7 @@ func UpdateLdapSyncTime(ldapId string) {
|
|||||||
func CheckLdapUuidExist(owner string, uuids []string) []string {
|
func CheckLdapUuidExist(owner string, uuids []string) []string {
|
||||||
var results []User
|
var results []User
|
||||||
var existUuids []string
|
var existUuids []string
|
||||||
|
existUuidSet := make(map[string]struct{})
|
||||||
|
|
||||||
//whereStr := ""
|
//whereStr := ""
|
||||||
//for i, uuid := range uuids {
|
//for i, uuid := range uuids {
|
||||||
@@ -343,9 +372,13 @@ func CheckLdapUuidExist(owner string, uuids []string) []string {
|
|||||||
|
|
||||||
if len(results) > 0 {
|
if len(results) > 0 {
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
existUuids = append(existUuids, result.Ldap)
|
existUuidSet[result.Ldap] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for uuid, _ := range existUuidSet {
|
||||||
|
existUuids = append(existUuids, uuid)
|
||||||
|
}
|
||||||
return existUuids
|
return existUuids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
111
object/ldap_autosync.go
Normal file
111
object/ldap_autosync.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego/logs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LdapAutoSynchronizer struct {
|
||||||
|
sync.Mutex
|
||||||
|
ldapIdToStopChan map[string]chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalLdapAutoSynchronizer *LdapAutoSynchronizer
|
||||||
|
|
||||||
|
func InitLdapAutoSynchronizer() {
|
||||||
|
globalLdapAutoSynchronizer = NewLdapAutoSynchronizer()
|
||||||
|
globalLdapAutoSynchronizer.LdapAutoSynchronizerStartUpAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLdapAutoSynchronizer() *LdapAutoSynchronizer {
|
||||||
|
return &LdapAutoSynchronizer{
|
||||||
|
ldapIdToStopChan: make(map[string]chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLdapAutoSynchronizer() *LdapAutoSynchronizer {
|
||||||
|
return globalLdapAutoSynchronizer
|
||||||
|
}
|
||||||
|
|
||||||
|
//start autosync for specified ldap, old existing autosync goroutine will be ceased
|
||||||
|
func (l *LdapAutoSynchronizer) StartAutoSync(ldapId string) error {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
|
||||||
|
ldap := GetLdap(ldapId)
|
||||||
|
if ldap == nil {
|
||||||
|
return fmt.Errorf("ldap %s doesn't exist", ldapId)
|
||||||
|
}
|
||||||
|
if res, ok := l.ldapIdToStopChan[ldapId]; ok {
|
||||||
|
res <- struct{}{}
|
||||||
|
delete(l.ldapIdToStopChan, ldapId)
|
||||||
|
}
|
||||||
|
|
||||||
|
stopChan := make(chan struct{})
|
||||||
|
l.ldapIdToStopChan[ldapId] = stopChan
|
||||||
|
logs.Info(fmt.Sprintf("autoSync started for %s", ldap.Id))
|
||||||
|
go l.syncRoutine(ldap, stopChan)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LdapAutoSynchronizer) StopAutoSync(ldapId string) {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
if res, ok := l.ldapIdToStopChan[ldapId]; ok {
|
||||||
|
res <- struct{}{}
|
||||||
|
delete(l.ldapIdToStopChan, ldapId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//autosync goroutine
|
||||||
|
func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
|
||||||
|
ticker := time.NewTicker(time.Duration(ldap.AutoSync) * time.Minute)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
UpdateLdapSyncTime(ldap.Id)
|
||||||
|
//fetch all users
|
||||||
|
conn, err := GetLdapConn(ldap.Host, ldap.Port, ldap.Admin, ldap.Passwd)
|
||||||
|
if err != nil {
|
||||||
|
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := conn.GetLdapUsers(ldap.BaseDn)
|
||||||
|
if err != nil {
|
||||||
|
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
existed, failed := SyncLdapUsers(ldap.Owner, LdapUsersToLdapRespUsers(users))
|
||||||
|
if len(*failed) != 0 {
|
||||||
|
logs.Warning(fmt.Sprintf("ldap autosync,%d new users,but %d user failed during :", len(users)-len(*existed)-len(*failed), len(*failed)), *failed)
|
||||||
|
} else {
|
||||||
|
logs.Info(fmt.Sprintf("ldap autosync success, %d new users, %d existing users", len(users)-len(*existed), len(*existed)))
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-stopChan:
|
||||||
|
logs.Info(fmt.Sprintf("autoSync goroutine for %s stopped", ldap.Id))
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//start all autosync goroutine for existing ldap servers in each organizations
|
||||||
|
func (l *LdapAutoSynchronizer) LdapAutoSynchronizerStartUpAll() {
|
||||||
|
organizations := []*Organization{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Find(&organizations)
|
||||||
|
if err != nil {
|
||||||
|
logs.Info("failed to Star up LdapAutoSynchronizer; ")
|
||||||
|
}
|
||||||
|
for _, org := range organizations {
|
||||||
|
for _, ldap := range GetLdaps(org.Name) {
|
||||||
|
if ldap.AutoSync != 0 {
|
||||||
|
l.StartAutoSync(ldap.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
object/oidc_discovery.go
Normal file
90
object/oidc_discovery.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
"gopkg.in/square/go-jose.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OidcDiscovery struct {
|
||||||
|
Issuer string `json:"issuer"`
|
||||||
|
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
||||||
|
TokenEndpoint string `json:"token_endpoint"`
|
||||||
|
UserinfoEndpoint string `json:"userinfo_endpoint"`
|
||||||
|
JwksUri string `json:"jwks_uri"`
|
||||||
|
ResponseTypesSupported []string `json:"response_types_supported"`
|
||||||
|
ResponseModesSupported []string `json:"response_modes_supported"`
|
||||||
|
GrantTypesSupported []string `json:"grant_types_supported"`
|
||||||
|
SubjectTypesSupported []string `json:"subject_types_supported"`
|
||||||
|
IdTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
|
||||||
|
ScopesSupported []string `json:"scopes_supported"`
|
||||||
|
ClaimsSupported []string `json:"claims_supported"`
|
||||||
|
RequestParameterSupported bool `json:"request_parameter_supported"`
|
||||||
|
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var oidcDiscovery OidcDiscovery
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
origin := beego.AppConfig.String("origin")
|
||||||
|
|
||||||
|
// Examples:
|
||||||
|
// https://login.okta.com/.well-known/openid-configuration
|
||||||
|
// https://auth0.auth0.com/.well-known/openid-configuration
|
||||||
|
// https://accounts.google.com/.well-known/openid-configuration
|
||||||
|
// https://access.line.me/.well-known/openid-configuration
|
||||||
|
oidcDiscovery = OidcDiscovery{
|
||||||
|
Issuer: origin,
|
||||||
|
AuthorizationEndpoint: fmt.Sprintf("%s/login/oauth/authorize", origin),
|
||||||
|
TokenEndpoint: fmt.Sprintf("%s/api/login/oauth/access_token", origin),
|
||||||
|
UserinfoEndpoint: fmt.Sprintf("%s/api/get-account", origin),
|
||||||
|
JwksUri: fmt.Sprintf("%s/api/certs", origin),
|
||||||
|
ResponseTypesSupported: []string{"id_token"},
|
||||||
|
ResponseModesSupported: []string{"login", "code", "link"},
|
||||||
|
GrantTypesSupported: []string{"password", "authorization_code"},
|
||||||
|
SubjectTypesSupported: []string{"public"},
|
||||||
|
IdTokenSigningAlgValuesSupported: []string{"RS256"},
|
||||||
|
ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"},
|
||||||
|
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isGlobalAdmin", "isForbidden", "signupApplication", "ldap"},
|
||||||
|
RequestParameterSupported: true,
|
||||||
|
RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOidcDiscovery() OidcDiscovery {
|
||||||
|
return oidcDiscovery
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetJSONWebKeySet() (jose.JSONWebKeySet, error) {
|
||||||
|
//follows the protocol rfc 7517(draft)
|
||||||
|
//link here: https://self-issued.info/docs/draft-ietf-jose-json-web-key.html
|
||||||
|
//or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key
|
||||||
|
certPEMBlock := []byte(tokenJwtPublicKey)
|
||||||
|
certDERBlock, _ := pem.Decode(certPEMBlock)
|
||||||
|
x509Cert, _ := x509.ParseCertificate(certDERBlock.Bytes)
|
||||||
|
|
||||||
|
var jwk jose.JSONWebKey
|
||||||
|
jwk.Key = x509Cert.PublicKey
|
||||||
|
jwk.Certificates = []*x509.Certificate{x509Cert}
|
||||||
|
|
||||||
|
var jwks jose.JSONWebKeySet
|
||||||
|
jwks.Keys = []jose.JSONWebKey{jwk}
|
||||||
|
return jwks, nil
|
||||||
|
}
|
@@ -15,6 +15,7 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/casbin/casdoor/cred"
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
"xorm.io/core"
|
"xorm.io/core"
|
||||||
)
|
)
|
||||||
@@ -24,13 +25,24 @@ type Organization struct {
|
|||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
|
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
|
||||||
Favicon string `xorm:"varchar(100)" json:"favicon"`
|
Favicon string `xorm:"varchar(100)" json:"favicon"`
|
||||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||||
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||||
PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"`
|
PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"`
|
||||||
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
|
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
|
||||||
|
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
|
||||||
|
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOrganizationCount(owner string) int {
|
||||||
|
count, err := adapter.Engine.Count(&Organization{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetOrganizations(owner string) []*Organization {
|
func GetOrganizations(owner string) []*Organization {
|
||||||
@@ -43,6 +55,16 @@ func GetOrganizations(owner string) []*Organization {
|
|||||||
return organizations
|
return organizations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetPaginationOrganizations(owner string, offset, limit int) []*Organization {
|
||||||
|
organizations := []*Organization{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Limit(limit, offset).Find(&organizations, &Provider{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return organizations
|
||||||
|
}
|
||||||
|
|
||||||
func getOrganization(owner string, name string) *Organization {
|
func getOrganization(owner string, name string) *Organization {
|
||||||
if owner == "" || name == "" {
|
if owner == "" || name == "" {
|
||||||
return nil
|
return nil
|
||||||
@@ -57,7 +79,7 @@ func getOrganization(owner string, name string) *Organization {
|
|||||||
if existed {
|
if existed {
|
||||||
return &organization
|
return &organization
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,12 +88,50 @@ func GetOrganization(id string) *Organization {
|
|||||||
return getOrganization(owner, name)
|
return getOrganization(owner, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetMaskedOrganization(organization *Organization) *Organization {
|
||||||
|
if organization == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if organization.MasterPassword != "" {
|
||||||
|
organization.MasterPassword = "***"
|
||||||
|
}
|
||||||
|
return organization
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMaskedOrganizations(organizations []*Organization) []*Organization {
|
||||||
|
for _, organization := range organizations {
|
||||||
|
organization = GetMaskedOrganization(organization)
|
||||||
|
}
|
||||||
|
return organizations
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateOrganization(id string, organization *Organization) bool {
|
func UpdateOrganization(id string, organization *Organization) bool {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
if getOrganization(owner, name) == nil {
|
if getOrganization(owner, name) == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if name == "built-in" {
|
||||||
|
organization.Name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != organization.Name {
|
||||||
|
applications := getApplicationsByOrganizationName("admin", name)
|
||||||
|
for _, application := range applications {
|
||||||
|
application.Organization = organization.Name
|
||||||
|
UpdateApplication(application.GetId(), application)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if organization.MasterPassword != "" {
|
||||||
|
credManager := cred.GetCredManager(organization.PasswordType)
|
||||||
|
if credManager != nil {
|
||||||
|
hashedPassword := credManager.GetHashedPassword(organization.MasterPassword, "", organization.PasswordSalt)
|
||||||
|
organization.MasterPassword = hashedPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(organization)
|
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(organization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -90,6 +150,10 @@ func AddOrganization(organization *Organization) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DeleteOrganization(organization *Organization) bool {
|
func DeleteOrganization(organization *Organization) bool {
|
||||||
|
if organization.Name == "built-in" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := adapter.Engine.ID(core.PK{organization.Owner, organization.Name}).Delete(&Organization{})
|
affected, err := adapter.Engine.ID(core.PK{organization.Owner, organization.Name}).Delete(&Organization{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
package object
|
|
||||||
|
|
||||||
type PayItem struct {
|
|
||||||
Invoice string `json:"invoice"`
|
|
||||||
Price string `json:"price"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Currency string `json:"currency"`
|
|
||||||
}
|
|
@@ -1,74 +0,0 @@
|
|||||||
package object
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/plutov/paypal/v4"
|
|
||||||
"xorm.io/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Payment struct {
|
|
||||||
Id string `xorm:"varchar(100) notnull pk" json:"id"`
|
|
||||||
Invoice string `xorm:"varchar(100)" json:"invoice"`
|
|
||||||
Application string `xorm:"varchar(100)" json:"application"`
|
|
||||||
PayItem PayItem `xorm:"json varchar(1000)" json:"pay_item"`
|
|
||||||
Payer *paypal.PayerWithNameAndPhone `xorm:"json varchar(1000)" json:"payer"`
|
|
||||||
Purchase []paypal.CapturedPurchaseUnit `xorm:"varchar(10000)" json:"purchase"`
|
|
||||||
Status string `xorm:"varchar(100)" json:"status"`
|
|
||||||
CreateTime string `xorm:"varchar(100) created" json:"create_time"`
|
|
||||||
UpdateTime string `xorm:"varchar(100) updated" json:"update_time"`
|
|
||||||
Callback string `xorm:"varchar(1000)" json:"callback"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddPayment(pay *Payment) bool {
|
|
||||||
affected, err := adapter.Engine.Insert(pay)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return affected != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPayments() []*Payment {
|
|
||||||
pays := []*Payment{}
|
|
||||||
err := adapter.Engine.Desc("create_time").Find(&pays)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return pays
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPayment(id string) *Payment {
|
|
||||||
pay := Payment{Id: id}
|
|
||||||
existed, err := adapter.Engine.Get(&pay)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if existed {
|
|
||||||
return &pay
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeletePayment(payment *Payment) bool {
|
|
||||||
affected, err := adapter.Engine.ID(core.PK{payment.Id}).Delete(&Payment{})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return affected != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdatePay(id string, pay *Payment) bool {
|
|
||||||
if GetPayment(id) == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
affected, err := adapter.Engine.ID(core.PK{id}).AllCols().Update(pay)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return affected != 0
|
|
||||||
}
|
|
@@ -26,12 +26,14 @@ type Provider struct {
|
|||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
Category string `xorm:"varchar(100)" json:"category"`
|
Category string `xorm:"varchar(100)" json:"category"`
|
||||||
Type string `xorm:"varchar(100)" json:"type"`
|
Type string `xorm:"varchar(100)" json:"type"`
|
||||||
Method string `xorm:"varchar(100)" json:"method"`
|
Method string `xorm:"varchar(100)" json:"method"`
|
||||||
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
||||||
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
||||||
|
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
|
||||||
|
ClientSecret2 string `xorm:"varchar(100)" json:"clientSecret2"`
|
||||||
|
|
||||||
Host string `xorm:"varchar(100)" json:"host"`
|
Host string `xorm:"varchar(100)" json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
@@ -43,25 +45,48 @@ type Provider struct {
|
|||||||
TemplateCode string `xorm:"varchar(100)" json:"templateCode"`
|
TemplateCode string `xorm:"varchar(100)" json:"templateCode"`
|
||||||
AppId string `xorm:"varchar(100)" json:"appId"`
|
AppId string `xorm:"varchar(100)" json:"appId"`
|
||||||
|
|
||||||
Endpoint string `xorm:"varchar(100)" json:"endpoint"`
|
Endpoint string `xorm:"varchar(1000)" json:"endpoint"`
|
||||||
Domain string `xorm:"varchar(100)" json:"domain"`
|
IntranetEndpoint string `xorm:"varchar(100)" json:"intranetEndpoint"`
|
||||||
Bucket string `xorm:"varchar(100)" json:"bucket"`
|
Domain string `xorm:"varchar(100)" json:"domain"`
|
||||||
|
Bucket string `xorm:"varchar(100)" json:"bucket"`
|
||||||
|
|
||||||
|
Metadata string `xorm:"mediumtext" json:"metadata"`
|
||||||
|
IdP string `xorm:"mediumtext" json:"idP"`
|
||||||
|
IssuerUrl string `xorm:"varchar(100)" json:"issuerUrl"`
|
||||||
|
EnableSignAuthnRequest bool `json:"enableSignAuthnRequest"`
|
||||||
|
|
||||||
ProviderUrl string `xorm:"varchar(200)" json:"providerUrl"`
|
ProviderUrl string `xorm:"varchar(200)" json:"providerUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMaskedProvider(provider *Provider) *Provider {
|
func GetMaskedProvider(provider *Provider) *Provider {
|
||||||
p := &Provider{
|
if provider == nil {
|
||||||
Owner: provider.Owner,
|
return nil
|
||||||
Name: provider.Name,
|
|
||||||
CreatedTime: provider.CreatedTime,
|
|
||||||
DisplayName: provider.DisplayName,
|
|
||||||
Category: provider.Category,
|
|
||||||
Type: provider.Type,
|
|
||||||
Method: provider.Method,
|
|
||||||
ClientId: provider.ClientId,
|
|
||||||
}
|
}
|
||||||
return p
|
|
||||||
|
if provider.ClientSecret != "" {
|
||||||
|
provider.ClientSecret = "***"
|
||||||
|
}
|
||||||
|
if provider.ClientSecret2 != "" {
|
||||||
|
provider.ClientSecret2 = "***"
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMaskedProviders(providers []*Provider) []*Provider {
|
||||||
|
for _, provider := range providers {
|
||||||
|
provider = GetMaskedProvider(provider)
|
||||||
|
}
|
||||||
|
return providers
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProviderCount(owner string) int {
|
||||||
|
count, err := adapter.Engine.Count(&Provider{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetProviders(owner string) []*Provider {
|
func GetProviders(owner string) []*Provider {
|
||||||
@@ -74,6 +99,16 @@ func GetProviders(owner string) []*Provider {
|
|||||||
return providers
|
return providers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetPaginationProviders(owner string, offset, limit int) []*Provider {
|
||||||
|
providers := []*Provider{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Limit(limit, offset).Find(&providers, &Provider{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return providers
|
||||||
|
}
|
||||||
|
|
||||||
func getProvider(owner string, name string) *Provider {
|
func getProvider(owner string, name string) *Provider {
|
||||||
if owner == "" || name == "" {
|
if owner == "" || name == "" {
|
||||||
return nil
|
return nil
|
||||||
|
@@ -34,7 +34,7 @@ func (application *Application) GetProviderItem(providerName string) *ProviderIt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pi *ProviderItem) IsProviderVisible() bool {
|
func (pi *ProviderItem) IsProviderVisible() bool {
|
||||||
return pi.Provider.Category == "OAuth"
|
return pi.Provider.Category == "OAuth" || pi.Provider.Category == "SAML"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pi *ProviderItem) isProviderPrompted() bool {
|
func (pi *ProviderItem) isProviderPrompted() bool {
|
||||||
|
120
object/record.go
120
object/record.go
@@ -15,19 +15,83 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
"github.com/astaxie/beego/context"
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Records struct {
|
var logPostOnly bool
|
||||||
Id int `xorm:"int notnull pk autoincr" json:"id"`
|
|
||||||
Record util.Record `xorm:"extends"`
|
func init() {
|
||||||
|
var err error
|
||||||
|
logPostOnly, err = beego.AppConfig.Bool("logPostOnly")
|
||||||
|
if err != nil {
|
||||||
|
//panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddRecord(record *util.Record) bool {
|
type Record struct {
|
||||||
records := new(Records)
|
Id int `xorm:"int notnull pk autoincr" json:"id"`
|
||||||
records.Record = *record
|
|
||||||
|
|
||||||
affected, err := adapter.Engine.Insert(records)
|
Owner string `xorm:"varchar(100) index" json:"owner"`
|
||||||
|
Name string `xorm:"varchar(100) index" json:"name"`
|
||||||
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
|
||||||
|
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||||
|
ClientIp string `xorm:"varchar(100)" json:"clientIp"`
|
||||||
|
User string `xorm:"varchar(100)" json:"user"`
|
||||||
|
Method string `xorm:"varchar(100)" json:"method"`
|
||||||
|
RequestUri string `xorm:"varchar(1000)" json:"requestUri"`
|
||||||
|
Action string `xorm:"varchar(1000)" json:"action"`
|
||||||
|
|
||||||
|
IsTriggered bool `json:"isTriggered"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecord(ctx *context.Context) *Record {
|
||||||
|
ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1)
|
||||||
|
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
|
||||||
|
requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"})
|
||||||
|
if len(requestUri) > 1000 {
|
||||||
|
requestUri = requestUri[0:1000]
|
||||||
|
}
|
||||||
|
|
||||||
|
record := Record{
|
||||||
|
Name: util.GenerateId(),
|
||||||
|
CreatedTime: util.GetCurrentTime(),
|
||||||
|
ClientIp: ip,
|
||||||
|
User: "",
|
||||||
|
Method: ctx.Request.Method,
|
||||||
|
RequestUri: requestUri,
|
||||||
|
Action: action,
|
||||||
|
IsTriggered: false,
|
||||||
|
}
|
||||||
|
return &record
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddRecord(record *Record) bool {
|
||||||
|
if logPostOnly {
|
||||||
|
if record.Method == "GET" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if record.Organization == "app" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
record.Owner = record.Organization
|
||||||
|
|
||||||
|
errWebhook := SendWebhooks(record)
|
||||||
|
if errWebhook == nil {
|
||||||
|
record.IsTriggered = true
|
||||||
|
} else {
|
||||||
|
fmt.Println(errWebhook)
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := adapter.Engine.Insert(record)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -36,7 +100,7 @@ func AddRecord(record *util.Record) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetRecordCount() int {
|
func GetRecordCount() int {
|
||||||
count, err := adapter.Engine.Count(&Records{})
|
count, err := adapter.Engine.Count(&Record{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -44,8 +108,8 @@ func GetRecordCount() int {
|
|||||||
return int(count)
|
return int(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRecords() []*Records {
|
func GetRecords() []*Record {
|
||||||
records := []*Records{}
|
records := []*Record{}
|
||||||
err := adapter.Engine.Desc("id").Find(&records)
|
err := adapter.Engine.Desc("id").Find(&records)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -54,8 +118,18 @@ func GetRecords() []*Records {
|
|||||||
return records
|
return records
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRecordsByField(record *Records) []*Records {
|
func GetPaginationRecords(offset, limit int) []*Record {
|
||||||
records := []*Records{}
|
records := []*Record{}
|
||||||
|
err := adapter.Engine.Desc("id").Limit(limit, offset).Find(&records)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return records
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetRecordsByField(record *Record) []*Record {
|
||||||
|
records := []*Record{}
|
||||||
err := adapter.Engine.Find(&records, record)
|
err := adapter.Engine.Find(&records, record)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -63,3 +137,25 @@ func GetRecordsByField(record *Records) []*Records {
|
|||||||
|
|
||||||
return records
|
return records
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SendWebhooks(record *Record) error {
|
||||||
|
webhooks := getWebhooksByOrganization(record.Organization)
|
||||||
|
for _, webhook := range webhooks {
|
||||||
|
matched := false
|
||||||
|
for _, event := range webhook.Events {
|
||||||
|
if record.Action == event {
|
||||||
|
matched = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matched {
|
||||||
|
err := sendWebhook(webhook, record)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@@ -35,7 +35,17 @@ type Resource struct {
|
|||||||
FileType string `xorm:"varchar(100)" json:"fileType"`
|
FileType string `xorm:"varchar(100)" json:"fileType"`
|
||||||
FileFormat string `xorm:"varchar(100)" json:"fileFormat"`
|
FileFormat string `xorm:"varchar(100)" json:"fileFormat"`
|
||||||
FileSize int `json:"fileSize"`
|
FileSize int `json:"fileSize"`
|
||||||
Url string `xorm:"varchar(100)" json:"url"`
|
Url string `xorm:"varchar(1000)" json:"url"`
|
||||||
|
Description string `xorm:"varchar(1000)" json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetResourceCount(owner string, user string) int {
|
||||||
|
count, err := adapter.Engine.Count(&Resource{Owner: owner, User: user})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetResources(owner string, user string) []*Resource {
|
func GetResources(owner string, user string) []*Resource {
|
||||||
@@ -53,6 +63,21 @@ func GetResources(owner string, user string) []*Resource {
|
|||||||
return resources
|
return resources
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetPaginationResources(owner, user string, offset, limit int) []*Resource {
|
||||||
|
if owner == "built-in" {
|
||||||
|
owner = ""
|
||||||
|
user = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
resources := []*Resource{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Limit(limit, offset).Find(&resources, &Resource{Owner: owner, User: user})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resources
|
||||||
|
}
|
||||||
|
|
||||||
func getResource(owner string, name string) *Resource {
|
func getResource(owner string, name string) *Resource {
|
||||||
resource := Resource{Owner: owner, Name: name}
|
resource := Resource{Owner: owner, Name: name}
|
||||||
existed, err := adapter.Engine.Get(&resource)
|
existed, err := adapter.Engine.Get(&resource)
|
||||||
|
135
object/saml.go
Normal file
135
object/saml.go
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
saml2 "github.com/russellhaering/gosaml2"
|
||||||
|
dsig "github.com/russellhaering/goxmldsig"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseSamlResponse(samlResponse string, providerType string) (string, error) {
|
||||||
|
samlResponse, _ = url.QueryUnescape(samlResponse)
|
||||||
|
sp, err := buildSp(&Provider{Type: providerType}, samlResponse)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
assertionInfo, err := sp.RetrieveAssertionInfo(samlResponse)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return assertionInfo.NameID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateSamlLoginUrl(id, relayState string) (string, string, error) {
|
||||||
|
provider := GetProvider(id)
|
||||||
|
if provider.Category != "SAML" {
|
||||||
|
return "", "", fmt.Errorf("Provider %s's category is not SAML", provider.Name)
|
||||||
|
}
|
||||||
|
sp, err := buildSp(provider, "")
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
auth := ""
|
||||||
|
method := ""
|
||||||
|
if provider.EnableSignAuthnRequest {
|
||||||
|
post, err := sp.BuildAuthBodyPost(relayState)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
auth = string(post[:])
|
||||||
|
method = "POST"
|
||||||
|
} else {
|
||||||
|
auth, err = sp.BuildAuthURL(relayState)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
method = "GET"
|
||||||
|
}
|
||||||
|
return auth, method, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildSp(provider *Provider, samlResponse string) (*saml2.SAMLServiceProvider, error) {
|
||||||
|
certStore := dsig.MemoryX509CertificateStore{
|
||||||
|
Roots: []*x509.Certificate{},
|
||||||
|
}
|
||||||
|
origin := beego.AppConfig.String("origin")
|
||||||
|
certEncodedData := ""
|
||||||
|
if samlResponse != "" {
|
||||||
|
certEncodedData = parseSamlResponse(samlResponse, provider.Type)
|
||||||
|
} else if provider.IdP != "" {
|
||||||
|
certEncodedData = provider.IdP
|
||||||
|
}
|
||||||
|
certData, err := base64.StdEncoding.DecodeString(certEncodedData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
idpCert, err := x509.ParseCertificate(certData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certStore.Roots = append(certStore.Roots, idpCert)
|
||||||
|
sp := &saml2.SAMLServiceProvider{
|
||||||
|
ServiceProviderIssuer: fmt.Sprintf("%s/api/acs", origin),
|
||||||
|
AssertionConsumerServiceURL: fmt.Sprintf("%s/api/acs", origin),
|
||||||
|
IDPCertificateStore: &certStore,
|
||||||
|
SignAuthnRequests: false,
|
||||||
|
SPKeyStore: dsig.RandomKeyStoreForTest(),
|
||||||
|
}
|
||||||
|
if provider.Endpoint != "" {
|
||||||
|
sp.IdentityProviderSSOURL = provider.Endpoint
|
||||||
|
sp.IdentityProviderIssuer = provider.IssuerUrl
|
||||||
|
}
|
||||||
|
if provider.EnableSignAuthnRequest {
|
||||||
|
sp.SignAuthnRequests = true
|
||||||
|
sp.SPKeyStore = buildSpKeyStore()
|
||||||
|
}
|
||||||
|
return sp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSamlResponse(samlResponse string, providerType string) string {
|
||||||
|
de, err := base64.StdEncoding.DecodeString(samlResponse)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
deStr := strings.Replace(string(de), "\n", "", -1)
|
||||||
|
tagMap := map[string]string{
|
||||||
|
"Aliyun IDaaS": "ds",
|
||||||
|
"Keycloak": "dsig",
|
||||||
|
}
|
||||||
|
tag := tagMap[providerType]
|
||||||
|
expression := fmt.Sprintf("<%s:X509Certificate>([\\s\\S]*?)</%s:X509Certificate>", tag, tag)
|
||||||
|
res := regexp.MustCompile(expression).FindStringSubmatch(deStr)
|
||||||
|
return res[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildSpKeyStore() dsig.X509KeyStore {
|
||||||
|
keyPair, err := tls.LoadX509KeyPair("object/token_jwt_key.pem", "object/token_jwt_key.key")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &dsig.TLSCertKeyStore {
|
||||||
|
PrivateKey: keyPair.PrivateKey,
|
||||||
|
Certificate: keyPair.Certificate,
|
||||||
|
}
|
||||||
|
}
|
@@ -17,7 +17,7 @@ package object
|
|||||||
import "github.com/casdoor/go-sms-sender"
|
import "github.com/casdoor/go-sms-sender"
|
||||||
|
|
||||||
func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
|
func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
|
||||||
client, err := go_sms_sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.RegionId, provider.TemplateCode, provider.AppId)
|
client, err := go_sms_sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -19,10 +19,29 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego"
|
||||||
"github.com/casbin/casdoor/storage"
|
"github.com/casbin/casdoor/storage"
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var isCloudIntranet bool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
isCloudIntranet, err = beego.AppConfig.Bool("isCloudIntranet")
|
||||||
|
if err != nil {
|
||||||
|
//panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProviderEndpoint(provider *Provider) string {
|
||||||
|
endpoint := provider.Endpoint
|
||||||
|
if provider.IntranetEndpoint != "" && isCloudIntranet {
|
||||||
|
endpoint = provider.IntranetEndpoint
|
||||||
|
}
|
||||||
|
return endpoint
|
||||||
|
}
|
||||||
|
|
||||||
func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
||||||
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), fullFilePath)
|
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), fullFilePath)
|
||||||
|
|
||||||
@@ -46,8 +65,9 @@ func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
|
|||||||
return fileUrl, objectKey
|
return fileUrl, objectKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func UploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer) (string, string, error) {
|
func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer) (string, string, error) {
|
||||||
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
|
endpoint := getProviderEndpoint(provider)
|
||||||
|
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
|
||||||
if storageProvider == nil {
|
if storageProvider == nil {
|
||||||
return "", "", fmt.Errorf("the provider type: %s is not supported", provider.Type)
|
return "", "", fmt.Errorf("the provider type: %s is not supported", provider.Type)
|
||||||
}
|
}
|
||||||
@@ -67,8 +87,28 @@ func UploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
|
|||||||
return fileUrl, objectKey, nil
|
return fileUrl, objectKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UploadFileSafe(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer) (string, string, error) {
|
||||||
|
var fileUrl string
|
||||||
|
var objectKey string
|
||||||
|
var err error
|
||||||
|
times := 0
|
||||||
|
for {
|
||||||
|
fileUrl, objectKey, err = uploadFile(provider, fullFilePath, fileBuffer)
|
||||||
|
if err != nil {
|
||||||
|
times += 1
|
||||||
|
if times >= 5 {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileUrl, objectKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
func DeleteFile(provider *Provider, objectKey string) error {
|
func DeleteFile(provider *Provider, objectKey string) error {
|
||||||
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
|
endpoint := getProviderEndpoint(provider)
|
||||||
|
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
|
||||||
if storageProvider == nil {
|
if storageProvider == nil {
|
||||||
return fmt.Errorf("the provider type: %s is not supported", provider.Type)
|
return fmt.Errorf("the provider type: %s is not supported", provider.Type)
|
||||||
}
|
}
|
||||||
|
160
object/syncer.go
Normal file
160
object/syncer.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
|
"xorm.io/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TableColumn struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
CasdoorName string `json:"casdoorName"`
|
||||||
|
IsHashed bool `json:"isHashed"`
|
||||||
|
Values []string `json:"values"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Syncer 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"`
|
||||||
|
|
||||||
|
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||||
|
Type string `xorm:"varchar(100)" json:"type"`
|
||||||
|
|
||||||
|
Host string `xorm:"varchar(100)" json:"host"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
User string `xorm:"varchar(100)" json:"user"`
|
||||||
|
Password string `xorm:"varchar(100)" json:"password"`
|
||||||
|
Database string `xorm:"varchar(100)" json:"database"`
|
||||||
|
Table string `xorm:"varchar(100)" json:"table"`
|
||||||
|
TablePrimaryKey string `xorm:"varchar(100)" json:"tablePrimaryKey"`
|
||||||
|
TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"`
|
||||||
|
AffiliationTable string `xorm:"varchar(100)" json:"affiliationTable"`
|
||||||
|
AvatarBaseUrl string `xorm:"varchar(100)" json:"avatarBaseUrl"`
|
||||||
|
SyncInterval int `json:"syncInterval"`
|
||||||
|
IsEnabled bool `json:"isEnabled"`
|
||||||
|
|
||||||
|
Adapter *Adapter `xorm:"-" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSyncerCount(owner string) int {
|
||||||
|
count, err := adapter.Engine.Count(&Syncer{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSyncers(owner string) []*Syncer {
|
||||||
|
syncers := []*Syncer{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Find(&syncers, &Syncer{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return syncers
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaginationSyncers(owner string, offset, limit int) []*Syncer {
|
||||||
|
syncers := []*Syncer{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Limit(limit, offset).Find(&syncers, &Syncer{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return syncers
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSyncer(owner string, name string) *Syncer {
|
||||||
|
if owner == "" || name == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
syncer := Syncer{Owner: owner, Name: name}
|
||||||
|
existed, err := adapter.Engine.Get(&syncer)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if existed {
|
||||||
|
return &syncer
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSyncer(id string) *Syncer {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
return getSyncer(owner, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMaskedSyncer(syncer *Syncer) *Syncer {
|
||||||
|
if syncer == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if syncer.Password != "" {
|
||||||
|
syncer.Password = "***"
|
||||||
|
}
|
||||||
|
return syncer
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMaskedSyncers(syncers []*Syncer) []*Syncer {
|
||||||
|
for _, syncer := range syncers {
|
||||||
|
syncer = GetMaskedSyncer(syncer)
|
||||||
|
}
|
||||||
|
return syncers
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateSyncer(id string, syncer *Syncer) bool {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
if getSyncer(owner, name) == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(syncer)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSyncer(syncer *Syncer) bool {
|
||||||
|
affected, err := adapter.Engine.Insert(syncer)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteSyncer(syncer *Syncer) bool {
|
||||||
|
affected, err := adapter.Engine.ID(core.PK{syncer.Owner, syncer.Name}).Delete(&Syncer{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) GetId() string {
|
||||||
|
return fmt.Sprintf("%s/%s", syncer.Owner, syncer.Name)
|
||||||
|
}
|
@@ -12,20 +12,16 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package original
|
package object
|
||||||
|
|
||||||
type Affiliation struct {
|
type Affiliation struct {
|
||||||
Id int `xorm:"int notnull pk autoincr" json:"id"`
|
Id int `xorm:"int notnull pk autoincr" json:"id"`
|
||||||
Name string `xorm:"varchar(128)" json:"name"`
|
Name string `xorm:"varchar(128)" json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Affiliation) TableName() string {
|
func (syncer *Syncer) getAffiliations() []*Affiliation {
|
||||||
return affiliationTableName
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAffiliations() []*Affiliation {
|
|
||||||
affiliations := []*Affiliation{}
|
affiliations := []*Affiliation{}
|
||||||
err := adapter.Engine.Asc("id").Find(&affiliations)
|
err := syncer.Adapter.Engine.Table(syncer.AffiliationTable).Asc("id").Find(&affiliations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -33,8 +29,8 @@ func getAffiliations() []*Affiliation {
|
|||||||
return affiliations
|
return affiliations
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAffiliationMap() ([]*Affiliation, map[int]string) {
|
func (syncer *Syncer) getAffiliationMap() ([]*Affiliation, map[int]string) {
|
||||||
affiliations := getAffiliations()
|
affiliations := syncer.getAffiliations()
|
||||||
|
|
||||||
m := map[int]string{}
|
m := map[int]string{}
|
||||||
for _, affiliation := range affiliations {
|
for _, affiliation := range affiliations {
|
40
object/syncer_cron.go
Normal file
40
object/syncer_cron.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2021 The casbin 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/mileusna/crontab"
|
||||||
|
|
||||||
|
var cronMap map[string]*crontab.Crontab
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cronMap = map[string]*crontab.Crontab{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCrontab(name string) *crontab.Crontab {
|
||||||
|
ctab, ok := cronMap[name]
|
||||||
|
if !ok {
|
||||||
|
ctab = crontab.New()
|
||||||
|
cronMap[name] = ctab
|
||||||
|
}
|
||||||
|
return ctab
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearCrontab(name string) {
|
||||||
|
ctab, ok := cronMap[name]
|
||||||
|
if ok {
|
||||||
|
ctab.Clear()
|
||||||
|
delete(cronMap, name)
|
||||||
|
}
|
||||||
|
}
|
61
object/syncer_public_api.go
Normal file
61
object/syncer_public_api.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2021 The casbin 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"
|
||||||
|
|
||||||
|
func getDbSyncerForUser(user *User) *Syncer {
|
||||||
|
syncers := GetSyncers("admin")
|
||||||
|
for _, syncer := range syncers {
|
||||||
|
if syncer.Organization == user.Owner && syncer.IsEnabled && syncer.Type == "Database" {
|
||||||
|
return syncer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnabledSyncerForOrganization(organization string) *Syncer {
|
||||||
|
syncers := GetSyncers("admin")
|
||||||
|
for _, syncer := range syncers {
|
||||||
|
if syncer.Organization == organization && syncer.IsEnabled {
|
||||||
|
return syncer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddUserToOriginalDatabase(user *User) {
|
||||||
|
syncer := getEnabledSyncerForOrganization(user.Owner)
|
||||||
|
if syncer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedOUser := syncer.createOriginalUserFromUser(user)
|
||||||
|
syncer.addUser(updatedOUser)
|
||||||
|
fmt.Printf("Add from user to oUser: %v\n", updatedOUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateUserToOriginalDatabase(user *User) {
|
||||||
|
syncer := getEnabledSyncerForOrganization(user.Owner)
|
||||||
|
if syncer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newUser := GetUser(user.GetId())
|
||||||
|
|
||||||
|
updatedOUser := syncer.createOriginalUserFromUser(newUser)
|
||||||
|
syncer.updateUser(updatedOUser)
|
||||||
|
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
|
||||||
|
}
|
83
object/syncer_sync.go
Normal file
83
object/syncer_sync.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright 2021 The casbin 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"
|
||||||
|
|
||||||
|
func (syncer *Syncer) syncUsers() {
|
||||||
|
fmt.Printf("Running syncUsers()..\n")
|
||||||
|
|
||||||
|
users, userMap := syncer.getUserMap()
|
||||||
|
oUsers, oUserMap := syncer.getOriginalUserMap()
|
||||||
|
fmt.Printf("Users: %d, oUsers: %d\n", len(users), len(oUsers))
|
||||||
|
|
||||||
|
var affiliationMap map[int]string
|
||||||
|
if syncer.AffiliationTable != "" {
|
||||||
|
_, affiliationMap = syncer.getAffiliationMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
newUsers := []*User{}
|
||||||
|
for _, oUser := range oUsers {
|
||||||
|
id := oUser.Id
|
||||||
|
if _, ok := userMap[id]; !ok {
|
||||||
|
newUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
|
||||||
|
fmt.Printf("New user: %v\n", newUser)
|
||||||
|
newUsers = append(newUsers, newUser)
|
||||||
|
} else {
|
||||||
|
user := userMap[id]
|
||||||
|
oHash := syncer.calculateHash(oUser)
|
||||||
|
|
||||||
|
if user.Hash == user.PreHash {
|
||||||
|
if user.Hash != oHash {
|
||||||
|
updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
|
||||||
|
updatedUser.Hash = oHash
|
||||||
|
updatedUser.PreHash = oHash
|
||||||
|
syncer.updateUserForOriginalFields(updatedUser)
|
||||||
|
fmt.Printf("Update from oUser to user: %v\n", updatedUser)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if user.PreHash == oHash {
|
||||||
|
updatedOUser := syncer.createOriginalUserFromUser(user)
|
||||||
|
syncer.updateUser(updatedOUser)
|
||||||
|
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
|
||||||
|
|
||||||
|
// update preHash
|
||||||
|
user.PreHash = user.Hash
|
||||||
|
SetUserField(user, "pre_hash", user.PreHash)
|
||||||
|
} else {
|
||||||
|
if user.Hash == oHash {
|
||||||
|
// update preHash
|
||||||
|
user.PreHash = user.Hash
|
||||||
|
SetUserField(user, "pre_hash", user.PreHash)
|
||||||
|
} else {
|
||||||
|
updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
|
||||||
|
updatedUser.Hash = oHash
|
||||||
|
updatedUser.PreHash = oHash
|
||||||
|
syncer.updateUserForOriginalFields(updatedUser)
|
||||||
|
fmt.Printf("Update from oUser to user (2nd condition): %v\n", updatedUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddUsersInBatch(newUsers)
|
||||||
|
|
||||||
|
for _, user := range users {
|
||||||
|
id := user.Id
|
||||||
|
if _, ok := oUserMap[id]; !ok {
|
||||||
|
panic(fmt.Sprintf("New original user: cannot create now, user = %v", user))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
154
object/syncer_user.go
Normal file
154
object/syncer_user.go
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
// Copyright 2021 The casbin 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"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/astaxie/beego"
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
|
"xorm.io/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OriginalUser = User
|
||||||
|
|
||||||
|
func (syncer *Syncer) getOriginalUsers() []*OriginalUser {
|
||||||
|
sql := fmt.Sprintf("select * from %s", syncer.Table)
|
||||||
|
results, err := syncer.Adapter.Engine.QueryString(sql)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return syncer.getOriginalUsersFromMap(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) getOriginalUserMap() ([]*OriginalUser, map[string]*OriginalUser) {
|
||||||
|
users := syncer.getOriginalUsers()
|
||||||
|
|
||||||
|
m := map[string]*OriginalUser{}
|
||||||
|
for _, user := range users {
|
||||||
|
m[user.Id] = user
|
||||||
|
}
|
||||||
|
return users, m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) addUser(user *OriginalUser) bool {
|
||||||
|
m := syncer.getMapFromOriginalUser(user)
|
||||||
|
affected, err := syncer.Adapter.Engine.Table(syncer.Table).Insert(m)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) getOriginalColumns() []string {
|
||||||
|
res := []string{}
|
||||||
|
for _, tableColumn := range syncer.TableColumns {
|
||||||
|
if tableColumn.CasdoorName != "Id" {
|
||||||
|
res = append(res, tableColumn.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) getCasdoorColumns() []string {
|
||||||
|
res := []string{}
|
||||||
|
for _, tableColumn := range syncer.TableColumns {
|
||||||
|
if tableColumn.CasdoorName != "Id" {
|
||||||
|
v := util.CamelToSnakeCase(tableColumn.CasdoorName)
|
||||||
|
res = append(res, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) updateUser(user *OriginalUser) bool {
|
||||||
|
m := syncer.getMapFromOriginalUser(user)
|
||||||
|
columns := syncer.getOriginalColumns()
|
||||||
|
affected, err := syncer.Adapter.Engine.Table(syncer.Table).ID(syncer.TablePrimaryKey).Cols(columns...).Update(m)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) updateUserForOriginalFields(user *User) bool {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(user.GetId())
|
||||||
|
oldUser := getUser(owner, name)
|
||||||
|
if oldUser == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Avatar != oldUser.Avatar && user.Avatar != "" {
|
||||||
|
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar)
|
||||||
|
}
|
||||||
|
|
||||||
|
columns := syncer.getCasdoorColumns()
|
||||||
|
columns = append(columns, "affiliation", "hash", "pre_hash")
|
||||||
|
affected, err := adapter.Engine.ID(core.PK{user.Owner, user.Name}).Cols(columns...).Update(user)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) calculateHash(user *OriginalUser) string {
|
||||||
|
values := []string{}
|
||||||
|
m := syncer.getMapFromOriginalUser(user)
|
||||||
|
for _, tableColumn := range syncer.TableColumns {
|
||||||
|
if tableColumn.IsHashed {
|
||||||
|
values = append(values, m[tableColumn.Name])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s := strings.Join(values, "|")
|
||||||
|
return util.GetMd5Hash(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) initAdapter() {
|
||||||
|
if syncer.Adapter == nil {
|
||||||
|
dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%d)/", syncer.User, syncer.Password, syncer.Host, syncer.Port)
|
||||||
|
syncer.Adapter = NewAdapter(beego.AppConfig.String("driverName"), dataSourceName, syncer.Database)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunSyncUsersJob() {
|
||||||
|
syncers := GetSyncers("admin")
|
||||||
|
for _, syncer := range syncers {
|
||||||
|
if !syncer.IsEnabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
syncer.initAdapter()
|
||||||
|
|
||||||
|
syncer.syncUsers()
|
||||||
|
|
||||||
|
// run at every minute
|
||||||
|
//schedule := fmt.Sprintf("* * * * %d", syncer.SyncInterval)
|
||||||
|
schedule := "* * * * *"
|
||||||
|
ctab := getCrontab(syncer.Name)
|
||||||
|
err := ctab.AddJob(schedule, syncer.syncUsers)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(time.Duration(1<<63 - 1))
|
||||||
|
}
|
@@ -12,38 +12,27 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package original
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/casbin/casdoor/object"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetUsers(t *testing.T) {
|
func TestGetUsers(t *testing.T) {
|
||||||
initConfig()
|
InitConfig()
|
||||||
initAdapter()
|
|
||||||
|
|
||||||
users := getUsersOriginal()
|
syncers := GetSyncers("admin")
|
||||||
|
syncer := syncers[0]
|
||||||
|
syncer.initAdapter()
|
||||||
|
users := syncer.getOriginalUsers()
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
fmt.Printf("%v\n", user)
|
fmt.Printf("%v\n", user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSyncUsers(t *testing.T) {
|
func TestSyncUsers(t *testing.T) {
|
||||||
initConfig()
|
InitConfig()
|
||||||
initAdapter()
|
|
||||||
object.InitAdapter()
|
|
||||||
|
|
||||||
syncUsers()
|
RunSyncUsersJob()
|
||||||
|
|
||||||
// run at every minute
|
|
||||||
schedule := "* * * * *"
|
|
||||||
err := ctab.AddJob(schedule, syncUsers)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
time.Sleep(time.Duration(1<<63 - 1))
|
|
||||||
}
|
}
|
213
object/syncer_util.go
Normal file
213
object/syncer_util.go
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
// Copyright 2021 The casbin 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"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (syncer *Syncer) getFullAvatarUrl(avatar string) string {
|
||||||
|
if syncer.AvatarBaseUrl == "" {
|
||||||
|
return avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(avatar, "https://") {
|
||||||
|
return fmt.Sprintf("%s%s", syncer.AvatarBaseUrl, avatar)
|
||||||
|
}
|
||||||
|
return avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) getPartialAvatarUrl(avatar string) string {
|
||||||
|
if strings.HasPrefix(avatar, syncer.AvatarBaseUrl) {
|
||||||
|
return avatar[len(syncer.AvatarBaseUrl):]
|
||||||
|
}
|
||||||
|
return avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) createUserFromOriginalUser(originalUser *OriginalUser, affiliationMap map[int]string) *User {
|
||||||
|
user := *originalUser
|
||||||
|
user.Owner = syncer.Organization
|
||||||
|
|
||||||
|
if user.Name == "" {
|
||||||
|
user.Name = originalUser.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.CreatedTime == "" {
|
||||||
|
user.CreatedTime = util.GetCurrentTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Type == "" {
|
||||||
|
user.Type = "normal-user"
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Avatar = syncer.getFullAvatarUrl(user.Avatar)
|
||||||
|
|
||||||
|
if affiliationMap != nil {
|
||||||
|
if originalUser.Score != 0 {
|
||||||
|
affiliation, ok := affiliationMap[originalUser.Score]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("Affiliation not found: %d", originalUser.Score))
|
||||||
|
}
|
||||||
|
user.Affiliation = affiliation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.Properties == nil {
|
||||||
|
user.Properties = map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) createOriginalUserFromUser(user *User) *OriginalUser {
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) setUserByKeyValue(user *User, key string, value string) {
|
||||||
|
switch key {
|
||||||
|
case "Name":
|
||||||
|
user.Name = value
|
||||||
|
case "CreatedTime":
|
||||||
|
user.CreatedTime = value
|
||||||
|
case "UpdatedTime":
|
||||||
|
user.UpdatedTime = value
|
||||||
|
case "Id":
|
||||||
|
user.Id = value
|
||||||
|
case "Type":
|
||||||
|
user.Type = value
|
||||||
|
case "Password":
|
||||||
|
user.Password = value
|
||||||
|
case "PasswordSalt":
|
||||||
|
user.PasswordSalt = value
|
||||||
|
case "DisplayName":
|
||||||
|
user.DisplayName = value
|
||||||
|
case "Avatar":
|
||||||
|
user.Avatar = syncer.getPartialAvatarUrl(value)
|
||||||
|
case "PermanentAvatar":
|
||||||
|
user.PermanentAvatar = value
|
||||||
|
case "Email":
|
||||||
|
user.Email = value
|
||||||
|
case "Phone":
|
||||||
|
user.Phone = value
|
||||||
|
case "Location":
|
||||||
|
user.Location = value
|
||||||
|
case "Address":
|
||||||
|
user.Address = []string{value}
|
||||||
|
case "Affiliation":
|
||||||
|
user.Affiliation = value
|
||||||
|
case "Title":
|
||||||
|
user.Title = value
|
||||||
|
case "IdCardType":
|
||||||
|
user.IdCardType = value
|
||||||
|
case "IdCard":
|
||||||
|
user.IdCard = value
|
||||||
|
case "Homepage":
|
||||||
|
user.Homepage = value
|
||||||
|
case "Bio":
|
||||||
|
user.Bio = value
|
||||||
|
case "Tag":
|
||||||
|
user.Tag = value
|
||||||
|
case "Region":
|
||||||
|
user.Region = value
|
||||||
|
case "Language":
|
||||||
|
user.Language = value
|
||||||
|
case "Gender":
|
||||||
|
user.Gender = value
|
||||||
|
case "Birthday":
|
||||||
|
user.Birthday = value
|
||||||
|
case "Education":
|
||||||
|
user.Education = value
|
||||||
|
case "Score":
|
||||||
|
user.Score = util.ParseInt(value)
|
||||||
|
case "Ranking":
|
||||||
|
user.Ranking = util.ParseInt(value)
|
||||||
|
case "IsDefaultAvatar":
|
||||||
|
user.IsDefaultAvatar = util.ParseBool(value)
|
||||||
|
case "IsOnline":
|
||||||
|
user.IsOnline = util.ParseBool(value)
|
||||||
|
case "IsAdmin":
|
||||||
|
user.IsAdmin = util.ParseBool(value)
|
||||||
|
case "IsGlobalAdmin":
|
||||||
|
user.IsGlobalAdmin = util.ParseBool(value)
|
||||||
|
case "IsForbidden":
|
||||||
|
user.IsForbidden = util.ParseBool(value)
|
||||||
|
case "IsDeleted":
|
||||||
|
user.IsDeleted = util.ParseBool(value)
|
||||||
|
case "CreatedIp":
|
||||||
|
user.CreatedIp = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) getOriginalUsersFromMap(results []map[string]string) []*OriginalUser {
|
||||||
|
users := []*OriginalUser{}
|
||||||
|
for _, result := range results {
|
||||||
|
originalUser := &OriginalUser{}
|
||||||
|
for _, tableColumn := range syncer.TableColumns {
|
||||||
|
syncer.setUserByKeyValue(originalUser, tableColumn.CasdoorName, result[tableColumn.Name])
|
||||||
|
}
|
||||||
|
users = append(users, originalUser)
|
||||||
|
}
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
func (syncer *Syncer) getMapFromOriginalUser(user *OriginalUser) map[string]string {
|
||||||
|
m := map[string]string{}
|
||||||
|
m["Name"] = user.Name
|
||||||
|
m["CreatedTime"] = user.CreatedTime
|
||||||
|
m["UpdatedTime"] = user.UpdatedTime
|
||||||
|
m["Id"] = user.Id
|
||||||
|
m["Type"] = user.Type
|
||||||
|
m["Password"] = user.Password
|
||||||
|
m["PasswordSalt"] = user.PasswordSalt
|
||||||
|
m["DisplayName"] = user.DisplayName
|
||||||
|
m["Avatar"] = syncer.getFullAvatarUrl(user.Avatar)
|
||||||
|
m["PermanentAvatar"] = user.PermanentAvatar
|
||||||
|
m["Email"] = user.Email
|
||||||
|
m["Phone"] = user.Phone
|
||||||
|
m["Location"] = user.Location
|
||||||
|
m["Address"] = strings.Join(user.Address, "|")
|
||||||
|
m["Affiliation"] = user.Affiliation
|
||||||
|
m["Title"] = user.Title
|
||||||
|
m["IdCardType"] = user.IdCardType
|
||||||
|
m["IdCard"] = user.IdCard
|
||||||
|
m["Homepage"] = user.Homepage
|
||||||
|
m["Bio"] = user.Bio
|
||||||
|
m["Tag"] = user.Tag
|
||||||
|
m["Region"] = user.Region
|
||||||
|
m["Language"] = user.Language
|
||||||
|
m["Gender"] = user.Gender
|
||||||
|
m["Birthday"] = user.Birthday
|
||||||
|
m["Education"] = user.Education
|
||||||
|
m["Score"] = strconv.Itoa(user.Score)
|
||||||
|
m["Ranking"] = strconv.Itoa(user.Ranking)
|
||||||
|
m["IsDefaultAvatar"] = util.BoolToString(user.IsDefaultAvatar)
|
||||||
|
m["IsOnline"] = util.BoolToString(user.IsOnline)
|
||||||
|
m["IsAdmin"] = util.BoolToString(user.IsAdmin)
|
||||||
|
m["IsGlobalAdmin"] = util.BoolToString(user.IsGlobalAdmin)
|
||||||
|
m["IsForbidden"] = util.BoolToString(user.IsForbidden)
|
||||||
|
m["IsDeleted"] = util.BoolToString(user.IsDeleted)
|
||||||
|
m["CreatedIp"] = user.CreatedIp
|
||||||
|
|
||||||
|
m2 := map[string]string{}
|
||||||
|
for _, tableColumn := range syncer.TableColumns {
|
||||||
|
m2[tableColumn.Name] = m[tableColumn.CasdoorName]
|
||||||
|
}
|
||||||
|
|
||||||
|
return m2
|
||||||
|
}
|
@@ -12,19 +12,17 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package original
|
package object
|
||||||
|
|
||||||
import "github.com/casbin/casdoor/object"
|
func (syncer *Syncer) getUsers() []*User {
|
||||||
|
users := GetUsers(syncer.Organization)
|
||||||
func getUsers() []*object.User {
|
|
||||||
users := object.GetUsers(orgName)
|
|
||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserMap() ([]*object.User, map[string]*object.User) {
|
func (syncer *Syncer) getUserMap() ([]*User, map[string]*User) {
|
||||||
users := getUsers()
|
users := syncer.getUsers()
|
||||||
|
|
||||||
m := map[string]*object.User{}
|
m := map[string]*User{}
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
m[user.Name] = user
|
m[user.Name] = user
|
||||||
}
|
}
|
121
object/token.go
121
object/token.go
@@ -15,7 +15,9 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/casbin/casdoor/util"
|
"github.com/casbin/casdoor/util"
|
||||||
"xorm.io/core"
|
"xorm.io/core"
|
||||||
@@ -35,20 +37,31 @@ type Token struct {
|
|||||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||||
User string `xorm:"varchar(100)" json:"user"`
|
User string `xorm:"varchar(100)" json:"user"`
|
||||||
|
|
||||||
Code string `xorm:"varchar(100)" json:"code"`
|
Code string `xorm:"varchar(100)" json:"code"`
|
||||||
AccessToken string `xorm:"mediumtext" json:"accessToken"`
|
AccessToken string `xorm:"mediumtext" json:"accessToken"`
|
||||||
ExpiresIn int `json:"expiresIn"`
|
RefreshToken string `xorm:"mediumtext" json:"refreshToken"`
|
||||||
Scope string `xorm:"varchar(100)" json:"scope"`
|
ExpiresIn int `json:"expiresIn"`
|
||||||
TokenType string `xorm:"varchar(100)" json:"tokenType"`
|
Scope string `xorm:"varchar(100)" json:"scope"`
|
||||||
|
TokenType string `xorm:"varchar(100)" json:"tokenType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenWrapper struct {
|
type TokenWrapper struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
|
IdToken string `json:"id_token"`
|
||||||
TokenType string `json:"token_type"`
|
TokenType string `json:"token_type"`
|
||||||
ExpiresIn int `json:"expires_in"`
|
ExpiresIn int `json:"expires_in"`
|
||||||
Scope string `json:"scope"`
|
Scope string `json:"scope"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetTokenCount(owner string) int {
|
||||||
|
count, err := adapter.Engine.Count(&Token{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
|
}
|
||||||
|
|
||||||
func GetTokens(owner string) []*Token {
|
func GetTokens(owner string) []*Token {
|
||||||
tokens := []*Token{}
|
tokens := []*Token{}
|
||||||
err := adapter.Engine.Desc("created_time").Find(&tokens, &Token{Owner: owner})
|
err := adapter.Engine.Desc("created_time").Find(&tokens, &Token{Owner: owner})
|
||||||
@@ -59,6 +72,16 @@ func GetTokens(owner string) []*Token {
|
|||||||
return tokens
|
return tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetPaginationTokens(owner string, offset, limit int) []*Token {
|
||||||
|
tokens := []*Token{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Limit(limit, offset).Find(&tokens, &Token{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
func getToken(owner string, name string) *Token {
|
func getToken(owner string, name string) *Token {
|
||||||
if owner == "" || name == "" {
|
if owner == "" || name == "" {
|
||||||
return nil
|
return nil
|
||||||
@@ -73,7 +96,7 @@ func getToken(owner string, name string) *Token {
|
|||||||
if existed {
|
if existed {
|
||||||
return &token
|
return &token
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +110,7 @@ func getTokenByCode(code string) *Token {
|
|||||||
if existed {
|
if existed {
|
||||||
return &token
|
return &token
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,17 +169,19 @@ func CheckOAuthLogin(clientId string, responseType string, redirectUri string, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !validUri {
|
if !validUri {
|
||||||
return "redirect_uri doesn't exist in the allowed Redirect URL list", application
|
return fmt.Sprintf("Redirect URI: \"%s\" doesn't exist in the allowed Redirect URI list", redirectUri), application
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mask application for /api/get-app-login
|
||||||
|
application.ClientSecret = ""
|
||||||
return "", application
|
return "", application
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetOAuthCode(userId string, clientId string, responseType string, redirectUri string, scope string, state string) *Code {
|
func GetOAuthCode(userId string, clientId string, responseType string, redirectUri string, scope string, state string, nonce string) *Code {
|
||||||
user := GetUser(userId)
|
user := GetUser(userId)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return &Code{
|
return &Code{
|
||||||
Message: "Invalid user_id",
|
Message: fmt.Sprintf("The user: %s doesn't exist", userId),
|
||||||
Code: "",
|
Code: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,7 +194,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accessToken, err := generateJwtToken(application, user)
|
accessToken, refreshToken, err := generateJwtToken(application, user, nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -183,6 +208,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
|
|||||||
User: user.Name,
|
User: user.Name,
|
||||||
Code: util.GenerateClientId(),
|
Code: util.GenerateClientId(),
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
|
RefreshToken: refreshToken,
|
||||||
ExpiresIn: application.ExpireInHours * 60,
|
ExpiresIn: application.ExpireInHours * 60,
|
||||||
Scope: scope,
|
Scope: scope,
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
@@ -254,6 +280,7 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
|
|||||||
|
|
||||||
tokenWrapper := &TokenWrapper{
|
tokenWrapper := &TokenWrapper{
|
||||||
AccessToken: token.AccessToken,
|
AccessToken: token.AccessToken,
|
||||||
|
IdToken: token.AccessToken,
|
||||||
TokenType: token.TokenType,
|
TokenType: token.TokenType,
|
||||||
ExpiresIn: token.ExpiresIn,
|
ExpiresIn: token.ExpiresIn,
|
||||||
Scope: token.Scope,
|
Scope: token.Scope,
|
||||||
@@ -261,3 +288,75 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
|
|||||||
|
|
||||||
return tokenWrapper
|
return tokenWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RefreshToken(grantType string, refreshToken string, scope string, clientId string, clientSecret string) *Code {
|
||||||
|
// check parameters
|
||||||
|
if grantType != "refresh_token" {
|
||||||
|
return &Code{
|
||||||
|
Message: "error: grant_type should be \"refresh_token\"",
|
||||||
|
Code: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
application := GetApplicationByClientId(clientId)
|
||||||
|
if application == nil {
|
||||||
|
return &Code{
|
||||||
|
Message: "error: invalid client_id",
|
||||||
|
Code: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if application.ClientSecret != clientSecret {
|
||||||
|
return &Code{
|
||||||
|
Message: "error: invalid client_secret",
|
||||||
|
Code: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check whether the refresh token is valid, and has not expired.
|
||||||
|
token := Token{RefreshToken: refreshToken}
|
||||||
|
existed, err := adapter.Engine.Get(&token)
|
||||||
|
if err != nil || !existed {
|
||||||
|
return &Code{
|
||||||
|
Message: "error: invalid refresh_token",
|
||||||
|
Code: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
claims, err := ParseJwtToken(refreshToken)
|
||||||
|
if err != nil {
|
||||||
|
return &Code{
|
||||||
|
Message: "error: invalid refresh_token",
|
||||||
|
Code: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if time.Now().Unix() > claims.ExpiresAt.Unix() {
|
||||||
|
return &Code{
|
||||||
|
Message: "error: expired refresh_token",
|
||||||
|
Code: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// generate a new token
|
||||||
|
user := getUser(application.Owner, token.User)
|
||||||
|
newAccessToken, newRefreshToken, err := generateJwtToken(application, user, "")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newToken := &Token{
|
||||||
|
Owner: application.Owner,
|
||||||
|
Name: util.GenerateId(),
|
||||||
|
CreatedTime: util.GetCurrentTime(),
|
||||||
|
Application: application.Name,
|
||||||
|
Organization: user.Owner,
|
||||||
|
User: user.Name,
|
||||||
|
Code: util.GenerateClientId(),
|
||||||
|
AccessToken: newAccessToken,
|
||||||
|
RefreshToken: newRefreshToken,
|
||||||
|
ExpiresIn: application.ExpireInHours * 60,
|
||||||
|
Scope: scope,
|
||||||
|
TokenType: "Bearer",
|
||||||
|
}
|
||||||
|
AddToken(newToken)
|
||||||
|
|
||||||
|
return &Code{
|
||||||
|
Message: "",
|
||||||
|
Code: token.Code,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -15,48 +15,92 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/astaxie/beego"
|
||||||
|
"github.com/golang-jwt/jwt/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
var jwtSecret = []byte("CasdoorSecret")
|
//go:embed token_jwt_key.pem
|
||||||
|
var tokenJwtPublicKey string
|
||||||
|
|
||||||
|
//go:embed token_jwt_key.key
|
||||||
|
var tokenJwtPrivateKey string
|
||||||
|
|
||||||
type Claims struct {
|
type Claims struct {
|
||||||
User
|
*User
|
||||||
jwt.StandardClaims
|
Name string `json:"name,omitempty"`
|
||||||
|
Owner string `json:"owner,omitempty"`
|
||||||
|
Nonce string `json:"nonce,omitempty"`
|
||||||
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateJwtToken(application *Application, user *User) (string, error) {
|
func generateJwtToken(application *Application, user *User, nonce string) (string, string, error) {
|
||||||
nowTime := time.Now()
|
nowTime := time.Now()
|
||||||
expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour)
|
expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour)
|
||||||
|
refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours) * time.Hour)
|
||||||
|
|
||||||
|
user.Password = ""
|
||||||
|
|
||||||
claims := Claims{
|
claims := Claims{
|
||||||
User: *user,
|
User: user,
|
||||||
StandardClaims: jwt.StandardClaims{
|
Nonce: nonce,
|
||||||
Audience: application.ClientId,
|
RegisteredClaims: jwt.RegisteredClaims{
|
||||||
ExpiresAt: expireTime.Unix(),
|
Issuer: beego.AppConfig.String("origin"),
|
||||||
Id: "",
|
|
||||||
IssuedAt: nowTime.Unix(),
|
|
||||||
Issuer: "casdoor",
|
|
||||||
NotBefore: nowTime.Unix(),
|
|
||||||
Subject: user.Id,
|
Subject: user.Id,
|
||||||
|
Audience: []string{application.ClientId},
|
||||||
|
ExpiresAt: jwt.NewNumericDate(expireTime),
|
||||||
|
NotBefore: jwt.NewNumericDate(nowTime),
|
||||||
|
IssuedAt: jwt.NewNumericDate(nowTime),
|
||||||
|
ID: "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
//all fields of the User struct are not added in "JWT-Empty" format
|
||||||
|
if application.TokenFormat == "JWT-Empty" {
|
||||||
|
claims.User = nil
|
||||||
|
}
|
||||||
|
claims.Name = user.Name
|
||||||
|
claims.Owner = user.Owner
|
||||||
|
|
||||||
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||||
token, err := tokenClaims.SignedString(jwtSecret)
|
claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
||||||
|
refreshToken := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||||
|
|
||||||
return token, err
|
// Use "token_jwt_key.key" as RSA private key
|
||||||
|
privateKey := tokenJwtPrivateKey
|
||||||
|
key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey))
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenString, err := token.SignedString(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
refreshTokenString, err := refreshToken.SignedString(key)
|
||||||
|
|
||||||
|
return tokenString, refreshTokenString, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseJwtToken(token string) (*Claims, error) {
|
func ParseJwtToken(token string) (*Claims, error) {
|
||||||
tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
t, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
return jwtSecret, nil
|
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use "token_jwt_key.pem" as RSA public key
|
||||||
|
publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(tokenJwtPublicKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return publicKey, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if tokenClaims != nil {
|
if t != nil {
|
||||||
if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
|
if claims, ok := t.Claims.(*Claims); ok && t.Valid {
|
||||||
return claims, nil
|
return claims, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
78
object/token_jwt_key.go
Normal file
78
object/token_jwt_key.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateRsaKeys(fileId string) {
|
||||||
|
// 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
|
||||||
|
|
||||||
|
bitSize := 4096
|
||||||
|
|
||||||
|
// Generate RSA key.
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, bitSize)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode private key to PKCS#1 ASN.1 PEM.
|
||||||
|
privateKeyPem := pem.EncodeToMemory(
|
||||||
|
&pem.Block{
|
||||||
|
Type: "PRIVATE KEY",
|
||||||
|
Bytes: x509.MarshalPKCS1PrivateKey(key),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
tml := x509.Certificate{
|
||||||
|
// you can add any attr that you need
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().AddDate(20, 0, 0),
|
||||||
|
// you have to generate a different serial number each execution
|
||||||
|
SerialNumber: big.NewInt(123456),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "Casdoor Cert",
|
||||||
|
Organization: []string{"Casdoor Organization"},
|
||||||
|
},
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a pem block with the certificate
|
||||||
|
certPem := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Bytes: cert,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Write private key to file.
|
||||||
|
util.WriteBytesToPath(privateKeyPem, fmt.Sprintf("%s.key", fileId))
|
||||||
|
|
||||||
|
// Write certificate (aka public key) to file.
|
||||||
|
util.WriteBytesToPath(certPem, fmt.Sprintf("%s.pem", fileId))
|
||||||
|
}
|
51
object/token_jwt_key.key
Normal file
51
object/token_jwt_key.key
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIJKQIBAAKCAgEAsInpb5E1/ym0f1RfSDSSE8IR7y+lw+RJjI74e5ejrq4b8zMY
|
||||||
|
k7HeHCyZr/hmNEwEVXnhXu1P0mBeQ5ypp/QGo8vgEmjAETNmzkI1NjOQCjCYwUra
|
||||||
|
sO/f/MnI1C0j13vx6mV1kHZjSrKsMhYY1vaxTEP3+VB8Hjg3MHFWrb07uvFMCJe5
|
||||||
|
W8+0rKErZCKTR8+9VB3janeBz//zQePFVh79bFZate/hLirPK0Go9P1gOvwIoC1A
|
||||||
|
3sarHTP4Qm/LQRt0rHqZFybdySpyWAQvhNaDFE7mTstRSBb/wUjNCUBDPTSLVjC0
|
||||||
|
4WllSf6Nkfx0Z7KvmbPstSj+btvcqsvRAGtvdsB9h62Kptjs1Yn7GAuoI3qt/4zo
|
||||||
|
KbiURYxkQJXIvwCQsEftUuk5ew5zuPSlDRLoLByQTLbx0JqLAFNfW3g/pzSDjgd/
|
||||||
|
60d6HTmvbZni4SmjdyFhXCDb1Kn7N+xTojnfaNkwep2REV+RMc0fx4GuhRsnLsmk
|
||||||
|
mUDeyIZ9aBL9oj11YEQfM2JZEq+RVtUx+wB4y8K/tD1bcY+IfnG5rBpwIDpS262b
|
||||||
|
oq4SRSvb3Z7bB0w4ZxvOfJ/1VLoRftjPbLIf0bhfr/AeZMHpIKOXvfz4yE+hqzi6
|
||||||
|
8wdF0VR9xYc/RbSAf7323OsjYnjjEgInUtRohnRgCpjIk/Mt2Kt84Kb0wn8CAwEA
|
||||||
|
AQKCAgAHP7JxHVJNRuYdcFZ1PYtd+lMIMjmpQH9woRI86O4UpxuIselpbx1CpOYu
|
||||||
|
npF7xj9LTzTc0/u6FLDqL82bkt6O7TknKFvymNy4zWkn75gTgwlSroMqTr8wvwxb
|
||||||
|
Aft9xp4ZVM8t/l53W7zMVbHxabHAAu50s0RVbVN+zriTa7i/JVdM5wX6ah3uFLQW
|
||||||
|
aYEIqtQIVy3WWk/fPZA8fWDF94HKaAVTgSUK40EccpbAcIL6CQ1FnnYSb6/pBBBG
|
||||||
|
khaTdtAkoOgWVkc3EmIdkRZuaux48gBs7dZJkoAv7JBWt+fK5JRwFpHmy5AYKLah
|
||||||
|
bu9Mrr6dHhEzIxrHbIm0DahoTwEFmso8kbU26caGEhufo4YiMk+B4bE6QsmNsNR9
|
||||||
|
Msau8qkSLprYo6Mrj1Q7y1q3rShf8SuZBa3Kxk0hBy8Zs9TjkJ+Dbtq2zakQWzDG
|
||||||
|
JLEttbGgdeYUMS2ycC/FUYUN/YPCdn769kw7lmOR2Kl56wpbFYwR9JYgynQqb3jd
|
||||||
|
4AORgsR3ADaxVDXw1ol7Bcie334WusvxNCJVRuzBY/DK0/W7ijxjJvdXevHxGrhe
|
||||||
|
1Gc+FkKebfiNfq6Dzdlkx66N80nyZuZvyrnRiavm9bVcrarb5XhS5ICfEOHOw0gH
|
||||||
|
5GdesqMuqTSOeveD1RUncF1CWWMvvPeEushW9jbL3BBh4wfLsQKCAQEAy4x+c+F8
|
||||||
|
IcbaKfssrhPRMfYUjWee39tOvHDtxM/3sx60ysrEUx1LsjagQz7noLljF7XfJ0H+
|
||||||
|
vc0G6A0ojwA6J+QdoEf8fevO4t56uMae3dFWP4J000vHCIvrgAo9GC2HOc940/Yn
|
||||||
|
6EqjWYqc2AXu156RA3P/XetgsxivVCFlGzPxFylbqzKB71Yb9hc5zz26G3K8+FVZ
|
||||||
|
dp+DYFjHBo6LRuVwXdXKi/QgUXv7iFR7zFqvkGhm5ZqvcKXfqWSpx4H71kQAUTQE
|
||||||
|
cJ8lvgquFTouViopOJD6DBII0PJ/TBtg5g9a+jsnXxcpCjj9XychGsj1dTzSDmQd
|
||||||
|
ha/rKyN4dNHy2QKCAQEA3gekrROPdglcooamedrvlwpIrxryKI5MrhEAgIBeJidp
|
||||||
|
98HhNcJ08wra5XuMS6ZC7R0xOKikSQLp4giT22W59lq/nPQ7PN5PdN2RzOlrmHcS
|
||||||
|
kAGF3Qg9x0cCelSqzyTl4RKrefYPLJUElVLmTxEgBiis7s2gxpsJ3q92WtWf8aU/
|
||||||
|
7Pc3ztAV/DqzeUCOACVz74l4QuE9LJlro0shs0TaHWM0AZ9eVk4V8xBM8VJ2DAX8
|
||||||
|
vpTYcNDxjEeByEMgWdXlyOndvolRDMCMrQMVg+ZoeX5SpDe2b6fgw5ZjZSngnrJ4
|
||||||
|
ifesFEbwmXmb3XDDNyWADG5/xAkpCGPCa34JmG7ZFwKCAQEAlsrrNx/ZnRA6uRUZ
|
||||||
|
wZBuzut1yFf2i/JlPxcOHlrPLwRVfVJ/5O70D/+F9KtaX2hXr84NloC+no+QSULO
|
||||||
|
RDov2zOUexQ5SnPyHYIiOlbyhHO7yGr17z7ZIUy+12k+X3YDEuHPqn9WizEYGJKm
|
||||||
|
pSaoDVasKXm6ujJQvf1Qjiv7Qg7V0YnTHl3ZgpwxNLt6GTyqbgEvW22nTEjZw/ug
|
||||||
|
3gulxIzfFLT4S3w8oQEPk6y61eZs37doWzqgM/y+WDh5ypJSJibUcVPu4hwUkthI
|
||||||
|
pPMoNq8fQIeupliJ7XlostIpk+XWSUCfZ0O6JJeZpO9RCA3OQd8f4odqk4qC1r99
|
||||||
|
UlXi6QKCAQEApSF+IpNXsWxJDz+h9SMV6nnlkQYzcGJVOWi/vNK8MxhBQdlajEcx
|
||||||
|
/8jlAKQgterT/9IkV4Vlmj+mf0vt29EOu+DGfg9PN3gIFFzuIT7BnUWB8sSPMNL+
|
||||||
|
T4XKm/z4hNNmfT0Ld8u/gWLbY8uiKtALx0jdRUZ9+vg4IPzSw7/6ExjaMH21bgVp
|
||||||
|
NIzcCqQueIFidpcBcIxgmRkJ6wrn55KfvheYCFTlLr8op/xJnXm8/jg9v+ioCU/9
|
||||||
|
Nl3AcpcqKmZhXkpBd4JdW2Shu9N9XvowXZvMDwK4ltZ+3jiteAHrY1xNNh+URgh0
|
||||||
|
zVCa0dkZ95vWXmiYcc52TB0V7ihxLoPSxQKCAQAwP3D0JyRvWepLasQd0NQeVt0q
|
||||||
|
0/ExXjq+qmIeT6q2GHhgZJjm6Ysovr+cEdZC6sV6JiqW9NhHfTYO4EkJetzlHMt5
|
||||||
|
jseFq14QMAgVuo7cYLkONgGxpYGo1DaddxlkMKpmpsTeFsvRyCnyWpHVG/sA1eVp
|
||||||
|
caanw6S2tnhxx2Yq78bv8Vj8jA0k8j34j2bMBFcuIEsaQ2Sdfw/1TSkKGB2k2crP
|
||||||
|
txbblVR4BN1DC8wpK/M67B097uMUmWe1UQxsjc0P9S/rlWUhKyBEeLOV3/yRPDSM
|
||||||
|
GMRXh780wMWlS34RUYxxv6dtWB9KI7++XRrnrBPrZa0xUUOpSYILm7OLeuQ5
|
||||||
|
-----END PRIVATE KEY-----
|
29
object/token_jwt_key.pem
Normal file
29
object/token_jwt_key.pem
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIE+TCCAuGgAwIBAgIDAeJAMA0GCSqGSIb3DQEBCwUAMDYxHTAbBgNVBAoTFENh
|
||||||
|
c2Rvb3IgT3JnYW5pemF0aW9uMRUwEwYDVQQDEwxDYXNkb29yIENlcnQwHhcNMjEx
|
||||||
|
MDE1MDgxMTUyWhcNNDExMDE1MDgxMTUyWjA2MR0wGwYDVQQKExRDYXNkb29yIE9y
|
||||||
|
Z2FuaXphdGlvbjEVMBMGA1UEAxMMQ2FzZG9vciBDZXJ0MIICIjANBgkqhkiG9w0B
|
||||||
|
AQEFAAOCAg8AMIICCgKCAgEAsInpb5E1/ym0f1RfSDSSE8IR7y+lw+RJjI74e5ej
|
||||||
|
rq4b8zMYk7HeHCyZr/hmNEwEVXnhXu1P0mBeQ5ypp/QGo8vgEmjAETNmzkI1NjOQ
|
||||||
|
CjCYwUrasO/f/MnI1C0j13vx6mV1kHZjSrKsMhYY1vaxTEP3+VB8Hjg3MHFWrb07
|
||||||
|
uvFMCJe5W8+0rKErZCKTR8+9VB3janeBz//zQePFVh79bFZate/hLirPK0Go9P1g
|
||||||
|
OvwIoC1A3sarHTP4Qm/LQRt0rHqZFybdySpyWAQvhNaDFE7mTstRSBb/wUjNCUBD
|
||||||
|
PTSLVjC04WllSf6Nkfx0Z7KvmbPstSj+btvcqsvRAGtvdsB9h62Kptjs1Yn7GAuo
|
||||||
|
I3qt/4zoKbiURYxkQJXIvwCQsEftUuk5ew5zuPSlDRLoLByQTLbx0JqLAFNfW3g/
|
||||||
|
pzSDjgd/60d6HTmvbZni4SmjdyFhXCDb1Kn7N+xTojnfaNkwep2REV+RMc0fx4Gu
|
||||||
|
hRsnLsmkmUDeyIZ9aBL9oj11YEQfM2JZEq+RVtUx+wB4y8K/tD1bcY+IfnG5rBpw
|
||||||
|
IDpS262boq4SRSvb3Z7bB0w4ZxvOfJ/1VLoRftjPbLIf0bhfr/AeZMHpIKOXvfz4
|
||||||
|
yE+hqzi68wdF0VR9xYc/RbSAf7323OsjYnjjEgInUtRohnRgCpjIk/Mt2Kt84Kb0
|
||||||
|
wn8CAwEAAaMQMA4wDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAn2lf
|
||||||
|
DKkLX+F1vKRO/5gJ+Plr8P5NKuQkmwH97b8CS2gS1phDyNgIc4/LSdzuf4Awe6ve
|
||||||
|
C06lVdWSIis8UPUPdjmT2uMPSNjwLxG3QsrimMURNwFlLTfRem/heJe0Zgur9J1M
|
||||||
|
8haawdSdJjH2RgmFoDeE2r8NVRfhbR8KnCO1ddTJKuS1N0/irHz21W4jt4rxzCvl
|
||||||
|
2nR42Fybap3O/g2JXMhNNROwZmNjgpsF7XVENCSuFO1jTywLaqjuXCg54IL7XVLG
|
||||||
|
omKNNNcc8h1FCeKj/nnbGMhodnFWKDTsJcbNmcOPNHo6ixzqMy/Hqc+mWYv7maAG
|
||||||
|
Jtevs3qgMZ8F9Qzr3HpUc6R3ZYYWDY/xxPisuKftOPZgtH979XC4mdf0WPnOBLqL
|
||||||
|
2DJ1zaBmjiGJolvb7XNVKcUfDXYw85ZTZQ5b9clI4e+6bmyWqQItlwt+Ati/uFEV
|
||||||
|
XzCj70B4lALX6xau1kLEpV9O1GERizYRz5P9NJNA7KoO5AVMp9w0DQTkt+LbXnZE
|
||||||
|
HHnWKy8xHQKZF9sR7YBPGLs/Ac6tviv5Ua15OgJ/8dLRZ/veyFfGo2yZsI+hKVU5
|
||||||
|
nCCJHBcAyFnm1hdvdwEdH33jDBjNB6ciotJZrf/3VYaIWSalADosHAgMWfXuWP+h
|
||||||
|
8XKXmzlxuHbTMQYtZPDgspS5aK+S4Q9wb8RRAYo=
|
||||||
|
-----END CERTIFICATE-----
|
@@ -12,12 +12,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package original
|
package object
|
||||||
|
|
||||||
import "github.com/mileusna/crontab"
|
import "testing"
|
||||||
|
|
||||||
var ctab *crontab.Crontab
|
func TestGenerateRsaKeys(t *testing.T) {
|
||||||
|
fileId := "token_jwt_key"
|
||||||
func init() {
|
generateRsaKeys(fileId)
|
||||||
ctab = crontab.New()
|
|
||||||
}
|
}
|
136
object/user.go
136
object/user.go
@@ -30,6 +30,7 @@ type User struct {
|
|||||||
Id string `xorm:"varchar(100)" json:"id"`
|
Id string `xorm:"varchar(100)" json:"id"`
|
||||||
Type string `xorm:"varchar(100)" json:"type"`
|
Type string `xorm:"varchar(100)" json:"type"`
|
||||||
Password string `xorm:"varchar(100)" json:"password"`
|
Password string `xorm:"varchar(100)" json:"password"`
|
||||||
|
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
Avatar string `xorm:"varchar(255)" json:"avatar"`
|
Avatar string `xorm:"varchar(255)" json:"avatar"`
|
||||||
PermanentAvatar string `xorm:"varchar(255)" json:"permanentAvatar"`
|
PermanentAvatar string `xorm:"varchar(255)" json:"permanentAvatar"`
|
||||||
@@ -39,21 +40,32 @@ type User struct {
|
|||||||
Address []string `json:"address"`
|
Address []string `json:"address"`
|
||||||
Affiliation string `xorm:"varchar(100)" json:"affiliation"`
|
Affiliation string `xorm:"varchar(100)" json:"affiliation"`
|
||||||
Title string `xorm:"varchar(100)" json:"title"`
|
Title string `xorm:"varchar(100)" json:"title"`
|
||||||
|
IdCardType string `xorm:"varchar(100)" json:"idCardType"`
|
||||||
|
IdCard string `xorm:"varchar(100)" json:"idCard"`
|
||||||
Homepage string `xorm:"varchar(100)" json:"homepage"`
|
Homepage string `xorm:"varchar(100)" json:"homepage"`
|
||||||
Bio string `xorm:"varchar(100)" json:"bio"`
|
Bio string `xorm:"varchar(100)" json:"bio"`
|
||||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||||
Region string `xorm:"varchar(100)" json:"region"`
|
Region string `xorm:"varchar(100)" json:"region"`
|
||||||
Language string `xorm:"varchar(100)" json:"language"`
|
Language string `xorm:"varchar(100)" json:"language"`
|
||||||
|
Gender string `xorm:"varchar(100)" json:"gender"`
|
||||||
|
Birthday string `xorm:"varchar(100)" json:"birthday"`
|
||||||
|
Education string `xorm:"varchar(100)" json:"education"`
|
||||||
Score int `json:"score"`
|
Score int `json:"score"`
|
||||||
Ranking int `json:"ranking"`
|
Ranking int `json:"ranking"`
|
||||||
|
IsDefaultAvatar bool `json:"isDefaultAvatar"`
|
||||||
IsOnline bool `json:"isOnline"`
|
IsOnline bool `json:"isOnline"`
|
||||||
IsAdmin bool `json:"isAdmin"`
|
IsAdmin bool `json:"isAdmin"`
|
||||||
IsGlobalAdmin bool `json:"isGlobalAdmin"`
|
IsGlobalAdmin bool `json:"isGlobalAdmin"`
|
||||||
IsForbidden bool `json:"isForbidden"`
|
IsForbidden bool `json:"isForbidden"`
|
||||||
|
IsDeleted bool `json:"isDeleted"`
|
||||||
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
|
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
|
||||||
Hash string `xorm:"varchar(100)" json:"hash"`
|
Hash string `xorm:"varchar(100)" json:"hash"`
|
||||||
PreHash string `xorm:"varchar(100)" json:"preHash"`
|
PreHash string `xorm:"varchar(100)" json:"preHash"`
|
||||||
|
|
||||||
|
CreatedIp string `xorm:"varchar(100)" json:"createdIp"`
|
||||||
|
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
|
||||||
|
LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"`
|
||||||
|
|
||||||
Github string `xorm:"varchar(100)" json:"github"`
|
Github string `xorm:"varchar(100)" json:"github"`
|
||||||
Google string `xorm:"varchar(100)" json:"google"`
|
Google string `xorm:"varchar(100)" json:"google"`
|
||||||
QQ string `xorm:"qq varchar(100)" json:"qq"`
|
QQ string `xorm:"qq varchar(100)" json:"qq"`
|
||||||
@@ -66,11 +78,23 @@ type User struct {
|
|||||||
Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
|
Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
|
||||||
Lark string `xorm:"lark varchar(100)" json:"lark"`
|
Lark string `xorm:"lark varchar(100)" json:"lark"`
|
||||||
Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"`
|
Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"`
|
||||||
|
Apple string `xorm:"apple varchar(100)" json:"apple"`
|
||||||
|
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
|
||||||
|
Slack string `xorm:"slack varchar(100)" json:"slack"`
|
||||||
|
|
||||||
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
|
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
|
||||||
Properties map[string]string `json:"properties"`
|
Properties map[string]string `json:"properties"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetGlobalUserCount() int {
|
||||||
|
count, err := adapter.Engine.Count(&User{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
|
}
|
||||||
|
|
||||||
func GetGlobalUsers() []*User {
|
func GetGlobalUsers() []*User {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
err := adapter.Engine.Desc("created_time").Find(&users)
|
err := adapter.Engine.Desc("created_time").Find(&users)
|
||||||
@@ -81,6 +105,34 @@ func GetGlobalUsers() []*User {
|
|||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetPaginationGlobalUsers(offset, limit int) []*User {
|
||||||
|
users := []*User{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Limit(limit, offset).Find(&users)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUserCount(owner string) int {
|
||||||
|
count, err := adapter.Engine.Count(&User{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOnlineUserCount(owner string, isOnline int) int {
|
||||||
|
count, err := adapter.Engine.Where("is_online = ?", isOnline).Count(&User{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
|
}
|
||||||
|
|
||||||
func GetUsers(owner string) []*User {
|
func GetUsers(owner string) []*User {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
err := adapter.Engine.Desc("created_time").Find(&users, &User{Owner: owner})
|
err := adapter.Engine.Desc("created_time").Find(&users, &User{Owner: owner})
|
||||||
@@ -91,6 +143,26 @@ func GetUsers(owner string) []*User {
|
|||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSortedUsers(owner string, sorter string, limit int) []*User {
|
||||||
|
users := []*User{}
|
||||||
|
err := adapter.Engine.Desc(sorter).Limit(limit, 0).Find(&users, &User{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaginationUsers(owner string, offset, limit int) []*User {
|
||||||
|
users := []*User{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Limit(limit, offset).Find(&users, &User{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
func getUser(owner string, name string) *User {
|
func getUser(owner string, name string) *User {
|
||||||
if owner == "" || name == "" {
|
if owner == "" || name == "" {
|
||||||
return nil
|
return nil
|
||||||
@@ -109,11 +181,34 @@ func getUser(owner string, name string) *User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUserByEmail(owner string, email string) *User {
|
||||||
|
if owner == "" || email == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
user := User{Owner: owner, Email: email}
|
||||||
|
existed, err := adapter.Engine.Get(&user)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if existed {
|
||||||
|
return &user
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func GetUser(id string) *User {
|
func GetUser(id string) *User {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
return getUser(owner, name)
|
return getUser(owner, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUserNoCheck(id string) *User {
|
||||||
|
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
|
||||||
|
return getUser(owner, name)
|
||||||
|
}
|
||||||
|
|
||||||
func GetMaskedUser(user *User) *User {
|
func GetMaskedUser(user *User) *User {
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -146,8 +241,8 @@ func GetLastUser(owner string) *User {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateUser(id string, user *User) bool {
|
func UpdateUser(id string, user *User, columns []string) bool {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
|
||||||
oldUser := getUser(owner, name)
|
oldUser := getUser(owner, name)
|
||||||
if oldUser == nil {
|
if oldUser == nil {
|
||||||
return false
|
return false
|
||||||
@@ -155,13 +250,17 @@ func UpdateUser(id string, user *User) bool {
|
|||||||
|
|
||||||
user.UpdateUserHash()
|
user.UpdateUserHash()
|
||||||
|
|
||||||
if user.Avatar != oldUser.Avatar && user.Avatar != "" {
|
if user.Avatar != oldUser.Avatar && user.Avatar != "" && user.PermanentAvatar != "*" {
|
||||||
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar)
|
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar)
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols("owner", "display_name", "avatar",
|
if len(columns) == 0 {
|
||||||
"location", "address", "region", "language", "affiliation", "title", "homepage", "bio", "score", "tag", "is_admin", "is_global_admin", "is_forbidden",
|
columns = []string{"owner", "display_name", "avatar",
|
||||||
"hash", "properties").Update(user)
|
"location", "address", "region", "language", "affiliation", "title", "homepage", "bio", "score", "tag",
|
||||||
|
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties"}
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols(columns...).Update(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -190,30 +289,15 @@ func UpdateUserForAllFields(id string, user *User) bool {
|
|||||||
return affected != 0
|
return affected != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateUserForOriginalFields(user *User) bool {
|
|
||||||
owner, name := util.GetOwnerAndNameFromId(user.GetId())
|
|
||||||
oldUser := getUser(owner, name)
|
|
||||||
if oldUser == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.Avatar != oldUser.Avatar && user.Avatar != "" {
|
|
||||||
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar)
|
|
||||||
}
|
|
||||||
|
|
||||||
affected, err := adapter.Engine.ID(core.PK{user.Owner, user.Name}).Cols("display_name", "password", "phone", "avatar", "affiliation", "score", "is_forbidden", "hash", "pre_hash").Update(user)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return affected != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddUser(user *User) bool {
|
func AddUser(user *User) bool {
|
||||||
if user.Id == "" {
|
if user.Id == "" {
|
||||||
user.Id = util.GenerateId()
|
user.Id = util.GenerateId()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user.Owner == "" || user.Name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
organization := GetOrganizationByUser(user)
|
organization := GetOrganizationByUser(user)
|
||||||
user.UpdateUserPassword(organization)
|
user.UpdateUserPassword(organization)
|
||||||
|
|
||||||
@@ -253,7 +337,7 @@ func AddUsers(users []*User) bool {
|
|||||||
return affected != 0
|
return affected != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddUsersSafe(users []*User) bool {
|
func AddUsersInBatch(users []*User) bool {
|
||||||
batchSize := 1000
|
batchSize := 1000
|
||||||
|
|
||||||
if len(users) == 0 {
|
if len(users) == 0 {
|
||||||
|
@@ -14,16 +14,15 @@
|
|||||||
|
|
||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import "github.com/casbin/casdoor/cred"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/casbin/casdoor/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
func calculateHash(user *User) string {
|
func calculateHash(user *User) string {
|
||||||
s := strings.Join([]string{user.Id, user.Password, user.DisplayName, user.Avatar, user.Phone, strconv.Itoa(user.Score)}, "|")
|
syncer := getDbSyncerForUser(user)
|
||||||
return util.GetMd5Hash(s)
|
if syncer == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return syncer.calculateHash(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) UpdateUserHash() {
|
func (user *User) UpdateUserHash() {
|
||||||
@@ -32,7 +31,9 @@ func (user *User) UpdateUserHash() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) UpdateUserPassword(organization *Organization) {
|
func (user *User) UpdateUserPassword(organization *Organization) {
|
||||||
if organization.PasswordType == "salt" {
|
credManager := cred.GetCredManager(organization.PasswordType)
|
||||||
user.Password = getSaltedPassword(user.Password, organization.PasswordSalt)
|
if credManager != nil {
|
||||||
|
hashedPassword := credManager.GetHashedPassword(user.Password, user.PasswordSalt, organization.PasswordSalt)
|
||||||
|
user.Password = hashedPassword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -74,12 +74,6 @@ func TestSyncHashes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSaltedPassword(t *testing.T) {
|
|
||||||
password := "123456"
|
|
||||||
salt := "123"
|
|
||||||
fmt.Printf("%s -> %s\n", password, getSaltedPassword(password, salt))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetMaskedUsers(t *testing.T) {
|
func TestGetMaskedUsers(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
users []*User
|
users []*User
|
||||||
|
@@ -68,7 +68,7 @@ func SendVerificationCodeToPhone(organization *Organization, user *User, provide
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return SendSms(provider, dest, code)
|
return SendSms(provider, code, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordType, dest, code string) error {
|
func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordType, dest, code string) error {
|
||||||
@@ -112,7 +112,7 @@ func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordT
|
|||||||
func getVerificationRecord(dest string) *VerificationRecord {
|
func getVerificationRecord(dest string) *VerificationRecord {
|
||||||
var record VerificationRecord
|
var record VerificationRecord
|
||||||
record.Receiver = dest
|
record.Receiver = dest
|
||||||
has, err := adapter.Engine.Desc("time").Where("is_used = 0").Get(&record)
|
has, err := adapter.Engine.Desc("time").Where("is_used = false").Get(&record)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
132
object/webhook.go
Normal file
132
object/webhook.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
|
"xorm.io/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Webhook 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"`
|
||||||
|
|
||||||
|
Url string `xorm:"varchar(100)" json:"url"`
|
||||||
|
ContentType string `xorm:"varchar(100)" json:"contentType"`
|
||||||
|
Events []string `xorm:"varchar(100)" json:"events"`
|
||||||
|
|
||||||
|
Organization string `xorm:"varchar(100) index" json:"organization"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWebhookCount(owner string) int {
|
||||||
|
count, err := adapter.Engine.Count(&Webhook{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWebhooks(owner string) []*Webhook {
|
||||||
|
webhooks := []*Webhook{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Find(&webhooks, &Webhook{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return webhooks
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaginationWebhooks(owner string, offset, limit int) []*Webhook {
|
||||||
|
webhooks := []*Webhook{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Limit(limit, offset).Find(&webhooks, &Webhook{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return webhooks
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWebhooksByOrganization(organization string) []*Webhook {
|
||||||
|
webhooks := []*Webhook{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Find(&webhooks, &Webhook{Organization: organization})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return webhooks
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWebhook(owner string, name string) *Webhook {
|
||||||
|
if owner == "" || name == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
webhook := Webhook{Owner: owner, Name: name}
|
||||||
|
existed, err := adapter.Engine.Get(&webhook)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if existed {
|
||||||
|
return &webhook
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWebhook(id string) *Webhook {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
return getWebhook(owner, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateWebhook(id string, webhook *Webhook) bool {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
if getWebhook(owner, name) == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(webhook)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddWebhook(webhook *Webhook) bool {
|
||||||
|
affected, err := adapter.Engine.Insert(webhook)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteWebhook(webhook *Webhook) bool {
|
||||||
|
affected, err := adapter.Engine.ID(core.PK{webhook.Owner, webhook.Name}).Delete(&Webhook{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Webhook) GetId() string {
|
||||||
|
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
||||||
|
}
|
@@ -15,23 +15,24 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"net/http"
|
||||||
"encoding/hex"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getSha256(data []byte) []byte {
|
func sendWebhook(webhook *Webhook, record *Record) error {
|
||||||
hash := sha256.Sum256(data)
|
client := &http.Client{}
|
||||||
return hash[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSha256HexDigest(s string) string {
|
body := strings.NewReader(util.StructToJson(record))
|
||||||
b := getSha256([]byte(s))
|
|
||||||
res := hex.EncodeToString(b)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSaltedPassword(password string, salt string) string {
|
req, err := http.NewRequest("POST", webhook.Url, body)
|
||||||
hash1 := getSha256HexDigest(password)
|
if err != nil {
|
||||||
res := getSha256HexDigest(hash1 + salt)
|
return err
|
||||||
return res
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", webhook.ContentType)
|
||||||
|
|
||||||
|
_, err = client.Do(req)
|
||||||
|
return err
|
||||||
}
|
}
|
@@ -1,50 +0,0 @@
|
|||||||
// Copyright 2021 The casbin 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 original
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
"github.com/casbin/casdoor/object"
|
|
||||||
_ "github.com/go-sql-driver/mysql" // db = mysql
|
|
||||||
//_ "github.com/lib/pq" // db = postgres
|
|
||||||
)
|
|
||||||
|
|
||||||
var adapter *object.Adapter
|
|
||||||
|
|
||||||
func initConfig() {
|
|
||||||
err := beego.LoadAppConfig("ini", "../conf/app.conf")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
initAdapter()
|
|
||||||
}
|
|
||||||
|
|
||||||
func initAdapter() {
|
|
||||||
if dbName == "dbName" {
|
|
||||||
adapter = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
adapter = object.NewAdapter(beego.AppConfig.String("driverName"), beego.AppConfig.String("dataSourceName"), dbName)
|
|
||||||
createTable(adapter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTable(a *object.Adapter) {
|
|
||||||
err := a.Engine.Sync2(new(User))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,59 +0,0 @@
|
|||||||
// Copyright 2021 The casbin 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 original
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/casbin/casdoor/object"
|
|
||||||
)
|
|
||||||
|
|
||||||
func isEnabled() bool {
|
|
||||||
if adapter == nil {
|
|
||||||
initAdapter()
|
|
||||||
if adapter == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddUserToOriginalDatabase(user *object.User) {
|
|
||||||
if user.Owner != orgName {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isEnabled() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedOUser := createOriginalUserFromUser(user)
|
|
||||||
addUser(updatedOUser)
|
|
||||||
fmt.Printf("Add from user to oUser: %v\n", updatedOUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateUserToOriginalDatabase(user *object.User) {
|
|
||||||
if user.Owner != orgName {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isEnabled() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedOUser := createOriginalUserFromUser(user)
|
|
||||||
updateUser(updatedOUser)
|
|
||||||
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
|
|
||||||
}
|
|
151
original/sync.go
151
original/sync.go
@@ -1,151 +0,0 @@
|
|||||||
// Copyright 2021 The casbin 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 original
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/casbin/casdoor/object"
|
|
||||||
"github.com/casbin/casdoor/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getFullAvatarUrl(avatar string) string {
|
|
||||||
if !strings.HasPrefix(avatar, "https://") {
|
|
||||||
return fmt.Sprintf("%s%s", avatarBaseUrl, avatar)
|
|
||||||
}
|
|
||||||
return avatar
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPartialAvatarUrl(avatar string) string {
|
|
||||||
if strings.HasPrefix(avatar, avatarBaseUrl) {
|
|
||||||
return avatar[len(avatarBaseUrl):]
|
|
||||||
}
|
|
||||||
return avatar
|
|
||||||
}
|
|
||||||
|
|
||||||
func createUserFromOriginalUser(originalUser *User, affiliationMap map[int]string) *object.User {
|
|
||||||
affiliation := ""
|
|
||||||
if originalUser.SchoolId != 0 {
|
|
||||||
var ok bool
|
|
||||||
affiliation, ok = affiliationMap[originalUser.SchoolId]
|
|
||||||
if !ok {
|
|
||||||
panic(fmt.Sprintf("SchoolId not found: %d", originalUser.SchoolId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
user := &object.User{
|
|
||||||
Owner: orgName,
|
|
||||||
Name: strconv.Itoa(originalUser.Id),
|
|
||||||
CreatedTime: util.GetCurrentTime(),
|
|
||||||
Id: strconv.Itoa(originalUser.Id),
|
|
||||||
Type: "normal-user",
|
|
||||||
Password: originalUser.Password,
|
|
||||||
DisplayName: originalUser.Name,
|
|
||||||
Avatar: getFullAvatarUrl(originalUser.Avatar),
|
|
||||||
Email: "",
|
|
||||||
Phone: originalUser.Cellphone,
|
|
||||||
Address: []string{},
|
|
||||||
Affiliation: affiliation,
|
|
||||||
Score: originalUser.SchoolId,
|
|
||||||
IsAdmin: false,
|
|
||||||
IsGlobalAdmin: false,
|
|
||||||
IsForbidden: originalUser.Deleted != 0,
|
|
||||||
Properties: map[string]string{},
|
|
||||||
}
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
|
|
||||||
func createOriginalUserFromUser(user *object.User) *User {
|
|
||||||
deleted := 0
|
|
||||||
if user.IsForbidden {
|
|
||||||
deleted = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
originalUser := &User{
|
|
||||||
Id: util.ParseInt(user.Id),
|
|
||||||
Name: user.DisplayName,
|
|
||||||
Password: user.Password,
|
|
||||||
Cellphone: user.Phone,
|
|
||||||
SchoolId: user.Score,
|
|
||||||
Avatar: getPartialAvatarUrl(user.Avatar),
|
|
||||||
Deleted: deleted,
|
|
||||||
}
|
|
||||||
return originalUser
|
|
||||||
}
|
|
||||||
|
|
||||||
func syncUsers() {
|
|
||||||
fmt.Printf("Running syncUsers()..\n")
|
|
||||||
|
|
||||||
users, userMap := getUserMap()
|
|
||||||
oUsers, oUserMap := getUserMapOriginal()
|
|
||||||
fmt.Printf("Users: %d, oUsers: %d\n", len(users), len(oUsers))
|
|
||||||
|
|
||||||
_, affiliationMap := getAffiliationMap()
|
|
||||||
|
|
||||||
newUsers := []*object.User{}
|
|
||||||
for _, oUser := range oUsers {
|
|
||||||
id := strconv.Itoa(oUser.Id)
|
|
||||||
if _, ok := userMap[id]; !ok {
|
|
||||||
newUser := createUserFromOriginalUser(oUser, affiliationMap)
|
|
||||||
fmt.Printf("New user: %v\n", newUser)
|
|
||||||
newUsers = append(newUsers, newUser)
|
|
||||||
} else {
|
|
||||||
user := userMap[id]
|
|
||||||
oHash := calculateHash(oUser)
|
|
||||||
|
|
||||||
if user.Hash == user.PreHash {
|
|
||||||
if user.Hash != oHash {
|
|
||||||
updatedUser := createUserFromOriginalUser(oUser, affiliationMap)
|
|
||||||
updatedUser.Hash = oHash
|
|
||||||
updatedUser.PreHash = oHash
|
|
||||||
object.UpdateUserForOriginalFields(updatedUser)
|
|
||||||
fmt.Printf("Update from oUser to user: %v\n", updatedUser)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if user.PreHash == oHash {
|
|
||||||
updatedOUser := createOriginalUserFromUser(user)
|
|
||||||
updateUser(updatedOUser)
|
|
||||||
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
|
|
||||||
|
|
||||||
// update preHash
|
|
||||||
user.PreHash = user.Hash
|
|
||||||
object.SetUserField(user, "pre_hash", user.PreHash)
|
|
||||||
} else {
|
|
||||||
if user.Hash == oHash {
|
|
||||||
// update preHash
|
|
||||||
user.PreHash = user.Hash
|
|
||||||
object.SetUserField(user, "pre_hash", user.PreHash)
|
|
||||||
} else {
|
|
||||||
updatedUser := createUserFromOriginalUser(oUser, affiliationMap)
|
|
||||||
updatedUser.Hash = oHash
|
|
||||||
updatedUser.PreHash = oHash
|
|
||||||
object.UpdateUserForOriginalFields(updatedUser)
|
|
||||||
fmt.Printf("Update from oUser to user (2nd condition): %v\n", updatedUser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
object.AddUsersSafe(newUsers)
|
|
||||||
|
|
||||||
for _, user := range users {
|
|
||||||
id := user.Id
|
|
||||||
if _, ok := oUserMap[id]; !ok {
|
|
||||||
panic(fmt.Sprintf("New original user: cannot create now, user = %v", user))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,79 +0,0 @@
|
|||||||
// Copyright 2021 The casbin 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 original
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/casbin/casdoor/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
Id int `xorm:"int notnull pk autoincr" json:"id"`
|
|
||||||
Name string `xorm:"varchar(128)" json:"name"`
|
|
||||||
Password string `xorm:"varchar(128)" json:"password"`
|
|
||||||
Cellphone string `xorm:"varchar(128)" json:"cellphone"`
|
|
||||||
SchoolId int `json:"schoolId"`
|
|
||||||
Avatar string `xorm:"varchar(128)" json:"avatar"`
|
|
||||||
Deleted int `xorm:"tinyint(1)" json:"deleted"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (User) TableName() string {
|
|
||||||
return userTableName
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUsersOriginal() []*User {
|
|
||||||
users := []*User{}
|
|
||||||
err := adapter.Engine.Asc("id").Find(&users)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return users
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUserMapOriginal() ([]*User, map[string]*User) {
|
|
||||||
users := getUsersOriginal()
|
|
||||||
|
|
||||||
m := map[string]*User{}
|
|
||||||
for _, user := range users {
|
|
||||||
m[strconv.Itoa(user.Id)] = user
|
|
||||||
}
|
|
||||||
return users, m
|
|
||||||
}
|
|
||||||
|
|
||||||
func addUser(user *User) bool {
|
|
||||||
affected, err := adapter.Engine.Insert(user)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return affected != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateUser(user *User) bool {
|
|
||||||
affected, err := adapter.Engine.ID(user.Id).Cols("name", "password", "cellphone", "school_id", "avatar", "deleted").Update(user)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return affected != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func calculateHash(user *User) string {
|
|
||||||
s := strings.Join([]string{strconv.Itoa(user.Id), user.Password, user.Name, getFullAvatarUrl(user.Avatar), user.Cellphone, strconv.Itoa(user.SchoolId)}, "|")
|
|
||||||
return util.GetMd5Hash(s)
|
|
||||||
}
|
|
@@ -1,105 +0,0 @@
|
|||||||
package payment
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
"github.com/casbin/casdoor/object"
|
|
||||||
"github.com/plutov/paypal/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
var client = GetClient()
|
|
||||||
|
|
||||||
func GetClient() *paypal.Client {
|
|
||||||
c, err := paypal.NewClient(beego.AppConfig.String("paypalClientId"), beego.AppConfig.String("paypalSecret"), paypal.APIBaseSandBox)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func Paypal(payItem object.PayItem, clientId string, redirectUri string) string {
|
|
||||||
|
|
||||||
application := object.GetApplicationByClientId(clientId)
|
|
||||||
if application == nil {
|
|
||||||
return "Invalid client_id"
|
|
||||||
}
|
|
||||||
applicationName := fmt.Sprintf("%s/%s", application.Owner, application.Name)
|
|
||||||
if payItem.Currency == "" {
|
|
||||||
payItem.Currency = "USD"
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := client.GetAccessToken(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
appContext := &paypal.ApplicationContext{
|
|
||||||
ReturnURL: "http://localhost:7001/pay/success", //回调链接
|
|
||||||
CancelURL: "https://www.baidu.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
purchaseUnits := make([]paypal.PurchaseUnitRequest, 1)
|
|
||||||
purchaseUnits[0] = paypal.PurchaseUnitRequest{
|
|
||||||
Amount: &paypal.PurchaseUnitAmount{
|
|
||||||
Currency: payItem.Currency, //收款类型
|
|
||||||
Value: payItem.Price, //收款数量
|
|
||||||
},
|
|
||||||
InvoiceID: payItem.Invoice,
|
|
||||||
Description: payItem.Description,
|
|
||||||
}
|
|
||||||
|
|
||||||
order, err := client.CreateOrder(context.Background(),
|
|
||||||
paypal.OrderIntentCapture,
|
|
||||||
purchaseUnits,
|
|
||||||
&paypal.CreateOrderPayer{},
|
|
||||||
appContext)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
newPay := object.Payment{
|
|
||||||
Id: order.ID,
|
|
||||||
Invoice: payItem.Invoice,
|
|
||||||
PayItem: payItem,
|
|
||||||
Application: applicationName,
|
|
||||||
Status: order.Status,
|
|
||||||
Callback: redirectUri,
|
|
||||||
}
|
|
||||||
|
|
||||||
success := object.AddPayment(&newPay)
|
|
||||||
if success {
|
|
||||||
links := order.Links
|
|
||||||
for _, link := range links {
|
|
||||||
fmt.Println(link.Rel)
|
|
||||||
if link.Rel == "approve" {
|
|
||||||
return link.Href
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Add Order to Database false"
|
|
||||||
}
|
|
||||||
|
|
||||||
func SuccessPay(token string) string {
|
|
||||||
_, err := client.GetAccessToken(context.Background())
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
captureOrder, err := client.CaptureOrder(context.Background(), token, paypal.CaptureOrderRequest{})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
pay := object.GetPayment(captureOrder.ID)
|
|
||||||
pay.Purchase = captureOrder.PurchaseUnits
|
|
||||||
pay.Payer = captureOrder.Payer
|
|
||||||
pay.UpdateTime = time.Now().String()
|
|
||||||
pay.Status = captureOrder.Status
|
|
||||||
object.UpdatePay(captureOrder.ID, pay)
|
|
||||||
if captureOrder.Status == "COMPLETED" {
|
|
||||||
return fmt.Sprintf("%s?paymentId=%s", pay.Callback, token)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
|
|
||||||
}
|
|
@@ -15,8 +15,11 @@
|
|||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
@@ -30,10 +33,34 @@ func InitHttpClient() {
|
|||||||
DefaultHttpClient = http.DefaultClient
|
DefaultHttpClient = http.DefaultClient
|
||||||
|
|
||||||
// use proxy
|
// use proxy
|
||||||
|
ProxyHttpClient = getProxyHttpClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAddressOpen(address string) bool {
|
||||||
|
timeout := time.Millisecond * 100
|
||||||
|
conn, err := net.DialTimeout("tcp", address, timeout)
|
||||||
|
if err != nil {
|
||||||
|
// cannot connect to address, proxy is not active
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn != nil {
|
||||||
|
defer conn.Close()
|
||||||
|
fmt.Printf("Socks5 proxy enabled: %s\n", address)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProxyHttpClient() *http.Client {
|
||||||
httpProxy := beego.AppConfig.String("httpProxy")
|
httpProxy := beego.AppConfig.String("httpProxy")
|
||||||
if httpProxy == "" {
|
if httpProxy == "" {
|
||||||
ProxyHttpClient = DefaultHttpClient
|
return &http.Client{}
|
||||||
return
|
}
|
||||||
|
|
||||||
|
if !isAddressOpen(httpProxy) {
|
||||||
|
return &http.Client{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/33585587/creating-a-go-socks5-client
|
// https://stackoverflow.com/questions/33585587/creating-a-go-socks5-client
|
||||||
@@ -43,16 +70,9 @@ func InitHttpClient() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tr := &http.Transport{Dial: dialer.Dial}
|
tr := &http.Transport{Dial: dialer.Dial}
|
||||||
ProxyHttpClient = &http.Client{
|
return &http.Client{
|
||||||
Transport: tr,
|
Transport: tr,
|
||||||
}
|
}
|
||||||
|
|
||||||
//resp, err2 := ProxyHttpClient.Get("https://google.com")
|
|
||||||
//if err2 != nil {
|
|
||||||
// panic(err2)
|
|
||||||
//}
|
|
||||||
//defer resp.Body.Close()
|
|
||||||
//println("Response status: %s", resp.Status)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetHttpClient(url string) *http.Client {
|
func GetHttpClient(url string) *http.Client {
|
||||||
|
@@ -16,6 +16,7 @@ package routers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/astaxie/beego/context"
|
"github.com/astaxie/beego/context"
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
@@ -35,6 +36,9 @@ func AutoSigninFilter(ctx *context.Context) {
|
|||||||
responseError(ctx, "invalid JWT token")
|
responseError(ctx, "invalid JWT token")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if time.Now().Unix() > claims.ExpiresAt.Unix() {
|
||||||
|
responseError(ctx, "expired JWT token")
|
||||||
|
}
|
||||||
|
|
||||||
userId := fmt.Sprintf("%s/%s", claims.User.Owner, claims.User.Name)
|
userId := fmt.Sprintf("%s/%s", claims.User.Owner, claims.User.Name)
|
||||||
setSessionUser(ctx, userId)
|
setSessionUser(ctx, userId)
|
||||||
@@ -62,4 +66,18 @@ func AutoSigninFilter(ctx *context.Context) {
|
|||||||
setSessionUser(ctx, userId)
|
setSessionUser(ctx, userId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP Bearer token
|
||||||
|
// Authorization: Bearer bearerToken
|
||||||
|
bearerToken := parseBearerToken(ctx)
|
||||||
|
if bearerToken != "" {
|
||||||
|
claims, err := object.ParseJwtToken(bearerToken)
|
||||||
|
if err != nil {
|
||||||
|
responseError(ctx, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setSessionUser(ctx, fmt.Sprintf("%s/%s", claims.Owner, claims.Name))
|
||||||
|
setSessionExpire(ctx, claims.ExpiresAt.Unix())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,9 +16,11 @@ package routers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/astaxie/beego/context"
|
"github.com/astaxie/beego/context"
|
||||||
"github.com/casbin/casdoor/object"
|
"github.com/casbin/casdoor/object"
|
||||||
|
"github.com/casbin/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
@@ -49,8 +51,12 @@ func denyRequest(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getUsernameByClientIdSecret(ctx *context.Context) string {
|
func getUsernameByClientIdSecret(ctx *context.Context) string {
|
||||||
clientId := ctx.Input.Query("clientId")
|
clientId, clientSecret, ok := ctx.Request.BasicAuth()
|
||||||
clientSecret := ctx.Input.Query("clientSecret")
|
if !ok {
|
||||||
|
clientId = ctx.Input.Query("clientId")
|
||||||
|
clientSecret = ctx.Input.Query("clientSecret")
|
||||||
|
}
|
||||||
|
|
||||||
if clientId == "" || clientSecret == "" {
|
if clientId == "" || clientSecret == "" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -81,3 +87,27 @@ func setSessionUser(ctx *context.Context, user string) {
|
|||||||
// https://github.com/beego/beego/issues/3445#issuecomment-455411915
|
// https://github.com/beego/beego/issues/3445#issuecomment-455411915
|
||||||
ctx.Input.CruSession.SessionRelease(ctx.ResponseWriter)
|
ctx.Input.CruSession.SessionRelease(ctx.ResponseWriter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setSessionExpire(ctx *context.Context, ExpireTime int64) {
|
||||||
|
SessionData := struct{ ExpireTime int64 }{ExpireTime: ExpireTime}
|
||||||
|
err := ctx.Input.CruSession.Set("SessionData", util.StructToJson(SessionData))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ctx.Input.CruSession.SessionRelease(ctx.ResponseWriter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBearerToken(ctx *context.Context) string {
|
||||||
|
header := ctx.Request.Header.Get("Authorization")
|
||||||
|
tokens := strings.Split(header, " ")
|
||||||
|
if len(tokens) != 2 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
prefix := tokens[0]
|
||||||
|
if prefix != "Bearer" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens[1]
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user