mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-15 06:33:50 +08:00
Compare commits
119 Commits
Author | SHA1 | Date | |
---|---|---|---|
ab5af979c8 | |||
e31aaf5657 | |||
eaf5cb66f3 | |||
83a6b757a4 | |||
2a0dcd746f | |||
22f5ad06ec | |||
18aa70dfb2 | |||
697b3e4998 | |||
d48d515c36 | |||
a5d166c35f | |||
4915963c52 | |||
759a1421e5 | |||
c14bf9fdab | |||
e19f07c521 | |||
39ab71c5db | |||
2c97f8a8b7 | |||
21392dcc14 | |||
953d3d5bc5 | |||
ddee97f544 | |||
c58a6d8725 | |||
a5ff9549c1 | |||
fe57dcbff4 | |||
f8c4ca0f00 | |||
e738c42bd8 | |||
cbc8c58e85 | |||
07c90e048f | |||
a33076ada4 | |||
9cabc4035f | |||
274096fe9d | |||
661abd6b6e | |||
4122c94205 | |||
68ef5f8311 | |||
e35b058ab4 | |||
7d1f368bc2 | |||
0bd86baf4d | |||
adf036d8c7 | |||
2d19d366d4 | |||
db37f53d6c | |||
eacd5f59db | |||
9024010081 | |||
3aab6c8687 | |||
7391773f0e | |||
de8163a19b | |||
07abe06332 | |||
0bc29465e5 | |||
c37b0111a7 | |||
47d1448c02 | |||
eb15afec34 | |||
e1c54744dc | |||
612b5f5c2e | |||
bd38552db5 | |||
256b433e57 | |||
63161d6135 | |||
5640d258bb | |||
f85f4c0cf8 | |||
0720794e75 | |||
940aa2bc2d | |||
db44957b1f | |||
e5e1fdae76 | |||
80f01074fa | |||
d943d5cc61 | |||
19ed35f964 | |||
5757021e87 | |||
259a4e1307 | |||
034d822dd5 | |||
a8502d1173 | |||
3c2f7b7fc8 | |||
fbc73de3bb | |||
479daf4fa4 | |||
d129202b95 | |||
c1f553440e | |||
7dcae2d183 | |||
5ec0c7a890 | |||
051752340d | |||
c87c001da3 | |||
12bc419659 | |||
d5f18f2d64 | |||
02c06bc93c | |||
40aa9a4693 | |||
630b84f534 | |||
339a85e4b0 | |||
c22ab44894 | |||
c3fb48f473 | |||
a111fd672c | |||
9fd175eefd | |||
d9bcce9485 | |||
d183b9eca9 | |||
f24d9ae251 | |||
030c1caa50 | |||
cee2c608a2 | |||
82d0e895e0 | |||
dee9bac110 | |||
e7a6986b62 | |||
b91b4aec91 | |||
fe48c38bc6 | |||
1be777c08f | |||
8d54bfad8a | |||
728fe11a3c | |||
69e0f4e40d | |||
ba32a45693 | |||
a4d83af768 | |||
5b8f6415d9 | |||
5389cb435c | |||
9b6131890c | |||
ffc0a0e0d5 | |||
ff22bf507f | |||
2d4103d751 | |||
4611b59b08 | |||
445d3c9d0e | |||
dbebd1846f | |||
2fcc8f5bfe | |||
4b65320a96 | |||
5e8897e41b | |||
ba1646a0c3 | |||
c1cd187558 | |||
519fd655cf | |||
377ac05928 | |||
4f124ff140 | |||
d5f802ec7d |
26
.github/workflows/build.yml
vendored
26
.github/workflows/build.yml
vendored
@ -3,9 +3,33 @@ name: Build
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
go-tests:
|
||||
name: Running Go tests
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_DATABASE: casdoor
|
||||
MYSQL_ROOT_PASSWORD: 123456
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16.5'
|
||||
- name: Tests
|
||||
run: |
|
||||
go test -v $(go list ./...) -tags skipCi
|
||||
working-directory: ./
|
||||
|
||||
frontend:
|
||||
name: Front-end
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ go-tests ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
@ -14,10 +38,10 @@ jobs:
|
||||
- run: yarn install && CI=false yarn run build
|
||||
working-directory: ./web
|
||||
|
||||
|
||||
backend:
|
||||
name: Back-end
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ go-tests ]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
|
2
.github/workflows/sync.yml
vendored
2
.github/workflows/sync.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
synchronize-with-crowdin:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'casbin/casdoor' && github.event_name == 'push'
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -13,7 +13,8 @@
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
vendor/
|
||||
bin/
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
|
42
.golangci.yml
Normal file
42
.golangci.yml
Normal file
@ -0,0 +1,42 @@
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- deadcode
|
||||
- dupl
|
||||
- errcheck
|
||||
- goconst
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- lll
|
||||
- misspell
|
||||
- nakedret
|
||||
- prealloc
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
- revive
|
||||
- exportloopref
|
||||
run:
|
||||
deadline: 5m
|
||||
skip-dirs:
|
||||
- api
|
||||
# skip-files:
|
||||
# - ".*_test\\.go$"
|
||||
modules-download-mode: vendor
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
line-length: 150
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 1
|
12
Dockerfile
12
Dockerfile
@ -7,12 +7,14 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOPROXY=https://goproxy.cn,direct go b
|
||||
FROM node:16.13.0 AS FRONT
|
||||
WORKDIR /web
|
||||
COPY ./web .
|
||||
RUN yarn config set registry https://registry.npm.taobao.org
|
||||
RUN yarn config set registry https://registry.npmmirror.com
|
||||
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
|
||||
RUN apt update
|
||||
RUN apt install -y ca-certificates && update-ca-certificates
|
||||
RUN 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 ./
|
||||
@ -20,17 +22,17 @@ 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
|
||||
./wait-for-it localhost:3306 -- ./server --createDatabase=true
|
||||
|
||||
|
||||
FROM alpine:latest
|
||||
RUN sed -i 's/https/http/' /etc/apk/repositories
|
||||
RUN apk add curl
|
||||
RUN apk add ca-certificates && update-ca-certificates
|
||||
LABEL MAINTAINER="https://casdoor.org/"
|
||||
|
||||
COPY --from=BACK /go/src/casdoor/ ./
|
||||
COPY --from=BACK /usr/bin/wait-for-it ./
|
||||
RUN mkdir -p web/build && apk add --no-cache bash coreutils
|
||||
COPY --from=FRONT /web/build /web/build
|
||||
CMD ./wait-for-it db:3306 -- ./server
|
||||
|
||||
CMD ./server
|
||||
|
113
Makefile
Normal file
113
Makefile
Normal file
@ -0,0 +1,113 @@
|
||||
|
||||
# Image URL to use all building/pushing image targets
|
||||
REGISTRY ?= casbin
|
||||
IMG ?= casdoor
|
||||
IMG_TAG ?=$(shell git --no-pager log -1 --format="%ad" --date=format:"%Y%m%d")-$(shell git describe --tags --always --dirty --abbrev=6)
|
||||
NAMESPACE ?= casdoor
|
||||
APP ?= casdoor
|
||||
HOST ?= test.com
|
||||
|
||||
|
||||
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||
ifeq (,$(shell go env GOBIN))
|
||||
GOBIN=$(shell go env GOPATH)/bin
|
||||
else
|
||||
GOBIN=$(shell go env GOBIN)
|
||||
endif
|
||||
|
||||
# Setting SHELL to bash allows bash commands to be executed by recipes.
|
||||
# This is a requirement for 'setup-envtest.sh' in the test target.
|
||||
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
|
||||
SHELL = /usr/bin/env bash -o pipefail
|
||||
.SHELLFLAGS = -ec
|
||||
|
||||
.PHONY: all
|
||||
all: docker-build docker-push deploy
|
||||
|
||||
##@ General
|
||||
|
||||
# The help target prints out all targets with their descriptions organized
|
||||
# beneath their categories. The categories are represented by '##@' and the
|
||||
# target descriptions by '##'. The awk commands is responsible for reading the
|
||||
# entire set of makefiles included in this invocation, looking for lines of the
|
||||
# file as xyz: ## something, and then pretty-format the target and help. Then,
|
||||
# if there's a line with ##@ something, that gets pretty-printed as a category.
|
||||
# More info on the usage of ANSI control characters for terminal formatting:
|
||||
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
|
||||
# More info on the awk command:
|
||||
# http://linuxcommand.org/lc3_adv_awk.php
|
||||
|
||||
.PHONY: help
|
||||
help: ## Display this help.
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
|
||||
|
||||
##@ Development
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: ## Run go fmt against code.
|
||||
go fmt ./...
|
||||
|
||||
.PHONY: vet
|
||||
vet: ## Run go vet against code.
|
||||
go vet ./...
|
||||
|
||||
.PHONY: ut
|
||||
ut: ## UT test
|
||||
go test -v -cover -coverprofile=coverage.out ./...
|
||||
go tool cover -func=coverage.out
|
||||
|
||||
##@ Build
|
||||
|
||||
.PHONY: backend
|
||||
backend: fmt vet ## Build backend binary.
|
||||
go build -o bin/manager main.go
|
||||
|
||||
.PHONY: backend-vendor
|
||||
backend-vendor: vendor fmt vet ## Build backend binary with vendor.
|
||||
go build -mod=vendor -o bin/manager main.go
|
||||
|
||||
.PHONY: frontend
|
||||
frontend: ## Build backend binary.
|
||||
cd web/ && yarn && yarn run build && cd -
|
||||
|
||||
.PHONY: vendor
|
||||
vendor: ## Update vendor.
|
||||
go mod vendor
|
||||
|
||||
.PHONY: run
|
||||
run: fmt vet ## Run backend in local
|
||||
go run ./main.go
|
||||
|
||||
.PHONY: docker-build
|
||||
docker-build: ## Build docker image with the manager.
|
||||
docker build -t ${REGISTRY}/${IMG}:${IMG_TAG} .
|
||||
|
||||
.PHONY: docker-push
|
||||
docker-push: ## Push docker image with the manager.
|
||||
docker push ${REGISTRY}/${IMG}:${IMG_TAG}
|
||||
|
||||
lint-install: ## Install golangci-lint
|
||||
@# The following installs a specific version of golangci-lint, which is appropriate for a CI server to avoid different results from build to build
|
||||
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.40.1
|
||||
|
||||
lint: ## Run golangci-lint
|
||||
@echo "---lint---"
|
||||
golangci-lint run --modules-download-mode=vendor ./...
|
||||
|
||||
##@ Deployment
|
||||
|
||||
.PHONY: deploy
|
||||
deploy: ## Deploy controller to the K8s cluster specified in ~/.kube/config.
|
||||
helm upgrade --install ${APP} manifests/casdoor --create-namespace --set ingress.enabled=true \
|
||||
--set "ingress.hosts[0].host=${HOST},ingress.hosts[0].paths[0].path=/,ingress.hosts[0].paths[0].pathType=ImplementationSpecific" \
|
||||
--set image.tag=${IMG_TAG} --set image.repository=${REGISTRY} --set image.name=${IMG} --version ${IMG_TAG} -n ${NAMESPACE}
|
||||
|
||||
.PHONY: dry-run
|
||||
dry-run: ## Dry run for helm install
|
||||
helm upgrade --install ${APP} manifests/casdoor --set ingress.enabled=true \
|
||||
--set "ingress.hosts[0].host=${HOST},ingress.hosts[0].paths[0].path=/,ingress.hosts[0].paths[0].pathType=ImplementationSpecific" \
|
||||
--set image.tag=${IMG_TAG} --set image.repository=${REGISTRY} --set image.name=${IMG} --version ${IMG_TAG} -n ${NAMESPACE} --dry-run
|
||||
|
||||
.PHONY: undeploy
|
||||
undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
|
||||
helm delete ${APP} -n ${NAMESPACE}
|
38
README.md
38
README.md
@ -7,10 +7,10 @@
|
||||
<a href="https://hub.docker.com/r/casbin/casdoor">
|
||||
<img alt="docker pull casbin/casdoor" src="https://img.shields.io/docker/pulls/casbin/casdoor.svg">
|
||||
</a>
|
||||
<a href="https://github.com/casbin/casdoor/actions/workflows/build.yml">
|
||||
<a href="https://github.com/casdoor/casdoor/actions/workflows/build.yml">
|
||||
<img alt="GitHub Workflow Status (branch)" src="https://github.com/casbin/jcasbin/workflows/build/badge.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/casbin/casdoor/releases/latest">
|
||||
<a href="https://github.com/casdoor/casdoor/releases/latest">
|
||||
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/casbin/casdoor.svg">
|
||||
</a>
|
||||
<a href="https://hub.docker.com/repository/docker/casbin/casdoor">
|
||||
@ -19,29 +19,32 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://goreportcard.com/report/github.com/casbin/casdoor">
|
||||
<img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/casbin/casdoor?style=flat-square">
|
||||
<a href="https://goreportcard.com/report/github.com/casdoor/casdoor">
|
||||
<img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/casdoor/casdoor?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/casbin/casdoor/blob/master/LICENSE">
|
||||
<a href="https://github.com/casdoor/casdoor/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/casbin/casdoor?style=flat-square" alt="license">
|
||||
</a>
|
||||
<a href="https://github.com/casbin/casdoor/issues">
|
||||
<a href="https://github.com/casdoor/casdoor/issues">
|
||||
<img alt="GitHub issues" src="https://img.shields.io/github/issues/casbin/casdoor?style=flat-square">
|
||||
</a>
|
||||
<a href="#">
|
||||
<img alt="GitHub stars" src="https://img.shields.io/github/stars/casbin/casdoor?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/casbin/casdoor/network">
|
||||
<a href="https://github.com/casdoor/casdoor/network">
|
||||
<img alt="GitHub forks" src="https://img.shields.io/github/forks/casbin/casdoor?style=flat-square">
|
||||
</a>
|
||||
<a href="https://crowdin.com/project/casdoor-site">
|
||||
<img alt="Crowdin" src="https://badges.crowdin.net/casdoor-site/localized.svg">
|
||||
</a>
|
||||
<a href="https://gitter.im/casbin/casdoor">
|
||||
<img alt="Gitter" src="https://badges.gitter.im/casbin/casdoor.svg">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Online demo
|
||||
|
||||
Deployed site: https://door.casbin.com/
|
||||
Deployed site: https://door.casdoor.com/
|
||||
|
||||
## Quick Start
|
||||
Run your own casdoor program in a few minutes.
|
||||
@ -51,13 +54,13 @@ Run your own casdoor program in a few minutes.
|
||||
There are two methods, get code via go subcommand `get`:
|
||||
|
||||
```shell
|
||||
go get github.com/casbin/casdoor
|
||||
go get github.com/casdoor/casdoor
|
||||
```
|
||||
|
||||
or `git`:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/casbin/casdoor
|
||||
git clone https://github.com/casdoor/casdoor
|
||||
```
|
||||
|
||||
Finally, change directory:
|
||||
@ -155,6 +158,19 @@ dataSourceName = root:123456@tcp(db:3306)/
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
### K8S
|
||||
You could use helm to deploy casdoor in k8s. At first, you should modify the [configmap](./manifests/casdoor/templates/configmap.yaml) for your application.
|
||||
And then run bellow command to deploy it.
|
||||
|
||||
```bash
|
||||
IMG_TAG=latest make deploy
|
||||
```
|
||||
|
||||
And undeploy it with:
|
||||
```bash
|
||||
make undeploy
|
||||
```
|
||||
|
||||
That's it! Try to visit http://localhost:8000/. :small_airplane:
|
||||
|
||||
## Detailed documentation
|
||||
@ -179,5 +195,5 @@ If you are contributing to casdoor, please note that we use [Crowdin](https://cr
|
||||
|
||||
## License
|
||||
|
||||
[Apache-2.0](https://github.com/casbin/casdoor/blob/master/LICENSE)
|
||||
[Apache-2.0](https://github.com/casdoor/casdoor/blob/master/LICENSE)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -18,8 +18,8 @@ import (
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/model"
|
||||
"github.com/casbin/casdoor/conf"
|
||||
xormadapter "github.com/casbin/xorm-adapter/v2"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
stringadapter "github.com/qiangmzsx/string-adapter/v2"
|
||||
)
|
||||
|
||||
@ -28,7 +28,8 @@ var Enforcer *casbin.Enforcer
|
||||
func InitAuthz() {
|
||||
var err error
|
||||
|
||||
a, err := xormadapter.NewAdapter(beego.AppConfig.String("driverName"), conf.GetBeegoConfDataSourceName()+beego.AppConfig.String("dbName"), true)
|
||||
tableNamePrefix := beego.AppConfig.String("tableNamePrefix")
|
||||
a, err := xormadapter.NewAdapterWithTableName(beego.AppConfig.String("driverName"), conf.GetBeegoConfDataSourceName()+beego.AppConfig.String("dbName"), "casbin_rule", tableNamePrefix, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -53,7 +54,7 @@ m = (r.subOwner == p.subOwner || p.subOwner == "*") && \
|
||||
(r.urlPath == p.urlPath || p.urlPath == "*") && \
|
||||
(r.objOwner == p.objOwner || p.objOwner == "*") && \
|
||||
(r.objName == p.objName || p.objName == "*") || \
|
||||
(r.urlPath == "/api/update-user" && r.subOwner == r.objOwner && r.subName == r.objName)
|
||||
(r.subOwner == r.objOwner && r.subName == r.objName)
|
||||
`
|
||||
|
||||
m, err := model.NewModelFromString(modelText)
|
||||
@ -79,15 +80,14 @@ p, *, *, POST, /api/login, *, *
|
||||
p, *, *, GET, /api/get-app-login, *, *
|
||||
p, *, *, POST, /api/logout, *, *
|
||||
p, *, *, GET, /api/get-account, *, *
|
||||
p, *, *, GET, /api/userinfo, *, *
|
||||
p, *, *, POST, /api/login/oauth/access_token, *, *
|
||||
p, *, *, POST, /api/login/oauth/refresh_token, *, *
|
||||
p, *, *, GET, /api/login/oauth/logout, *, *
|
||||
p, *, *, GET, /api/get-application, *, *
|
||||
p, *, *, GET, /api/get-users, *, *
|
||||
p, *, *, GET, /api/get-user, *, *
|
||||
p, *, *, GET, /api/get-organizations, *, *
|
||||
p, *, *, GET, /api/get-user-application, *, *
|
||||
p, *, *, GET, /api/get-default-providers, *, *
|
||||
p, *, *, GET, /api/get-resources, *, *
|
||||
p, *, *, POST, /api/upload-avatar, *, *
|
||||
p, *, *, POST, /api/unlink, *, *
|
||||
p, *, *, POST, /api/set-password, *, *
|
||||
p, *, *, POST, /api/send-verification-code, *, *
|
||||
@ -95,7 +95,7 @@ p, *, *, GET, /api/get-human-check, *, *
|
||||
p, *, *, POST, /api/reset-email-or-phone, *, *
|
||||
p, *, *, POST, /api/upload-resource, *, *
|
||||
p, *, *, GET, /.well-known/openid-configuration, *, *
|
||||
p, *, *, *, /api/certs, *, *
|
||||
p, *, *, *, /.well-known/jwks, *, *
|
||||
p, *, *, GET, /api/get-saml-login, *, *
|
||||
p, *, *, POST, /api/acs, *, *
|
||||
`
|
||||
|
@ -6,12 +6,14 @@ copyrequestbody = true
|
||||
driverName = mysql
|
||||
dataSourceName = root:123456@tcp(localhost:3306)/
|
||||
dbName = casdoor
|
||||
tableNamePrefix =
|
||||
showSql = false
|
||||
redisEndpoint =
|
||||
defaultStorageProvider =
|
||||
isCloudIntranet = false
|
||||
authState = "casdoor"
|
||||
httpProxy = "127.0.0.1:10808"
|
||||
sock5Proxy = "127.0.0.1:10808"
|
||||
verificationCodeTimeout = 10
|
||||
initScore = 2000
|
||||
logPostOnly = true
|
||||
origin = "https://door.casbin.com"
|
||||
origin =
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -19,13 +19,15 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
const (
|
||||
ResponseTypeLogin = "login"
|
||||
ResponseTypeCode = "code"
|
||||
ResponseTypeLogin = "login"
|
||||
ResponseTypeCode = "code"
|
||||
ResponseTypeToken = "token"
|
||||
ResponseTypeIdToken = "id_token"
|
||||
)
|
||||
|
||||
type RequestForm struct {
|
||||
@ -35,9 +37,12 @@ type RequestForm struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Name string `json:"name"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Affiliation string `json:"affiliation"`
|
||||
IdCard string `json:"idCard"`
|
||||
Region string `json:"region"`
|
||||
|
||||
Application string `json:"application"`
|
||||
@ -61,6 +66,7 @@ type Response struct {
|
||||
Status string `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
Sub string `json:"sub"`
|
||||
Name string `json:"name"`
|
||||
Data interface{} `json:"data"`
|
||||
Data2 interface{} `json:"data2"`
|
||||
}
|
||||
@ -100,7 +106,7 @@ func (c *ApiController) Signup() {
|
||||
}
|
||||
|
||||
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.FirstName, form.LastName, form.Email, form.Phone, form.Affiliation)
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
@ -129,7 +135,12 @@ func (c *ApiController) Signup() {
|
||||
id := util.GenerateId()
|
||||
if application.GetSignupItemRule("ID") == "Incremental" {
|
||||
lastUser := object.GetLastUser(form.Organization)
|
||||
lastIdInt := util.ParseInt(lastUser.Id)
|
||||
|
||||
lastIdInt := -1
|
||||
if lastUser != nil {
|
||||
lastIdInt = util.ParseInt(lastUser.Id)
|
||||
}
|
||||
|
||||
id = strconv.Itoa(lastIdInt + 1)
|
||||
}
|
||||
|
||||
@ -138,6 +149,8 @@ func (c *ApiController) Signup() {
|
||||
username = id
|
||||
}
|
||||
|
||||
userCount := object.GetUserCount(form.Organization, "", "") + 1
|
||||
|
||||
user := &object.User{
|
||||
Owner: form.Organization,
|
||||
Name: username,
|
||||
@ -151,6 +164,7 @@ func (c *ApiController) Signup() {
|
||||
Phone: form.Phone,
|
||||
Address: []string{},
|
||||
Affiliation: form.Affiliation,
|
||||
IdCard: form.IdCard,
|
||||
Region: form.Region,
|
||||
Score: getInitScore(),
|
||||
IsAdmin: false,
|
||||
@ -159,6 +173,16 @@ func (c *ApiController) Signup() {
|
||||
IsDeleted: false,
|
||||
SignupApplication: application.Name,
|
||||
Properties: map[string]string{},
|
||||
Ranking: userCount + 1,
|
||||
Karma: 0,
|
||||
}
|
||||
|
||||
if application.GetSignupItemRule("Display name") == "First, last" {
|
||||
if form.FirstName != "" || form.LastName != "" {
|
||||
user.DisplayName = fmt.Sprintf("%s %s", form.FirstName, form.LastName)
|
||||
user.FirstName = form.FirstName
|
||||
user.LastName = form.LastName
|
||||
}
|
||||
}
|
||||
|
||||
affected := object.AddUser(user)
|
||||
@ -177,6 +201,11 @@ func (c *ApiController) Signup() {
|
||||
object.DisableVerificationCode(form.Email)
|
||||
object.DisableVerificationCode(checkPhone)
|
||||
|
||||
record := object.NewRecord(c.Ctx)
|
||||
record.Organization = application.Organization
|
||||
record.User = user.Name
|
||||
go object.AddRecord(record)
|
||||
|
||||
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
|
||||
|
||||
c.ResponseOk(userId)
|
||||
@ -220,6 +249,7 @@ func (c *ApiController) GetAccount() {
|
||||
resp := Response{
|
||||
Status: "ok",
|
||||
Sub: user.Id,
|
||||
Name: user.Name,
|
||||
Data: user,
|
||||
Data2: organization,
|
||||
}
|
||||
@ -227,6 +257,27 @@ func (c *ApiController) GetAccount() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UserInfo
|
||||
// @Title UserInfo
|
||||
// @Tag Account API
|
||||
// @Description return user information according to OIDC standards
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
// @router /userinfo [get]
|
||||
func (c *ApiController) GetUserinfo() {
|
||||
userId, ok := c.RequireSignedIn()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
scope, aud := c.GetSessionOidc()
|
||||
host := c.Ctx.Request.Host
|
||||
resp, err := object.GetUserInfo(userId, scope, aud, host)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
}
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetHumanCheck ...
|
||||
// @Tag Login API
|
||||
// @Title GetHumancheck
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -18,8 +18,8 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetApplications
|
||||
@ -30,6 +30,7 @@ import (
|
||||
// @Success 200 {array} object.Application The Response object
|
||||
// @router /get-applications [get]
|
||||
func (c *ApiController) GetApplications() {
|
||||
userId := c.GetSessionUsername()
|
||||
owner := c.Input().Get("owner")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
@ -37,13 +38,22 @@ func (c *ApiController) GetApplications() {
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
organization := c.Input().Get("organization")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
c.Data["json"] = object.GetApplications(owner)
|
||||
var applications []*object.Application
|
||||
if organization == "" {
|
||||
applications = object.GetApplications(owner)
|
||||
} else {
|
||||
applications = object.GetApplicationsByOrganizationName(owner, organization)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedApplications(applications, userId)
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetApplicationCount(owner, field, value)))
|
||||
applications := object.GetPaginationApplications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
applications := object.GetMaskedApplications(object.GetPaginationApplications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder), userId)
|
||||
c.ResponseOk(applications, paginator.Nums())
|
||||
}
|
||||
}
|
||||
@ -56,9 +66,10 @@ func (c *ApiController) GetApplications() {
|
||||
// @Success 200 {object} object.Application The Response object
|
||||
// @router /get-application [get]
|
||||
func (c *ApiController) GetApplication() {
|
||||
userId := c.GetSessionUsername()
|
||||
id := c.Input().Get("id")
|
||||
|
||||
c.Data["json"] = object.GetApplication(id)
|
||||
c.Data["json"] = object.GetMaskedApplication(object.GetApplication(id), userId)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@ -70,6 +81,7 @@ func (c *ApiController) GetApplication() {
|
||||
// @Success 200 {object} object.Application The Response object
|
||||
// @router /get-user-application [get]
|
||||
func (c *ApiController) GetUserApplication() {
|
||||
userId := c.GetSessionUsername()
|
||||
id := c.Input().Get("id")
|
||||
user := object.GetUser(id)
|
||||
if user == nil {
|
||||
@ -77,7 +89,7 @@ func (c *ApiController) GetUserApplication() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetApplicationByUser(user)
|
||||
c.Data["json"] = object.GetMaskedApplication(object.GetApplicationByUser(user), userId)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -24,10 +24,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casbin/casdoor/idp"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/proxy"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/idp"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
func codeToResponse(code *object.Code) *Response {
|
||||
@ -38,6 +38,14 @@ func codeToResponse(code *object.Code) *Response {
|
||||
return &Response{Status: "ok", Msg: "", Data: code.Code}
|
||||
}
|
||||
|
||||
func tokenToResponse(token *object.Token) *Response {
|
||||
if token.AccessToken == "" {
|
||||
return &Response{Status: "error", Msg: "fail to get accessToken", Data: token.AccessToken}
|
||||
}
|
||||
return &Response{Status: "ok", Msg: "", Data: token.AccessToken}
|
||||
|
||||
}
|
||||
|
||||
// HandleLoggedIn ...
|
||||
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *RequestForm) (resp *Response) {
|
||||
userId := user.GetId()
|
||||
@ -52,13 +60,29 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
scope := c.Input().Get("scope")
|
||||
state := c.Input().Get("state")
|
||||
nonce := c.Input().Get("nonce")
|
||||
code := object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce)
|
||||
challengeMethod := c.Input().Get("code_challenge_method")
|
||||
codeChallenge := c.Input().Get("code_challenge")
|
||||
|
||||
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
|
||||
c.ResponseError("Challenge method should be S256")
|
||||
return
|
||||
}
|
||||
code := object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce, codeChallenge, c.Ctx.Request.Host)
|
||||
resp = codeToResponse(code)
|
||||
|
||||
if application.EnableSigninSession || application.HasPromptPage() {
|
||||
// The prompt page needs the user to be signed in
|
||||
c.SetSessionUsername(userId)
|
||||
}
|
||||
} else if form.Type == ResponseTypeToken || form.Type == ResponseTypeIdToken { //implicit flow
|
||||
if !object.IsGrantTypeValid(form.Type, application.GrantTypes) {
|
||||
resp = &Response{Status: "error", Msg: fmt.Sprintf("error: grant_type: %s is not supported in this application", form.Type), Data: ""}
|
||||
} else {
|
||||
scope := c.Input().Get("scope")
|
||||
token, _ := object.GetTokenByUser(application, user, scope, c.Ctx.Request.Host)
|
||||
resp = tokenToResponse(token)
|
||||
}
|
||||
|
||||
} else {
|
||||
resp = &Response{Status: "error", Msg: fmt.Sprintf("Unknown response type: %s", form.Type)}
|
||||
}
|
||||
@ -94,6 +118,7 @@ func (c *ApiController) GetApplicationLogin() {
|
||||
state := c.Input().Get("state")
|
||||
|
||||
msg, application := object.CheckOAuthLogin(clientId, responseType, redirectUri, scope, state)
|
||||
application = object.GetMaskedApplication(application, "")
|
||||
if msg != "" {
|
||||
c.ResponseError(msg, application)
|
||||
} else {
|
||||
@ -102,7 +127,7 @@ func (c *ApiController) GetApplicationLogin() {
|
||||
}
|
||||
|
||||
func setHttpClient(idProvider idp.IdProvider, providerType string) {
|
||||
if providerType == "GitHub" || providerType == "Google" || providerType == "Facebook" || providerType == "LinkedIn" {
|
||||
if providerType == "GitHub" || providerType == "Google" || providerType == "Facebook" || providerType == "LinkedIn" || providerType == "Steam" {
|
||||
idProvider.SetHttpClient(proxy.ProxyHttpClient)
|
||||
} else {
|
||||
idProvider.SetHttpClient(proxy.DefaultHttpClient)
|
||||
@ -214,7 +239,7 @@ func (c *ApiController) Login() {
|
||||
clientSecret = provider.ClientSecret2
|
||||
}
|
||||
|
||||
idProvider := idp.GetIdProvider(provider.Type, clientId, clientSecret, form.RedirectUri)
|
||||
idProvider := idp.GetIdProvider(provider.Type, provider.SubType, clientId, clientSecret, provider.AppId, form.RedirectUri, provider.Domain)
|
||||
if idProvider == nil {
|
||||
c.ResponseError(fmt.Sprintf("The provider type: %s is not supported", provider.Type))
|
||||
return
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -15,10 +15,12 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// controller for handlers under /api uri
|
||||
@ -35,6 +37,21 @@ type SessionData struct {
|
||||
ExpireTime int64
|
||||
}
|
||||
|
||||
func (c *ApiController) IsGlobalAdmin() bool {
|
||||
username := c.GetSessionUsername()
|
||||
if strings.HasPrefix(username, "app/") {
|
||||
// e.g., "app/app-casnode"
|
||||
return true
|
||||
}
|
||||
|
||||
user := object.GetUser(username)
|
||||
if user == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return user.Owner == "built-in" || user.IsGlobalAdmin
|
||||
}
|
||||
|
||||
// GetSessionUsername ...
|
||||
func (c *ApiController) GetSessionUsername() string {
|
||||
// check if user session expired
|
||||
@ -55,6 +72,28 @@ func (c *ApiController) GetSessionUsername() string {
|
||||
return user.(string)
|
||||
}
|
||||
|
||||
func (c *ApiController) GetSessionOidc() (string, string) {
|
||||
sessionData := c.GetSessionData()
|
||||
if sessionData != nil &&
|
||||
sessionData.ExpireTime != 0 &&
|
||||
sessionData.ExpireTime < time.Now().Unix() {
|
||||
c.SetSessionUsername("")
|
||||
c.SetSessionData(nil)
|
||||
return "", ""
|
||||
}
|
||||
scopeValue := c.GetSession("scope")
|
||||
audValue := c.GetSession("aud")
|
||||
var scope, aud string
|
||||
var ok bool
|
||||
if scope, ok = scopeValue.(string); !ok {
|
||||
scope = ""
|
||||
}
|
||||
if aud, ok = audValue.(string); !ok {
|
||||
aud = ""
|
||||
}
|
||||
return scope, aud
|
||||
}
|
||||
|
||||
// SetSessionUsername ...
|
||||
func (c *ApiController) SetSessionUsername(user string) {
|
||||
c.SetSession("username", user)
|
||||
|
116
controllers/cert.go
Normal file
116
controllers/cert.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetCerts
|
||||
// @Title GetCerts
|
||||
// @Tag Cert API
|
||||
// @Description get certs
|
||||
// @Param owner query string true "The owner of certs"
|
||||
// @Success 200 {array} object.Cert The Response object
|
||||
// @router /get-certs [get]
|
||||
func (c *ApiController) GetCerts() {
|
||||
owner := c.Input().Get("owner")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
if limit == "" || page == "" {
|
||||
c.Data["json"] = object.GetMaskedCerts(object.GetCerts(owner))
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetCertCount(owner, field, value)))
|
||||
certs := object.GetMaskedCerts(object.GetPaginationCerts(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||
c.ResponseOk(certs, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// @Title GetCert
|
||||
// @Tag Cert API
|
||||
// @Description get cert
|
||||
// @Param id query string true "The id of the cert"
|
||||
// @Success 200 {object} object.Cert The Response object
|
||||
// @router /get-cert [get]
|
||||
func (c *ApiController) GetCert() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
c.Data["json"] = object.GetMaskedCert(object.GetCert(id))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title UpdateCert
|
||||
// @Tag Cert API
|
||||
// @Description update cert
|
||||
// @Param id query string true "The id of the cert"
|
||||
// @Param body body object.Cert true "The details of the cert"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-cert [post]
|
||||
func (c *ApiController) UpdateCert() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var cert object.Cert
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &cert)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateCert(id, &cert))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title AddCert
|
||||
// @Tag Cert API
|
||||
// @Description add cert
|
||||
// @Param body body object.Cert true "The details of the cert"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-cert [post]
|
||||
func (c *ApiController) AddCert() {
|
||||
var cert object.Cert
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &cert)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddCert(&cert))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title DeleteCert
|
||||
// @Tag Cert API
|
||||
// @Description delete cert
|
||||
// @Param body body object.Cert true "The details of the cert"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-cert [post]
|
||||
func (c *ApiController) DeleteCert() {
|
||||
var cert object.Cert
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &cert)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteCert(&cert))
|
||||
c.ServeJSON()
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -17,8 +17,8 @@ package controllers
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
type LdapServer struct {
|
||||
@ -170,6 +170,7 @@ func (c *ApiController) UpdateLdap() {
|
||||
return
|
||||
}
|
||||
|
||||
prevLdap := object.GetLdap(ldap.Id)
|
||||
affected := object.UpdateLdap(&ldap)
|
||||
resp := wrapActionResponse(affected)
|
||||
if affected {
|
||||
@ -177,6 +178,8 @@ func (c *ApiController) UpdateLdap() {
|
||||
}
|
||||
if ldap.AutoSync != 0 {
|
||||
object.GetLdapAutoSynchronizer().StartAutoSync(ldap.Id)
|
||||
} else if ldap.AutoSync == 0 && prevLdap.AutoSync != 0{
|
||||
object.GetLdapAutoSynchronizer().StopAutoSync(ldap.Id)
|
||||
}
|
||||
|
||||
c.Data["json"] = resp
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -17,7 +17,7 @@ package controllers
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
type LinkForm struct {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -14,21 +14,22 @@
|
||||
|
||||
package controllers
|
||||
|
||||
import "github.com/casbin/casdoor/object"
|
||||
import "github.com/casdoor/casdoor/object"
|
||||
|
||||
// @Title GetOidcDiscovery
|
||||
// @Tag OIDC API
|
||||
// @router /.well-known/openid-configuration [get]
|
||||
func (c *RootController) GetOidcDiscovery() {
|
||||
c.Data["json"] = object.GetOidcDiscovery()
|
||||
host := c.Ctx.Request.Host
|
||||
c.Data["json"] = object.GetOidcDiscovery(host)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title GetOidcCert
|
||||
// @Title GetJwks
|
||||
// @Tag OIDC API
|
||||
// @router /api/certs [get]
|
||||
func (c *RootController) GetOidcCert() {
|
||||
jwks, err := object.GetJSONWebKeySet()
|
||||
// @router /.well-known/jwks [get]
|
||||
func (c *RootController) GetJwks() {
|
||||
jwks, err := object.GetJsonWebKeySet()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -18,8 +18,8 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetOrganizations ...
|
||||
|
116
controllers/payment.go
Normal file
116
controllers/payment.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetPayments
|
||||
// @Title GetPayments
|
||||
// @Tag Payment API
|
||||
// @Description get payments
|
||||
// @Param owner query string true "The owner of payments"
|
||||
// @Success 200 {array} object.Payment The Response object
|
||||
// @router /get-payments [get]
|
||||
func (c *ApiController) GetPayments() {
|
||||
owner := c.Input().Get("owner")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
if limit == "" || page == "" {
|
||||
c.Data["json"] = object.GetPayments(owner)
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetPaymentCount(owner, field, value)))
|
||||
payments := object.GetPaginationPayments(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
c.ResponseOk(payments, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// @Title GetPayment
|
||||
// @Tag Payment API
|
||||
// @Description get payment
|
||||
// @Param id query string true "The id of the payment"
|
||||
// @Success 200 {object} object.Payment The Response object
|
||||
// @router /get-payment [get]
|
||||
func (c *ApiController) GetPayment() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
c.Data["json"] = object.GetPayment(id)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title UpdatePayment
|
||||
// @Tag Payment API
|
||||
// @Description update payment
|
||||
// @Param id query string true "The id of the payment"
|
||||
// @Param body body object.Payment true "The details of the payment"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-payment [post]
|
||||
func (c *ApiController) UpdatePayment() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var payment object.Payment
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdatePayment(id, &payment))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title AddPayment
|
||||
// @Tag Payment API
|
||||
// @Description add payment
|
||||
// @Param body body object.Payment true "The details of the payment"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-payment [post]
|
||||
func (c *ApiController) AddPayment() {
|
||||
var payment object.Payment
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddPayment(&payment))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title DeletePayment
|
||||
// @Tag Payment API
|
||||
// @Description delete payment
|
||||
// @Param body body object.Payment true "The details of the payment"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-payment [post]
|
||||
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()
|
||||
}
|
116
controllers/permission.go
Normal file
116
controllers/permission.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetPermissions
|
||||
// @Title GetPermissions
|
||||
// @Tag Permission API
|
||||
// @Description get permissions
|
||||
// @Param owner query string true "The owner of permissions"
|
||||
// @Success 200 {array} object.Permission The Response object
|
||||
// @router /get-permissions [get]
|
||||
func (c *ApiController) GetPermissions() {
|
||||
owner := c.Input().Get("owner")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
if limit == "" || page == "" {
|
||||
c.Data["json"] = object.GetPermissions(owner)
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetPermissionCount(owner, field, value)))
|
||||
permissions := object.GetPaginationPermissions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
c.ResponseOk(permissions, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// @Title GetPermission
|
||||
// @Tag Permission API
|
||||
// @Description get permission
|
||||
// @Param id query string true "The id of the permission"
|
||||
// @Success 200 {object} object.Permission The Response object
|
||||
// @router /get-permission [get]
|
||||
func (c *ApiController) GetPermission() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
c.Data["json"] = object.GetPermission(id)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title UpdatePermission
|
||||
// @Tag Permission API
|
||||
// @Description update permission
|
||||
// @Param id query string true "The id of the permission"
|
||||
// @Param body body object.Permission true "The details of the permission"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-permission [post]
|
||||
func (c *ApiController) UpdatePermission() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var permission object.Permission
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permission)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdatePermission(id, &permission))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title AddPermission
|
||||
// @Tag Permission API
|
||||
// @Description add permission
|
||||
// @Param body body object.Permission true "The details of the permission"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-permission [post]
|
||||
func (c *ApiController) AddPermission() {
|
||||
var permission object.Permission
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permission)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddPermission(&permission))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title DeletePermission
|
||||
// @Tag Permission API
|
||||
// @Description delete permission
|
||||
// @Param body body object.Permission true "The details of the permission"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-permission [post]
|
||||
func (c *ApiController) DeletePermission() {
|
||||
var permission object.Permission
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permission)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeletePermission(&permission))
|
||||
c.ServeJSON()
|
||||
}
|
116
controllers/product.go
Normal file
116
controllers/product.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetProducts
|
||||
// @Title GetProducts
|
||||
// @Tag Product API
|
||||
// @Description get products
|
||||
// @Param owner query string true "The owner of products"
|
||||
// @Success 200 {array} object.Product The Response object
|
||||
// @router /get-products [get]
|
||||
func (c *ApiController) GetProducts() {
|
||||
owner := c.Input().Get("owner")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
if limit == "" || page == "" {
|
||||
c.Data["json"] = object.GetProducts(owner)
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetProductCount(owner, field, value)))
|
||||
products := object.GetPaginationProducts(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
c.ResponseOk(products, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// @Title GetProduct
|
||||
// @Tag Product API
|
||||
// @Description get product
|
||||
// @Param id query string true "The id of the product"
|
||||
// @Success 200 {object} object.Product The Response object
|
||||
// @router /get-product [get]
|
||||
func (c *ApiController) GetProduct() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
c.Data["json"] = object.GetProduct(id)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title UpdateProduct
|
||||
// @Tag Product API
|
||||
// @Description update product
|
||||
// @Param id query string true "The id of the product"
|
||||
// @Param body body object.Product true "The details of the product"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-product [post]
|
||||
func (c *ApiController) UpdateProduct() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var product object.Product
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &product)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateProduct(id, &product))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title AddProduct
|
||||
// @Tag Product API
|
||||
// @Description add product
|
||||
// @Param body body object.Product true "The details of the product"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-product [post]
|
||||
func (c *ApiController) AddProduct() {
|
||||
var product object.Product
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &product)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddProduct(&product))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title DeleteProduct
|
||||
// @Tag Product API
|
||||
// @Description delete product
|
||||
// @Param body body object.Product true "The details of the product"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-product [post]
|
||||
func (c *ApiController) DeleteProduct() {
|
||||
var product object.Product
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &product)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteProduct(&product))
|
||||
c.ServeJSON()
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -17,8 +17,8 @@ package controllers
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetProviders
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -16,8 +16,8 @@ package controllers
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetRecords
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -23,8 +23,8 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// @router /get-resources [get]
|
||||
@ -202,7 +202,7 @@ func (c *ApiController) UploadResource() {
|
||||
}
|
||||
|
||||
user.Avatar = fileUrl
|
||||
object.UpdateUser(user.GetId(), user, []string{"avatar"})
|
||||
object.UpdateUser(user.GetId(), user, []string{"avatar"}, false)
|
||||
case "termsOfUse":
|
||||
applicationId := fmt.Sprintf("admin/%s", parent)
|
||||
app := object.GetApplication(applicationId)
|
||||
|
116
controllers/role.go
Normal file
116
controllers/role.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetRoles
|
||||
// @Title GetRoles
|
||||
// @Tag Role API
|
||||
// @Description get roles
|
||||
// @Param owner query string true "The owner of roles"
|
||||
// @Success 200 {array} object.Role The Response object
|
||||
// @router /get-roles [get]
|
||||
func (c *ApiController) GetRoles() {
|
||||
owner := c.Input().Get("owner")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
if limit == "" || page == "" {
|
||||
c.Data["json"] = object.GetRoles(owner)
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetRoleCount(owner, field, value)))
|
||||
roles := object.GetPaginationRoles(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
c.ResponseOk(roles, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// @Title GetRole
|
||||
// @Tag Role API
|
||||
// @Description get role
|
||||
// @Param id query string true "The id of the role"
|
||||
// @Success 200 {object} object.Role The Response object
|
||||
// @router /get-role [get]
|
||||
func (c *ApiController) GetRole() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
c.Data["json"] = object.GetRole(id)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title UpdateRole
|
||||
// @Tag Role API
|
||||
// @Description update role
|
||||
// @Param id query string true "The id of the role"
|
||||
// @Param body body object.Role true "The details of the role"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-role [post]
|
||||
func (c *ApiController) UpdateRole() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var role object.Role
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &role)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateRole(id, &role))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title AddRole
|
||||
// @Tag Role API
|
||||
// @Description add role
|
||||
// @Param body body object.Role true "The details of the role"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-role [post]
|
||||
func (c *ApiController) AddRole() {
|
||||
var role object.Role
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &role)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddRole(&role))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// @Title DeleteRole
|
||||
// @Tag Role API
|
||||
// @Description delete role
|
||||
// @Param body body object.Role true "The details of the role"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-role [post]
|
||||
func (c *ApiController) DeleteRole() {
|
||||
var role object.Role
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &role)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteRole(&role))
|
||||
c.ServeJSON()
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -21,8 +21,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// SendEmail
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -18,8 +18,8 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetSyncers
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -16,10 +16,11 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetTokens
|
||||
@ -142,7 +143,16 @@ func (c *ApiController) GetOAuthCode() {
|
||||
state := c.Input().Get("state")
|
||||
nonce := c.Input().Get("nonce")
|
||||
|
||||
c.Data["json"] = object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce)
|
||||
challengeMethod := c.Input().Get("code_challenge_method")
|
||||
codeChallenge := c.Input().Get("code_challenge")
|
||||
|
||||
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
|
||||
c.ResponseError("Challenge method should be S256")
|
||||
return
|
||||
}
|
||||
host := c.Ctx.Request.Host
|
||||
|
||||
c.Data["json"] = object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce, codeChallenge, host)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@ -161,17 +171,23 @@ func (c *ApiController) GetOAuthToken() {
|
||||
clientId := c.Input().Get("client_id")
|
||||
clientSecret := c.Input().Get("client_secret")
|
||||
code := c.Input().Get("code")
|
||||
verifier := c.Input().Get("code_verifier")
|
||||
scope := c.Input().Get("scope")
|
||||
username := c.Input().Get("username")
|
||||
password := c.Input().Get("password")
|
||||
|
||||
if clientId == "" && clientSecret == "" {
|
||||
clientId, clientSecret, _ = c.Ctx.Request.BasicAuth()
|
||||
}
|
||||
host := c.Ctx.Request.Host
|
||||
|
||||
c.Data["json"] = object.GetOAuthToken(grantType, clientId, clientSecret, code)
|
||||
c.Data["json"] = object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// RefreshToken
|
||||
// @Title RefreshToken
|
||||
// @Tag Token API
|
||||
// @Description refresh OAuth access token
|
||||
// @Param grant_type query string true "OAuth grant type"
|
||||
// @Param refresh_token query string true "OAuth refresh token"
|
||||
@ -186,7 +202,92 @@ func (c *ApiController) RefreshToken() {
|
||||
scope := c.Input().Get("scope")
|
||||
clientId := c.Input().Get("client_id")
|
||||
clientSecret := c.Input().Get("client_secret")
|
||||
host := c.Ctx.Request.Host
|
||||
|
||||
c.Data["json"] = object.RefreshToken(grantType, refreshToken, scope, clientId, clientSecret)
|
||||
c.Data["json"] = object.RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// TokenLogout
|
||||
// @Title TokenLogout
|
||||
// @Tag Token API
|
||||
// @Description delete token by AccessToken
|
||||
// @Param id_token_hint query string true "id_token_hint"
|
||||
// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri"
|
||||
// @Param state query string true "state"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /login/oauth/logout [get]
|
||||
func (c *ApiController) TokenLogout() {
|
||||
token := c.Input().Get("id_token_hint")
|
||||
flag, application := object.DeleteTokenByAceessToken(token)
|
||||
redirectUri := c.Input().Get("post_logout_redirect_uri")
|
||||
state := c.Input().Get("state")
|
||||
if application != nil && object.CheckRedirectUriValid(application, redirectUri) {
|
||||
c.Ctx.Redirect(http.StatusFound, redirectUri+"?state="+state)
|
||||
return
|
||||
}
|
||||
c.Data["json"] = wrapActionResponse(flag)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// IntrospectToken
|
||||
// @Title IntrospectToken
|
||||
// @Description The introspection endpoint is an OAuth 2.0 endpoint that takes a
|
||||
// parameter representing an OAuth 2.0 token and returns a JSON document
|
||||
// representing the meta information surrounding the
|
||||
// token, including whether this token is currently active.
|
||||
// This endpoint only support Basic Authorization.
|
||||
// @Param body body {object.TokenIntrospectionRequest} true "the request body"
|
||||
// @Success 200 {object} object.IntrospectionResponse The Response object
|
||||
// @router /login/oauth/introspect [post]
|
||||
func (c *ApiController) IntrospectToken() {
|
||||
var body object.TokenIntrospectionRequest
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &body)
|
||||
clientId, clientSecret, ok := c.Ctx.Request.BasicAuth()
|
||||
if !ok {
|
||||
util.LogWarning(c.Ctx, "Basic Authorization parses failed")
|
||||
c.Data["json"] = Response{Status: "error", Msg: "Unauthorized operation"}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
application := object.GetApplicationByClientId(clientId)
|
||||
if application == nil || application.ClientSecret != clientSecret {
|
||||
util.LogWarning(c.Ctx, "Basic Authorization failed")
|
||||
c.Data["json"] = Response{Status: "error", Msg: "Unauthorized operation"}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
token := object.GetTokenByTokenAndApplication(body.Token, application.Name)
|
||||
if token == nil {
|
||||
util.LogWarning(c.Ctx, "application: %s can not find token", application.Name)
|
||||
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
jwtToken, err := object.ParseJwtTokenByApplication(body.Token, application)
|
||||
if err != nil || jwtToken.Valid() != nil {
|
||||
// and token revoked case. but we not implement
|
||||
// TODO: 2022-03-03 add token revoked check, when we implemented the Token Revocation(rfc7009) Specs.
|
||||
// refs: https://tools.ietf.org/html/rfc7009
|
||||
util.LogWarning(c.Ctx, "token invalid")
|
||||
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = &object.IntrospectionResponse{
|
||||
Active: true,
|
||||
Scope: jwtToken.Scope,
|
||||
ClientId: clientId,
|
||||
Username: token.User,
|
||||
TokenType: token.TokenType,
|
||||
Exp: jwtToken.ExpiresAt.Unix(),
|
||||
Iat: jwtToken.IssuedAt.Unix(),
|
||||
Nbf: jwtToken.NotBefore.Unix(),
|
||||
Sub: jwtToken.Subject,
|
||||
Aud: jwtToken.Audience,
|
||||
Iss: jwtToken.Issuer,
|
||||
Jti: jwtToken.Id,
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -20,8 +20,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetGlobalUsers
|
||||
@ -44,6 +44,7 @@ func (c *ApiController) GetGlobalUsers() {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetGlobalUserCount(field, value)))
|
||||
users := object.GetPaginationGlobalUsers(paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
users = object.GetMaskedUsers(users)
|
||||
c.ResponseOk(users, paginator.Nums())
|
||||
}
|
||||
}
|
||||
@ -70,6 +71,7 @@ func (c *ApiController) GetUsers() {
|
||||
limit := util.ParseInt(limit)
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetUserCount(owner, field, value)))
|
||||
users := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
users = object.GetMaskedUsers(users)
|
||||
c.ResponseOk(users, paginator.Nums())
|
||||
}
|
||||
}
|
||||
@ -125,7 +127,8 @@ func (c *ApiController) UpdateUser() {
|
||||
columns = strings.Split(columnsStr, ",")
|
||||
}
|
||||
|
||||
affected := object.UpdateUser(id, &user, columns)
|
||||
isGlobalAdmin := c.IsGlobalAdmin()
|
||||
affected := object.UpdateUser(id, &user, columns, isGlobalAdmin)
|
||||
if affected {
|
||||
object.UpdateUserToOriginalDatabase(&user)
|
||||
}
|
||||
@ -226,11 +229,6 @@ func (c *ApiController) SetPassword() {
|
||||
c.ResponseError("Please login first.")
|
||||
return
|
||||
}
|
||||
requestUser := object.GetUser(requestUserId)
|
||||
if requestUser == nil {
|
||||
c.ResponseError("Session outdated. Please login again.")
|
||||
return
|
||||
}
|
||||
|
||||
userId := fmt.Sprintf("%s/%s", userOwner, userName)
|
||||
targetUser := object.GetUser(userId)
|
||||
@ -240,15 +238,22 @@ func (c *ApiController) SetPassword() {
|
||||
}
|
||||
|
||||
hasPermission := false
|
||||
|
||||
if requestUser.IsGlobalAdmin {
|
||||
hasPermission = true
|
||||
} else if requestUserId == userId {
|
||||
hasPermission = true
|
||||
} else if targetUser.Owner == requestUser.Owner && requestUser.IsAdmin {
|
||||
if strings.HasPrefix(requestUserId, "app/") {
|
||||
hasPermission = true
|
||||
} else {
|
||||
requestUser := object.GetUser(requestUserId)
|
||||
if requestUser == nil {
|
||||
c.ResponseError("Session outdated. Please login again.")
|
||||
return
|
||||
}
|
||||
if requestUser.IsGlobalAdmin {
|
||||
hasPermission = true
|
||||
} else if requestUserId == userId {
|
||||
hasPermission = true
|
||||
} else if targetUser.Owner == requestUser.Owner && requestUser.IsAdmin {
|
||||
hasPermission = true
|
||||
}
|
||||
}
|
||||
|
||||
if !hasPermission {
|
||||
c.ResponseError("You don't have the permission to do this.")
|
||||
return
|
||||
@ -272,8 +277,6 @@ func (c *ApiController) SetPassword() {
|
||||
return
|
||||
}
|
||||
|
||||
c.SetSessionUsername("")
|
||||
|
||||
targetUser.Password = newPassword
|
||||
object.SetUserField(targetUser, "password", targetUser.Password)
|
||||
c.Data["json"] = Response{Status: "ok"}
|
||||
|
60
controllers/user_upload.go
Normal file
60
controllers/user_upload.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
func saveFile(path string, file *multipart.File) {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = io.Copy(f, *file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ApiController) UploadUsers() {
|
||||
userId := c.GetSessionUsername()
|
||||
owner, user := util.GetOwnerAndNameFromId(userId)
|
||||
|
||||
file, header, err := c.Ctx.Request.FormFile("file")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename))
|
||||
|
||||
path := util.GetUploadXlsxPath(fileId)
|
||||
util.EnsureFileFolderExists(path)
|
||||
saveFile(path, &file)
|
||||
|
||||
affected := object.UploadUsers(owner, fileId)
|
||||
if affected {
|
||||
c.ResponseOk()
|
||||
} else {
|
||||
c.ResponseError("Failed to import users")
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -19,8 +19,8 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// ResponseOk ...
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -19,8 +19,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
func (c *ApiController) getCurrentUser() *object.User {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -18,8 +18,8 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/astaxie/beego/utils/pagination"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetWebhooks
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,6 +1,7 @@
|
||||
version: '3.1'
|
||||
services:
|
||||
casdoor:
|
||||
restart: always
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: Dockerfile
|
||||
@ -15,6 +16,7 @@ services:
|
||||
db:
|
||||
restart: always
|
||||
image: mysql:8.0.25
|
||||
platform: linux/amd64
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
|
10
go.mod
10
go.mod
@ -1,4 +1,4 @@
|
||||
module github.com/casbin/casdoor
|
||||
module github.com/casdoor/casdoor
|
||||
|
||||
go 1.16
|
||||
|
||||
@ -8,7 +8,7 @@ require (
|
||||
github.com/aws/aws-sdk-go v1.37.30
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
|
||||
github.com/casbin/casbin/v2 v2.30.1
|
||||
github.com/casbin/xorm-adapter/v2 v2.3.1
|
||||
github.com/casbin/xorm-adapter/v2 v2.5.1
|
||||
github.com/casdoor/go-sms-sender v0.0.5
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
|
||||
@ -17,14 +17,17 @@ require (
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/jinzhu/configor v1.2.1 // indirect
|
||||
github.com/lestrrat-go/jwx v0.9.0
|
||||
github.com/markbates/goth v1.68.1-0.20211006204042-9dc8905b41c8
|
||||
github.com/mileusna/crontab v1.0.1
|
||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
||||
github.com/qor/oss v0.0.0-20191031055114-aef9ba66bf76
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
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/smartystreets/goconvey v1.6.4 // indirect
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tealeg/xlsx v1.0.5
|
||||
github.com/thanhpk/randstr v1.0.4
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
|
||||
@ -34,6 +37,7 @@ require (
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||
xorm.io/core v0.7.2
|
||||
xorm.io/xorm v1.0.3
|
||||
)
|
||||
|
18
go.sum
18
go.sum
@ -74,11 +74,11 @@ github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6
|
||||
github.com/casbin/casbin v1.7.0 h1:PuzlE8w0JBg/DhIqnkF1Dewf3z+qmUZMVN07PonvVUQ=
|
||||
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
|
||||
github.com/casbin/casbin/v2 v2.1.0/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/casbin/casbin/v2 v2.25.5/go.mod h1:wUgota0cQbTXE6Vd+KWpg41726jFRi7upxio0sR+Xd0=
|
||||
github.com/casbin/casbin/v2 v2.28.3/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
||||
github.com/casbin/casbin/v2 v2.30.1 h1:P5HWadDL7olwUXNdcuKUBk+x75Y2eitFxYTcLNKeKF0=
|
||||
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/go.mod h1:GZ+nlIdasVFunQ71SlvkL/HcQQBvFncphDf+2Yl167c=
|
||||
github.com/casbin/xorm-adapter/v2 v2.5.1 h1:BkpIxRHKa0s3bSMx173PpuU7oTs+Zw7XmD0BIta0HGM=
|
||||
github.com/casbin/xorm-adapter/v2 v2.5.1/go.mod h1:AeH4dBKHC9/zYxzdPVHhPDzF8LYLqjDdb767CWJoV54=
|
||||
github.com/casdoor/go-sms-sender v0.0.5 h1:9qhlMM+UoSOvvY7puUULqSHBBA7fbe02Px/tzchQboo=
|
||||
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=
|
||||
@ -264,8 +264,6 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJK
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mileusna/crontab v1.0.1 h1:YrDLc7l3xOiznmXq2FtAgg+1YQ3yC6pfFVPe+ywXNtg=
|
||||
github.com/mileusna/crontab v1.0.1/go.mod h1:dbns64w/u3tUnGZGf8pAa76ZqOfeBX4olW4U1ZwExmc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -313,6 +311,8 @@ github.com/qiangmzsx/string-adapter/v2 v2.1.0 h1:q0y8TPa/sTwtriJPRe8gWL++PuZ+XbO
|
||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0/go.mod h1:PElPB7b7HnGKTsuADAffFpOQXHqjEGJz1+U1a6yR5wA=
|
||||
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/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
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=
|
||||
@ -343,12 +343,15 @@ 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.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.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/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-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
|
||||
github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v1.0.154 h1:THBgwGwUQtsw6L53cSSA2wwL3sLrm+HJ3Dk+ye/lMCI=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v1.0.154/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI=
|
||||
github.com/thanhpk/randstr v1.0.4 h1:IN78qu/bR+My+gHCvMEXhR/i5oriVHcTB/BJJIRTsNo=
|
||||
@ -665,8 +668,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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=
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -21,7 +21,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
type I18nData map[string]map[string]string
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -18,7 +18,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
func getI18nFilePath(language string) string {
|
||||
|
136
idp/adfs.go
Normal file
136
idp/adfs.go
Normal file
@ -0,0 +1,136 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package idp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type AdfsIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
Host string
|
||||
}
|
||||
|
||||
func NewAdfsIdProvider(clientId string, clientSecret string, redirectUrl string, hostUrl string) *AdfsIdProvider {
|
||||
idp := &AdfsIdProvider{}
|
||||
|
||||
config := idp.getConfig(hostUrl)
|
||||
config.ClientID = clientId
|
||||
config.ClientSecret = clientSecret
|
||||
config.RedirectURL = redirectUrl
|
||||
idp.Config = config
|
||||
idp.Host = hostUrl
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *AdfsIdProvider) SetHttpClient(client *http.Client) {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
idp.Client = client
|
||||
idp.Client.Transport = tr
|
||||
}
|
||||
|
||||
func (idp *AdfsIdProvider) getConfig(hostUrl string) *oauth2.Config {
|
||||
var endpoint = oauth2.Endpoint{
|
||||
AuthURL: fmt.Sprintf("%s/adfs/oauth2/authorize", hostUrl),
|
||||
TokenURL: fmt.Sprintf("%s/adfs/oauth2/token", hostUrl),
|
||||
}
|
||||
|
||||
var config = &oauth2.Config{
|
||||
Endpoint: endpoint,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type AdfsToken struct {
|
||||
IdToken string `json:"id_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
ErrMsg string `json:"error_description"`
|
||||
}
|
||||
|
||||
// get more detail via: https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/overview/ad-fs-openid-connect-oauth-flows-scenarios#request-an-access-token
|
||||
func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
payload := url.Values{}
|
||||
payload.Set("code", code)
|
||||
payload.Set("grant_type", "authorization_code")
|
||||
payload.Set("client_id", idp.Config.ClientID)
|
||||
payload.Set("redirect_uri", idp.Config.RedirectURL)
|
||||
resp, err := idp.Client.PostForm(idp.Config.Endpoint.TokenURL, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pToken := &AdfsToken{}
|
||||
err = json.Unmarshal(data, pToken)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to unmarshal token response: %s", err.Error())
|
||||
}
|
||||
if pToken.ErrMsg != "" {
|
||||
return nil, fmt.Errorf("pToken.Errmsg = %s", pToken.ErrMsg)
|
||||
}
|
||||
|
||||
token := &oauth2.Token{
|
||||
AccessToken: pToken.IdToken,
|
||||
Expiry: time.Unix(time.Now().Unix()+int64(pToken.ExpiresIn), 0),
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// Since the userinfo endpoint of ADFS only returns sub,
|
||||
// the id_token is used to resolve the userinfo
|
||||
func (idp *AdfsIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
resp, err := idp.Client.Get(fmt.Sprintf("%s/adfs/discovery/keys", idp.Host))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyset, err := jwk.Parse(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenSrc := []byte(token.AccessToken)
|
||||
publicKey, _ := keyset.Keys[0].Materialize()
|
||||
id_token, _ := jwt.Parse(bytes.NewReader(tokenSrc), jwt.WithVerify(jwa.RS256, publicKey))
|
||||
sid, _ := id_token.Get("sid")
|
||||
upn, _ := id_token.Get("upn")
|
||||
name, _ := id_token.Get("unique_name")
|
||||
userinfo := &UserInfo{
|
||||
Id: sid.(string),
|
||||
Username: name.(string),
|
||||
DisplayName: name.(string),
|
||||
Email: upn.(string),
|
||||
}
|
||||
return userinfo, nil
|
||||
}
|
116
idp/baidu.go
Normal file
116
idp/baidu.go
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package idp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type BaiduIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
}
|
||||
|
||||
func NewBaiduIdProvider(clientId string, clientSecret string, redirectUrl string) *BaiduIdProvider {
|
||||
idp := &BaiduIdProvider{}
|
||||
|
||||
config := idp.getConfig()
|
||||
config.ClientID = clientId
|
||||
config.ClientSecret = clientSecret
|
||||
config.RedirectURL = redirectUrl
|
||||
idp.Config = config
|
||||
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *BaiduIdProvider) SetHttpClient(client *http.Client) {
|
||||
idp.Client = client
|
||||
}
|
||||
|
||||
func (idp *BaiduIdProvider) getConfig() *oauth2.Config {
|
||||
var endpoint = oauth2.Endpoint{
|
||||
AuthURL: "https://openapi.baidu.com/oauth/2.0/authorize",
|
||||
TokenURL: "https://openapi.baidu.com/oauth/2.0/token",
|
||||
}
|
||||
|
||||
var config = &oauth2.Config{
|
||||
Scopes: []string{"email"},
|
||||
Endpoint: endpoint,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func (idp *BaiduIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, idp.Client)
|
||||
return idp.Config.Exchange(ctx, code)
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"userid":"2097322476",
|
||||
"username":"wl19871011",
|
||||
"realname":"阳光",
|
||||
"userdetail":"喜欢自由",
|
||||
"birthday":"1987-01-01",
|
||||
"marriage":"恋爱",
|
||||
"sex":"男",
|
||||
"blood":"O",
|
||||
"constellation":"射手",
|
||||
"figure":"小巧",
|
||||
"education":"大学/专科",
|
||||
"trade":"计算机/电子产品",
|
||||
"job":"未知",
|
||||
"birthday_year":"1987",
|
||||
"birthday_month":"01",
|
||||
"birthday_day":"01",
|
||||
}
|
||||
*/
|
||||
|
||||
type BaiduUserInfo struct {
|
||||
OpenId string `json:"openid"`
|
||||
Username string `json:"username"`
|
||||
Portrait string `json:"portrait"`
|
||||
}
|
||||
|
||||
func (idp *BaiduIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
resp, err := idp.Client.Get(fmt.Sprintf("https://openapi.baidu.com/rest/2.0/passport/users/getInfo?access_token=%s", token.AccessToken))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
baiduUser := BaiduUserInfo{}
|
||||
if err = json.Unmarshal(data, &baiduUser); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userInfo := UserInfo{
|
||||
Id: baiduUser.OpenId,
|
||||
Username: baiduUser.Username,
|
||||
DisplayName: baiduUser.Username,
|
||||
AvatarUrl: fmt.Sprintf("https://himg.bdimg.com/sys/portrait/item/%s", baiduUser.Portrait),
|
||||
}
|
||||
return &userInfo, nil
|
||||
}
|
322
idp/dingtalk.go
322
idp/dingtalk.go
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -15,32 +15,16 @@
|
||||
package idp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// A total of three steps are required:
|
||||
//
|
||||
// 1. Construct the link and get the temporary authorization code
|
||||
// tmp_auth_code through the code at the end of the url.
|
||||
//
|
||||
// 2. Use hmac256 to calculate the signature, and then submit it together with timestamp,
|
||||
// tmp_auth_code, accessKey to obtain unionid, userid, accessKey.
|
||||
//
|
||||
// 3. Get detailed information through userid.
|
||||
|
||||
type DingTalkIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
@ -64,8 +48,8 @@ func (idp *DingTalkIdProvider) SetHttpClient(client *http.Client) {
|
||||
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
|
||||
func (idp *DingTalkIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
|
||||
var endpoint = oauth2.Endpoint{
|
||||
AuthURL: "https://oapi.dingtalk.com/sns/getuserinfo_bycode",
|
||||
TokenURL: "https://oapi.dingtalk.com/gettoken",
|
||||
AuthURL: "https://api.dingtalk.com/v1.0/contact/users/me",
|
||||
TokenURL: "https://api.dingtalk.com/v1.0/oauth2/userAccessToken",
|
||||
}
|
||||
|
||||
var config = &oauth2.Config{
|
||||
@ -83,256 +67,121 @@ func (idp *DingTalkIdProvider) getConfig(clientId string, clientSecret string, r
|
||||
}
|
||||
|
||||
type DingTalkAccessToken struct {
|
||||
ErrCode int `json:"errcode"`
|
||||
ErrMsg string `json:"errmsg"`
|
||||
AccessToken string `json:"access_token"` // Interface call credentials
|
||||
ExpiresIn int64 `json:"expires_in"` // access_token interface call credential timeout time, unit (seconds)
|
||||
ErrCode int `json:"code"`
|
||||
ErrMsg string `json:"message"`
|
||||
AccessToken string `json:"accessToken"` // Interface call credentials
|
||||
ExpiresIn int64 `json:"expireIn"` // access_token interface call credential timeout time, unit (seconds)
|
||||
}
|
||||
|
||||
type DingTalkIds struct {
|
||||
UserId string `json:"user_id"`
|
||||
UnionId string `json:"union_id"`
|
||||
}
|
||||
|
||||
type InfoResp struct {
|
||||
Errcode int `json:"errcode"`
|
||||
UserInfo struct {
|
||||
Nick string `json:"nick"`
|
||||
Unionid string `json:"unionid"`
|
||||
Openid string `json:"openid"`
|
||||
MainOrgAuthHighLevel bool `json:"main_org_auth_high_level"`
|
||||
} `json:"user_info"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
}
|
||||
|
||||
// GetToken use code get access_token (*operation of getting code ought to be done in front)
|
||||
// get more detail via: https://developers.dingtalk.com/document/app/dingtalk-retrieve-user-information?spm=ding_open_doc.document.0.0.51b91a31wWV3tY#doc-api-dingtalk-GetUser
|
||||
// GetToken use code get access_token (*operation of getting authCode ought to be done in front)
|
||||
// get more detail via: https://open.dingtalk.com/document/orgapp-server/obtain-user-token
|
||||
func (idp *DingTalkIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
timestamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
|
||||
signature := EncodeSHA256(timestamp, idp.Config.ClientSecret)
|
||||
u := fmt.Sprintf(
|
||||
"%s?accessKey=%s×tamp=%s&signature=%s", idp.Config.Endpoint.AuthURL,
|
||||
idp.Config.ClientID, timestamp, signature)
|
||||
pTokenParams := &struct {
|
||||
ClientId string `json:"clientId"`
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
Code string `json:"code"`
|
||||
GrantType string `json:"grantType"`
|
||||
}{idp.Config.ClientID, idp.Config.ClientSecret, code, "authorization_code"}
|
||||
|
||||
tmpCode := struct {
|
||||
TmpAuthCode string `json:"tmp_auth_code"`
|
||||
}{code}
|
||||
bs, _ := json.Marshal(tmpCode)
|
||||
r := strings.NewReader(string(bs))
|
||||
resp, err := http.Post(u, "application/json;charset=UTF-8", r)
|
||||
data, err := idp.postWithBody(pTokenParams, idp.Config.Endpoint.TokenURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
info := InfoResp{}
|
||||
_ = json.Unmarshal(body, &info)
|
||||
errCode := info.Errcode
|
||||
if errCode != 0 {
|
||||
return nil, fmt.Errorf("%d: %s", errCode, info.Errmsg)
|
||||
}
|
||||
|
||||
u2 := fmt.Sprintf("%s?appkey=%s&appsecret=%s", idp.Config.Endpoint.TokenURL, idp.Config.ClientID, idp.Config.ClientSecret)
|
||||
resp, _ = http.Get(u2)
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(resp.Body)
|
||||
body, _ = io.ReadAll(resp.Body)
|
||||
tokenResp := DingTalkAccessToken{}
|
||||
_ = json.Unmarshal(body, &tokenResp)
|
||||
if tokenResp.ErrCode != 0 {
|
||||
return nil, fmt.Errorf("%d: %s", tokenResp.ErrCode, tokenResp.ErrMsg)
|
||||
}
|
||||
|
||||
// use unionid to get userid
|
||||
unionid := info.UserInfo.Unionid
|
||||
userid, err := idp.GetUseridByUnionid(tokenResp.AccessToken, unionid)
|
||||
pToken := &DingTalkAccessToken{}
|
||||
err = json.Unmarshal(data, pToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Since DingTalk does not require scopes, put userid and unionid into
|
||||
// idp.config.scopes to facilitate GetUserInfo() to obtain these two parameters.
|
||||
idp.Config.Scopes = []string{unionid, userid}
|
||||
if pToken.ErrCode != 0 {
|
||||
return nil, fmt.Errorf("pToken.Errcode = %d, pToken.Errmsg = %s", pToken.ErrCode, pToken.ErrMsg)
|
||||
}
|
||||
|
||||
token := &oauth2.Token{
|
||||
AccessToken: tokenResp.AccessToken,
|
||||
Expiry: time.Unix(time.Now().Unix()+tokenResp.ExpiresIn, 0),
|
||||
AccessToken: pToken.AccessToken,
|
||||
Expiry: time.Unix(time.Now().Unix()+int64(pToken.ExpiresIn), 0),
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
type UnionIdResponse struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
Result struct {
|
||||
ContactType string `json:"contact_type"`
|
||||
Userid string `json:"userid"`
|
||||
} `json:"result"`
|
||||
RequestId string `json:"request_id"`
|
||||
}
|
||||
|
||||
// GetUseridByUnionid ...
|
||||
func (idp *DingTalkIdProvider) GetUseridByUnionid(accesstoken, unionid string) (userid string, err error) {
|
||||
u := fmt.Sprintf("https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token=%s&unionid=%s",
|
||||
accesstoken, unionid)
|
||||
useridInfo, err := idp.GetUrlResp(u)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
uresp := UnionIdResponse{}
|
||||
_ = json.Unmarshal([]byte(useridInfo), &uresp)
|
||||
errcode := uresp.Errcode
|
||||
if errcode != 0 {
|
||||
return "", fmt.Errorf("%d: %s", errcode, uresp.Errmsg)
|
||||
}
|
||||
return uresp.Result.Userid, nil
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"errcode":0,
|
||||
"result":{
|
||||
"boss":false,
|
||||
"unionid":"5M6zgZBKQPCxdiPdANeJ6MgiEiE",
|
||||
"role_list":[
|
||||
{
|
||||
"group_name":"默认",
|
||||
"name":"主管理员",
|
||||
"id":2062489174
|
||||
}
|
||||
],
|
||||
"exclusive_account":false,
|
||||
"mobile":"15236176076",
|
||||
"active":true,
|
||||
"admin":true,
|
||||
"avatar":"https://static-legacy.dingtalk.com/media/lALPDeRETW9WAnnNAyDNAyA_800_800.png",
|
||||
"hide_mobile":false,
|
||||
"userid":"manager4713",
|
||||
"senior":false,
|
||||
"dept_order_list":[
|
||||
{
|
||||
"dept_id":1,
|
||||
"order":176294576350761512
|
||||
}
|
||||
],
|
||||
"real_authed":true,
|
||||
"name":"刘继坤",
|
||||
"dept_id_list":[
|
||||
1
|
||||
],
|
||||
"state_code":"86",
|
||||
"email":"",
|
||||
"leader_in_dept":[
|
||||
{
|
||||
"leader":false,
|
||||
"dept_id":1
|
||||
}
|
||||
]
|
||||
},
|
||||
"errmsg":"ok",
|
||||
"request_id":"3sug9d2exsla"
|
||||
{
|
||||
"nick" : "zhangsan",
|
||||
"avatarUrl" : "https://xxx",
|
||||
"mobile" : "150xxxx9144",
|
||||
"openId" : "123",
|
||||
"unionId" : "z21HjQliSzpw0Yxxxx",
|
||||
"email" : "zhangsan@alibaba-inc.com",
|
||||
"stateCode" : "86"
|
||||
}
|
||||
*/
|
||||
|
||||
type DingTalkUserResponse struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
Result struct {
|
||||
Extension string `json:"extension"`
|
||||
Unionid string `json:"unionid"`
|
||||
Boss bool `json:"boss"`
|
||||
UnionEmpExt struct {
|
||||
CorpId string `json:"corpId"`
|
||||
Userid string `json:"userid"`
|
||||
UnionEmpMapList []struct {
|
||||
CorpId string `json:"corpId"`
|
||||
Userid string `json:"userid"`
|
||||
} `json:"unionEmpMapList"`
|
||||
} `json:"unionEmpExt"`
|
||||
RoleList []struct {
|
||||
GroupName string `json:"group_name"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
} `json:"role_list"`
|
||||
Admin bool `json:"admin"`
|
||||
Remark string `json:"remark"`
|
||||
Title string `json:"title"`
|
||||
HiredDate int64 `json:"hired_date"`
|
||||
Userid string `json:"userid"`
|
||||
WorkPlace string `json:"work_place"`
|
||||
DeptOrderList []struct {
|
||||
DeptId int `json:"dept_id"`
|
||||
Order int64 `json:"order"`
|
||||
} `json:"dept_order_list"`
|
||||
RealAuthed bool `json:"real_authed"`
|
||||
DeptIdList []int `json:"dept_id_list"`
|
||||
JobNumber string `json:"job_number"`
|
||||
Email string `json:"email"`
|
||||
LeaderInDept []struct {
|
||||
DeptId int `json:"dept_id"`
|
||||
Leader bool `json:"leader"`
|
||||
} `json:"leader_in_dept"`
|
||||
ManagerUserid string `json:"manager_userid"`
|
||||
Mobile string `json:"mobile"`
|
||||
Active bool `json:"active"`
|
||||
Telephone string `json:"telephone"`
|
||||
Avatar string `json:"avatar"`
|
||||
HideMobile bool `json:"hide_mobile"`
|
||||
Senior bool `json:"senior"`
|
||||
Name string `json:"name"`
|
||||
StateCode string `json:"state_code"`
|
||||
} `json:"result"`
|
||||
RequestId string `json:"request_id"`
|
||||
Nick string `json:"nick"`
|
||||
OpenId string `json:"openId"`
|
||||
AvatarUrl string `json:"avatarUrl"`
|
||||
Email string `json:"email"`
|
||||
Errmsg string `json:"message"`
|
||||
Errcode string `json:"code"`
|
||||
}
|
||||
|
||||
// GetUserInfo Use userid and access_token to get UserInfo
|
||||
// get more detail via: https://developers.dingtalk.com/document/app/query-user-details
|
||||
// GetUserInfo Use access_token to get UserInfo
|
||||
// get more detail via: https://open.dingtalk.com/document/orgapp-server/dingtalk-retrieve-user-information
|
||||
func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
var dtUserInfo DingTalkUserResponse
|
||||
dtUserInfo := &DingTalkUserResponse{}
|
||||
accessToken := token.AccessToken
|
||||
|
||||
u := fmt.Sprintf("https://oapi.dingtalk.com/topapi/v2/user/get?access_token=%s&userid=%s",
|
||||
accessToken, idp.Config.Scopes[1])
|
||||
|
||||
userinfoResp, err := idp.GetUrlResp(u)
|
||||
reqest, err := http.NewRequest("GET", idp.Config.Endpoint.AuthURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reqest.Header.Add("x-acs-dingtalk-access-token", accessToken)
|
||||
resp, err := idp.Client.Do(reqest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal([]byte(userinfoResp), &dtUserInfo); err != nil {
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, dtUserInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dtUserInfo.Errmsg != "" {
|
||||
return nil, fmt.Errorf("userIdResp.Errcode = %s, userIdResp.Errmsg = %s", dtUserInfo.Errcode, dtUserInfo.Errmsg)
|
||||
}
|
||||
|
||||
userInfo := UserInfo{
|
||||
Id: strconv.Itoa(dtUserInfo.Result.RoleList[0].Id),
|
||||
Username: dtUserInfo.Result.RoleList[0].Name,
|
||||
DisplayName: dtUserInfo.Result.Name,
|
||||
Email: dtUserInfo.Result.Email,
|
||||
AvatarUrl: dtUserInfo.Result.Avatar,
|
||||
Id: dtUserInfo.OpenId,
|
||||
Username: dtUserInfo.Nick,
|
||||
DisplayName: dtUserInfo.Nick,
|
||||
Email: dtUserInfo.Email,
|
||||
AvatarUrl: dtUserInfo.AvatarUrl,
|
||||
}
|
||||
|
||||
return &userInfo, nil
|
||||
}
|
||||
|
||||
func (idp *DingTalkIdProvider) GetUrlResp(url string) (string, error) {
|
||||
resp, err := idp.Client.Get(url)
|
||||
func (idp *DingTalkIdProvider) postWithBody(body interface{}, url string) ([]byte, error) {
|
||||
bs, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
r := strings.NewReader(string(bs))
|
||||
resp, err := idp.Client.Post(url, "application/json;charset=UTF-8", r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
@ -340,26 +189,5 @@ func (idp *DingTalkIdProvider) GetUrlResp(url string) (string, error) {
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = buf.ReadFrom(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// EncodeSHA256 Use the HmacSHA256 algorithm to sign, the signature data is the current timestamp,
|
||||
// and the key is the appSecret corresponding to the appId. Use this key to calculate the timestamp signature value.
|
||||
// get more detail via: https://developers.dingtalk.com/document/app/signature-calculation-for-logon-free-scenarios-1?spm=ding_open_doc.document.0.0.63262ea7l6iEm1#topic-2021698
|
||||
func EncodeSHA256(message, secret string) string {
|
||||
h := hmac.New(sha256.New, []byte(secret))
|
||||
h.Write([]byte(message))
|
||||
sum := h.Sum(nil)
|
||||
msg1 := base64.StdEncoding.EncodeToString(sum)
|
||||
|
||||
uv := url.Values{}
|
||||
uv.Add("0", msg1)
|
||||
msg2 := uv.Encode()[2:]
|
||||
return msg2
|
||||
return data, nil
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -164,6 +164,7 @@ func (idp *FacebookIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
|
||||
userInfo := UserInfo{
|
||||
Id: facebookUserInfo.Id,
|
||||
Username: facebookUserInfo.Name,
|
||||
DisplayName: facebookUserInfo.Name,
|
||||
Email: facebookUserInfo.Email,
|
||||
AvatarUrl: facebookUserInfo.Picture.Data.Url,
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
38
idp/goth.go
38
idp/goth.go
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -21,7 +21,7 @@ import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/providers/amazon"
|
||||
"github.com/markbates/goth/providers/apple"
|
||||
@ -45,6 +45,7 @@ import (
|
||||
"github.com/markbates/goth/providers/salesforce"
|
||||
"github.com/markbates/goth/providers/shopify"
|
||||
"github.com/markbates/goth/providers/slack"
|
||||
"github.com/markbates/goth/providers/steam"
|
||||
"github.com/markbates/goth/providers/tumblr"
|
||||
"github.com/markbates/goth/providers/twitter"
|
||||
"github.com/markbates/goth/providers/yahoo"
|
||||
@ -171,6 +172,11 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
|
||||
Provider: slack.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &slack.Session{},
|
||||
}
|
||||
case "Steam":
|
||||
idp = GothIdProvider{
|
||||
Provider: steam.New(clientSecret, redirectUrl),
|
||||
Session: &steam.Session{},
|
||||
}
|
||||
case "Tumblr":
|
||||
idp = GothIdProvider{
|
||||
Provider: tumblr.New(clientId, clientSecret, redirectUrl),
|
||||
@ -209,10 +215,21 @@ func (idp *GothIdProvider) SetHttpClient(client *http.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)
|
||||
var value url.Values
|
||||
var err error
|
||||
if idp.Provider.Name() == "steam" {
|
||||
value, err = url.ParseQuery(code)
|
||||
returnUrl := reflect.ValueOf(idp.Session).Elem().FieldByName("CallbackURL")
|
||||
returnUrl.Set(reflect.ValueOf(value.Get("openid.return_to")))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
//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")
|
||||
@ -231,10 +248,10 @@ func (idp *GothIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getUser(gothUser), nil
|
||||
return getUser(gothUser, idp.Provider.Name()), nil
|
||||
}
|
||||
|
||||
func getUser(gothUser goth.User) *UserInfo {
|
||||
func getUser(gothUser goth.User, provider string) *UserInfo {
|
||||
user := UserInfo{
|
||||
Id: gothUser.UserID,
|
||||
Username: gothUser.Name,
|
||||
@ -258,7 +275,10 @@ func getUser(gothUser goth.User) *UserInfo {
|
||||
user.DisplayName = user.Username
|
||||
}
|
||||
}
|
||||
|
||||
if provider == "steam" {
|
||||
user.Username = user.DisplayName
|
||||
user.Email = ""
|
||||
}
|
||||
return &user
|
||||
}
|
||||
|
||||
|
192
idp/infoflow_internal.go
Normal file
192
idp/infoflow_internal.go
Normal file
@ -0,0 +1,192 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package idp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type InfoflowInternalIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
AgentId string
|
||||
}
|
||||
|
||||
func NewInfoflowInternalIdProvider(clientId string, clientSecret string, appId string, redirectUrl string) *InfoflowInternalIdProvider {
|
||||
idp := &InfoflowInternalIdProvider{}
|
||||
|
||||
config := idp.getConfig(clientId, clientSecret, redirectUrl)
|
||||
idp.Config = config
|
||||
idp.AgentId = appId
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *InfoflowInternalIdProvider) SetHttpClient(client *http.Client) {
|
||||
idp.Client = client
|
||||
}
|
||||
|
||||
func (idp *InfoflowInternalIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
|
||||
var config = &oauth2.Config{
|
||||
ClientID: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirectUrl,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type InfoflowInterToken struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
// get more detail via: https://qy.baidu.com/doc/index.html#/inner_quickstart/flow?id=%E8%8E%B7%E5%8F%96accesstoken
|
||||
func (idp *InfoflowInternalIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
pTokenParams := &struct {
|
||||
CorpId string `json:"corpid"`
|
||||
Corpsecret string `json:"corpsecret"`
|
||||
}{idp.Config.ClientID, idp.Config.ClientSecret}
|
||||
resp, err := idp.Client.Get(fmt.Sprintf("https://qy.im.baidu.com/api/gettoken?corpid=%s&corpsecret=%s", pTokenParams.CorpId, pTokenParams.Corpsecret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pToken := &InfoflowInterToken{}
|
||||
err = json.Unmarshal(data, pToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pToken.Errcode != 0 {
|
||||
return nil, fmt.Errorf("pToken.Errcode = %d, pToken.Errmsg = %s", pToken.Errcode, pToken.Errmsg)
|
||||
}
|
||||
token := &oauth2.Token{
|
||||
AccessToken: pToken.AccessToken,
|
||||
}
|
||||
|
||||
raw := make(map[string]interface{})
|
||||
raw["code"] = code
|
||||
token = token.WithExtra(raw)
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"errcode": 0,
|
||||
"errmsg": "ok",
|
||||
"userid": "lili",
|
||||
"name": "丽丽",
|
||||
"department": [1],
|
||||
"mobile": "13500088888",
|
||||
"email": "lili4@gzdev.com",
|
||||
"imid": 40000318,
|
||||
"hiuname": "lili4",
|
||||
"status": 1,
|
||||
"extattr":
|
||||
{
|
||||
"attrs": [
|
||||
{
|
||||
"name": "爱好",
|
||||
"value": "旅游"
|
||||
},
|
||||
{
|
||||
"name": "卡号,
|
||||
"value": "1234567234"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lm": 14236463257
|
||||
}
|
||||
*/
|
||||
|
||||
type InfoflowInternalUserResp struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
UserId string `json:"UserId"`
|
||||
}
|
||||
|
||||
type InfoflowInternalUserInfo struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
UserId string `json:"userid"`
|
||||
Imid int `json:"imid"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"headimg"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// get more detail via: https://qy.baidu.com/doc/index.html#/inner_serverapi/contacts?id=%e8%8e%b7%e5%8f%96%e6%88%90%e5%91%98
|
||||
func (idp *InfoflowInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
//Get userid first
|
||||
accessToken := token.AccessToken
|
||||
code := token.Extra("code").(string)
|
||||
resp, err := idp.Client.Get(fmt.Sprintf("https://qy.im.baidu.com/api/user/getuserinfo?access_token=%s&code=%s&agentid=%s", accessToken, code, idp.AgentId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userResp := &InfoflowInternalUserResp{}
|
||||
err = json.Unmarshal(data, userResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userResp.Errcode != 0 {
|
||||
return nil, fmt.Errorf("userIdResp.Errcode = %d, userIdResp.Errmsg = %s", userResp.Errcode, userResp.Errmsg)
|
||||
}
|
||||
//Use userid and accesstoken to get user information
|
||||
resp, err = idp.Client.Get(fmt.Sprintf("https://api.im.baidu.com/api/user/get?access_token=%s&userid=%s", accessToken, userResp.UserId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infoResp := &InfoflowInternalUserInfo{}
|
||||
err = json.Unmarshal(data, infoResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if infoResp.Errcode != 0 {
|
||||
return nil, fmt.Errorf("userInfoResp.errcode = %d, userInfoResp.errmsg = %s", infoResp.Errcode, infoResp.Errmsg)
|
||||
}
|
||||
userInfo := UserInfo{
|
||||
Id: infoResp.UserId,
|
||||
Username: infoResp.UserId,
|
||||
DisplayName: infoResp.Name,
|
||||
AvatarUrl: infoResp.Avatar,
|
||||
Email: infoResp.Email,
|
||||
}
|
||||
|
||||
if userInfo.Id == "" {
|
||||
userInfo.Id = userInfo.Username
|
||||
}
|
||||
return &userInfo, nil
|
||||
}
|
211
idp/infoflow_third_party.go
Normal file
211
idp/infoflow_third_party.go
Normal file
@ -0,0 +1,211 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package idp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type InfoflowIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
AgentId string
|
||||
Ticket string
|
||||
}
|
||||
|
||||
func NewInfoflowIdProvider(clientId string, clientSecret string, appId string, redirectUrl string) *InfoflowIdProvider {
|
||||
idp := &InfoflowIdProvider{}
|
||||
|
||||
config := idp.getConfig(clientId, clientSecret, redirectUrl)
|
||||
idp.Config = config
|
||||
idp.AgentId = appId
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *InfoflowIdProvider) SetHttpClient(client *http.Client) {
|
||||
idp.Client = client
|
||||
}
|
||||
|
||||
func (idp *InfoflowIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
|
||||
var config = &oauth2.Config{
|
||||
ClientID: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirectUrl,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type InfoflowToken struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
AccessToken string `json:"suite_access_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
}
|
||||
|
||||
// get more detail via: https://qy.baidu.com/doc/index.html#/third_serverapi/authority
|
||||
func (idp *InfoflowIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
pTokenParams := &struct {
|
||||
SuiteId string `json:"suite_id"`
|
||||
SuiteSecret string `json:"suite_secret"`
|
||||
SuiteTicket string `json:"suite_ticket"`
|
||||
}{idp.Config.ClientID, idp.Config.ClientSecret, idp.Ticket}
|
||||
data, err := idp.postWithBody(pTokenParams, "https://api.im.baidu.com/api/service/get_suite_token")
|
||||
|
||||
pToken := &InfoflowToken{}
|
||||
err = json.Unmarshal(data, pToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pToken.Errcode != 0 {
|
||||
return nil, fmt.Errorf("pToken.Errcode = %d, pToken.Errmsg = %s", pToken.Errcode, pToken.Errmsg)
|
||||
}
|
||||
token := &oauth2.Token{
|
||||
AccessToken: pToken.AccessToken,
|
||||
Expiry: time.Unix(time.Now().Unix()+int64(pToken.ExpiresIn), 0),
|
||||
}
|
||||
|
||||
raw := make(map[string]interface{})
|
||||
raw["code"] = code
|
||||
token = token.WithExtra(raw)
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"errcode": 0,
|
||||
"errmsg": "ok",
|
||||
"userid": "lili",
|
||||
"name": "丽丽",
|
||||
"department": [1],
|
||||
"mobile": "13500088888",
|
||||
"email": "lili4@gzdev.com",
|
||||
"imid": 40000318,
|
||||
"hiuname": "lili4",
|
||||
"status": 1,
|
||||
"extattr": {
|
||||
"attrs": [
|
||||
{
|
||||
"name": "爱好",
|
||||
"value": "旅游"
|
||||
},
|
||||
{
|
||||
"name": "卡号",
|
||||
"value": "1234567234"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lm" : 14236463257
|
||||
}
|
||||
*/
|
||||
|
||||
type InfoflowUserResp struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
UserId string `json:"UserId"`
|
||||
}
|
||||
|
||||
type InfoflowUserInfo struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
Imid string `json:"imid"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// get more detail via: https://qy.baidu.com/doc/index.html#/third_serverapi/contacts?id=%e8%8e%b7%e5%8f%96%e6%88%90%e5%91%98
|
||||
func (idp *InfoflowIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
//Get userid first
|
||||
accessToken := token.AccessToken
|
||||
code := token.Extra("code").(string)
|
||||
resp, err := idp.Client.Get(fmt.Sprintf("https://api.im.baidu.com/api/user/getuserinfo?access_token=%s&code=%s&agentid=%s", accessToken, code, idp.AgentId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userResp := &InfoflowUserResp{}
|
||||
err = json.Unmarshal(data, userResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userResp.Errcode != 0 {
|
||||
return nil, fmt.Errorf("userIdResp.Errcode = %d, userIdResp.Errmsg = %s", userResp.Errcode, userResp.Errmsg)
|
||||
}
|
||||
//Use userid and accesstoken to get user information
|
||||
resp, err = idp.Client.Get(fmt.Sprintf("https://api.im.baidu.com/api/user/get?access_token=%s&userid=%s", accessToken, userResp.UserId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infoResp := &InfoflowUserInfo{}
|
||||
err = json.Unmarshal(data, infoResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if infoResp.Errcode != 0 {
|
||||
return nil, fmt.Errorf("userInfoResp.errcode = %d, userInfoResp.errmsg = %s", infoResp.Errcode, infoResp.Errmsg)
|
||||
}
|
||||
userInfo := UserInfo{
|
||||
Id: infoResp.Imid,
|
||||
Username: infoResp.Name,
|
||||
DisplayName: infoResp.Name,
|
||||
Email: infoResp.Email,
|
||||
}
|
||||
|
||||
if userInfo.Id == "" {
|
||||
userInfo.Id = userInfo.Username
|
||||
}
|
||||
return &userInfo, nil
|
||||
}
|
||||
|
||||
func (idp *InfoflowIdProvider) postWithBody(body interface{}, url string) ([]byte, error) {
|
||||
bs, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := strings.NewReader(string(bs))
|
||||
resp, err := idp.Client.Post(url, "application/json;charset=UTF-8", r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func(Body io.ReadCloser) {
|
||||
err := Body.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
return data, nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -35,39 +35,57 @@ type IdProvider interface {
|
||||
GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
||||
}
|
||||
|
||||
func GetIdProvider(providerType string, clientId string, clientSecret string, redirectUrl string) IdProvider {
|
||||
if providerType == "GitHub" {
|
||||
func GetIdProvider(typ string, subType string, clientId string, clientSecret string, appId string, redirectUrl string, hostUrl string) IdProvider {
|
||||
if typ == "GitHub" {
|
||||
return NewGithubIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "Google" {
|
||||
} else if typ == "Google" {
|
||||
return NewGoogleIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "QQ" {
|
||||
} else if typ == "QQ" {
|
||||
return NewQqIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "WeChat" {
|
||||
} else if typ == "WeChat" {
|
||||
return NewWeChatIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "Facebook" {
|
||||
} else if typ == "Facebook" {
|
||||
return NewFacebookIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "DingTalk" {
|
||||
} else if typ == "DingTalk" {
|
||||
return NewDingTalkIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "Weibo" {
|
||||
} else if typ == "Weibo" {
|
||||
return NewWeiBoIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "Gitee" {
|
||||
} else if typ == "Gitee" {
|
||||
return NewGiteeIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "LinkedIn" {
|
||||
} else if typ == "LinkedIn" {
|
||||
return NewLinkedInIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "WeCom" {
|
||||
return NewWeComIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "Lark" {
|
||||
} else if typ == "WeCom" {
|
||||
if subType == "Internal" {
|
||||
return NewWeComInternalIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if subType == "Third-party" {
|
||||
return NewWeComIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if typ == "Lark" {
|
||||
return NewLarkIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if providerType == "GitLab" {
|
||||
} else if typ == "GitLab" {
|
||||
return NewGitlabIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if isGothSupport(providerType) {
|
||||
return NewGothIdProvider(providerType, clientId, clientSecret, redirectUrl)
|
||||
} else if typ == "Adfs" {
|
||||
return NewAdfsIdProvider(clientId, clientSecret, redirectUrl, hostUrl)
|
||||
} else if typ == "Baidu" {
|
||||
return NewBaiduIdProvider(clientId, clientSecret, redirectUrl)
|
||||
} else if typ == "Infoflow" {
|
||||
if subType == "Internal" {
|
||||
return NewInfoflowInternalIdProvider(clientId, clientSecret, appId, redirectUrl)
|
||||
} else if subType == "Third-party" {
|
||||
return NewInfoflowIdProvider(clientId, clientSecret, appId, redirectUrl)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else if isGothSupport(typ) {
|
||||
return NewGothIdProvider(typ, clientId, clientSecret, redirectUrl)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var gothList = []string{"Apple", "AzureAd", "Slack"}
|
||||
var gothList = []string{"Apple", "AzureAd", "Slack", "Steam"}
|
||||
|
||||
func isGothSupport(provider string) bool {
|
||||
for _, value := range gothList {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -21,6 +21,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
@ -98,6 +99,11 @@ func (idp *WeChatIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// {"errcode":40163,"errmsg":"code been used, rid: 6206378a-793424c0-2e4091cc"}
|
||||
if strings.Contains(buf.String(), "errcode") {
|
||||
return nil, fmt.Errorf(buf.String())
|
||||
}
|
||||
|
||||
var wechatAccessToken WechatAccessToken
|
||||
if err = json.Unmarshal(buf.Bytes(), &wechatAccessToken); err != nil {
|
||||
return nil, err
|
||||
|
171
idp/wecom_internal.go
Normal file
171
idp/wecom_internal.go
Normal file
@ -0,0 +1,171 @@
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package idp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
//This idp is using wecom internal application api as idp
|
||||
type WeComInternalIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
}
|
||||
|
||||
func NewWeComInternalIdProvider(clientId string, clientSecret string, redirectUrl string) *WeComInternalIdProvider {
|
||||
idp := &WeComInternalIdProvider{}
|
||||
|
||||
config := idp.getConfig(clientId, clientSecret, redirectUrl)
|
||||
idp.Config = config
|
||||
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *WeComInternalIdProvider) SetHttpClient(client *http.Client) {
|
||||
idp.Client = client
|
||||
}
|
||||
|
||||
func (idp *WeComInternalIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
|
||||
var config = &oauth2.Config{
|
||||
ClientID: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirectUrl,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type WecomInterToken struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
}
|
||||
|
||||
// GetToken use code get access_token (*operation of getting code ought to be done in front)
|
||||
// get more detail via: https://developer.work.weixin.qq.com/document/path/91039
|
||||
func (idp *WeComInternalIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
pTokenParams := &struct {
|
||||
CorpId string `json:"corpid"`
|
||||
Corpsecret string `json:"corpsecret"`
|
||||
}{idp.Config.ClientID, idp.Config.ClientSecret}
|
||||
resp, err := idp.Client.Get(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s", pTokenParams.CorpId, pTokenParams.Corpsecret))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pToken := &WecomInterToken{}
|
||||
err = json.Unmarshal(data, pToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pToken.Errcode != 0 {
|
||||
return nil, fmt.Errorf("pToken.Errcode = %d, pToken.Errmsg = %s", pToken.Errcode, pToken.Errmsg)
|
||||
}
|
||||
|
||||
token := &oauth2.Token{
|
||||
AccessToken: pToken.AccessToken,
|
||||
Expiry: time.Unix(time.Now().Unix()+int64(pToken.ExpiresIn), 0),
|
||||
}
|
||||
|
||||
raw := make(map[string]interface{})
|
||||
raw["code"] = code
|
||||
token = token.WithExtra(raw)
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
type WecomInternalUserResp struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
UserId string `json:"UserId"`
|
||||
OpenId string `json:"OpenId"`
|
||||
}
|
||||
|
||||
type WecomInternalUserInfo struct {
|
||||
Errcode int `json:"errcode"`
|
||||
Errmsg string `json:"errmsg"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Avatar string `json:"avatar"`
|
||||
OpenId string `json:"open_userid"`
|
||||
}
|
||||
|
||||
func (idp *WeComInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
//Get userid first
|
||||
accessToken := token.AccessToken
|
||||
code := token.Extra("code").(string)
|
||||
resp, err := idp.Client.Get(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=%s&code=%s", accessToken, code))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userResp := &WecomInternalUserResp{}
|
||||
err = json.Unmarshal(data, userResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userResp.Errcode != 0 {
|
||||
return nil, fmt.Errorf("userIdResp.Errcode = %d, userIdResp.Errmsg = %s", userResp.Errcode, userResp.Errmsg)
|
||||
}
|
||||
if userResp.OpenId != "" {
|
||||
return nil, fmt.Errorf("not an internal user")
|
||||
}
|
||||
//Use userid and accesstoken to get user information
|
||||
resp, err = idp.Client.Get(fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=%s&userid=%s", accessToken, userResp.UserId))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infoResp := &WecomInternalUserInfo{}
|
||||
err = json.Unmarshal(data, infoResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if infoResp.Errcode != 0 {
|
||||
return nil, fmt.Errorf("userInfoResp.errcode = %d, userInfoResp.errmsg = %s", infoResp.Errcode, infoResp.Errmsg)
|
||||
}
|
||||
userInfo := UserInfo{
|
||||
Id: infoResp.OpenId,
|
||||
Username: infoResp.Name,
|
||||
DisplayName: infoResp.Name,
|
||||
Email: infoResp.Email,
|
||||
AvatarUrl: infoResp.Avatar,
|
||||
}
|
||||
|
||||
if userInfo.Id == "" {
|
||||
userInfo.Id = userInfo.Username
|
||||
}
|
||||
|
||||
return &userInfo, nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
56
k8s.yaml
Normal file
56
k8s.yaml
Normal file
@ -0,0 +1,56 @@
|
||||
# this is only an EXAMPLE of deploying casddor in kubernetes
|
||||
# please modify this file according to your requirements
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
#EDIT IT: if you don't want to run casdoor in default namespace, please modify this field
|
||||
#namespace: casdoor
|
||||
name: casdoor-svc
|
||||
labels:
|
||||
app: casdoor
|
||||
spec:
|
||||
#EDIT IT: if you don't want to run casdoor in default namespace, please modify this filed
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 8000
|
||||
selector:
|
||||
app: casdoor
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
#EDIT IT: if you don't want to run casdoor in default namespace, please modify this field
|
||||
#namespace: casdoor
|
||||
name: casdoor-deployment
|
||||
labels:
|
||||
app: casdoor
|
||||
spec:
|
||||
#EDIT IT: if you don't use redis, casdoor should not have multiple replicas
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: casdoor
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: casdoor
|
||||
spec:
|
||||
containers:
|
||||
- name: casdoor-container
|
||||
image: casbin/casdoor:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
volumeMounts:
|
||||
# the mounted directory path in THE CONTAINER
|
||||
- mountPath: /conf
|
||||
name: conf
|
||||
env:
|
||||
- name: RUNNING_IN_DOCKER
|
||||
value: "true"
|
||||
#if you want to deploy this in real prod env, consider the config map
|
||||
volumes:
|
||||
- name: conf
|
||||
hostPath:
|
||||
#EDIT IT: the mounted directory path in THE HOST
|
||||
path: /conf
|
33
main.go
33
main.go
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -15,20 +15,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/astaxie/beego/plugins/cors"
|
||||
_ "github.com/astaxie/beego/session/redis"
|
||||
"github.com/casbin/casdoor/authz"
|
||||
"github.com/casbin/casdoor/object"
|
||||
"github.com/casbin/casdoor/proxy"
|
||||
"github.com/casbin/casdoor/routers"
|
||||
|
||||
_ "github.com/casbin/casdoor/routers"
|
||||
"github.com/casdoor/casdoor/authz"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/casdoor/casdoor/routers"
|
||||
_ "github.com/casdoor/casdoor/routers"
|
||||
)
|
||||
|
||||
func main() {
|
||||
object.InitAdapter()
|
||||
createDatabase := flag.Bool("createDatabase", false, "true if you need casdoor to create database")
|
||||
flag.Parse()
|
||||
object.InitAdapter(*createDatabase)
|
||||
object.InitDb()
|
||||
object.InitDefaultStorageProvider()
|
||||
object.InitLdapAutoSynchronizer()
|
||||
@ -37,14 +40,6 @@ func main() {
|
||||
|
||||
go object.RunSyncUsersJob()
|
||||
|
||||
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
|
||||
AllowOrigins: []string{"*"},
|
||||
AllowMethods: []string{"GET", "PUT", "PATCH"},
|
||||
AllowHeaders: []string{"Origin"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
}))
|
||||
|
||||
//beego.DelStaticPath("/static")
|
||||
beego.SetStaticPath("/static", "web/build/static")
|
||||
beego.BConfig.WebConfig.DirectoryIndex = true
|
||||
@ -71,8 +66,8 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
port := beego.AppConfig.DefaultInt("httpport", 8000)
|
||||
//logs.SetLevel(logs.LevelInformational)
|
||||
logs.SetLogFuncCall(false)
|
||||
|
||||
beego.Run()
|
||||
beego.Run(fmt.Sprintf(":%v", port))
|
||||
}
|
||||
|
23
manifests/casdoor/.helmignore
Normal file
23
manifests/casdoor/.helmignore
Normal file
@ -0,0 +1,23 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
24
manifests/casdoor/Chart.yaml
Normal file
24
manifests/casdoor/Chart.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
apiVersion: v2
|
||||
name: casdoor
|
||||
description: A Helm chart for Kubernetes
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "1.16.0"
|
22
manifests/casdoor/templates/NOTES.txt
Normal file
22
manifests/casdoor/templates/NOTES.txt
Normal file
@ -0,0 +1,22 @@
|
||||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range $host := .Values.ingress.hosts }}
|
||||
{{- range .paths }}
|
||||
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "casdoor.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "casdoor.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "casdoor.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "casdoor.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||
{{- end }}
|
62
manifests/casdoor/templates/_helpers.tpl
Normal file
62
manifests/casdoor/templates/_helpers.tpl
Normal file
@ -0,0 +1,62 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "casdoor.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "casdoor.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "casdoor.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "casdoor.labels" -}}
|
||||
helm.sh/chart: {{ include "casdoor.chart" . }}
|
||||
{{ include "casdoor.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "casdoor.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "casdoor.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "casdoor.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "casdoor.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
23
manifests/casdoor/templates/configmap.yaml
Normal file
23
manifests/casdoor/templates/configmap.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: casdoor-config
|
||||
data:
|
||||
app.conf: |
|
||||
appname = casdoor
|
||||
httpport = 80
|
||||
runmode = dev
|
||||
SessionOn = true
|
||||
copyrequestbody = true
|
||||
driverName = mysql
|
||||
dataSourceName = root:123456@tcp(localhost:3306)/
|
||||
dbName = casdoor
|
||||
redisEndpoint =
|
||||
defaultStorageProvider =
|
||||
isCloudIntranet = false
|
||||
authState = "casdoor"
|
||||
sock5Proxy = "127.0.0.1:10808"
|
||||
verificationCodeTimeout = 10
|
||||
initScore = 2000
|
||||
logPostOnly = true
|
||||
origin = "https://door.casbin.com"
|
75
manifests/casdoor/templates/deployment.yaml
Normal file
75
manifests/casdoor/templates/deployment.yaml
Normal file
@ -0,0 +1,75 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "casdoor.fullname" . }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "casdoor.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "casdoor.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "casdoor.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}/{{ .Values.image.name }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
# command: ["sleep", "100000000"]
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
# livenessProbe:
|
||||
# httpGet:
|
||||
# path: /
|
||||
# port: http
|
||||
# readinessProbe:
|
||||
# httpGet:
|
||||
# path: /
|
||||
# port: http
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: config-volume
|
||||
mountPath: /conf
|
||||
volumes:
|
||||
- name: config-volume
|
||||
projected:
|
||||
defaultMode: 420
|
||||
sources:
|
||||
- configMap:
|
||||
items:
|
||||
- key: app.conf
|
||||
path: app.conf
|
||||
name: casdoor-config
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
28
manifests/casdoor/templates/hpa.yaml
Normal file
28
manifests/casdoor/templates/hpa.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2beta1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "casdoor.fullname" . }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "casdoor.fullname" . }}
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
61
manifests/casdoor/templates/ingress.yaml
Normal file
61
manifests/casdoor/templates/ingress.yaml
Normal file
@ -0,0 +1,61 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- $fullName := include "casdoor.fullname" . -}}
|
||||
{{- $svcPort := .Values.service.port -}}
|
||||
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
|
||||
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
|
||||
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
{{- else -}}
|
||||
apiVersion: extensions/v1beta1
|
||||
{{- end }}
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
|
||||
pathType: {{ .pathType }}
|
||||
{{- end }}
|
||||
backend:
|
||||
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
|
||||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
number: {{ $svcPort }}
|
||||
{{- else }}
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: {{ $svcPort }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
15
manifests/casdoor/templates/service.yaml
Normal file
15
manifests/casdoor/templates/service.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "casdoor.fullname" . }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "casdoor.selectorLabels" . | nindent 4 }}
|
12
manifests/casdoor/templates/serviceaccount.yaml
Normal file
12
manifests/casdoor/templates/serviceaccount.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "casdoor.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
15
manifests/casdoor/templates/tests/test-connection.yaml
Normal file
15
manifests/casdoor/templates/tests/test-connection.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "{{ include "casdoor.fullname" . }}-test-connection"
|
||||
labels:
|
||||
{{- include "casdoor.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: wget
|
||||
image: busybox
|
||||
command: ['wget']
|
||||
args: ['{{ include "casdoor.fullname" . }}:{{ .Values.service.port }}']
|
||||
restartPolicy: Never
|
83
manifests/casdoor/values.yaml
Normal file
83
manifests/casdoor/values.yaml
Normal file
@ -0,0 +1,83 @@
|
||||
# Default values for casdoor.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: casbin
|
||||
name: casdoor-all-in-one
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
className: ""
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
hosts:
|
||||
- host: chart-example.local
|
||||
paths:
|
||||
- path: /
|
||||
pathType: ImplementationSpecific
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - chart-example.local
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 100
|
||||
targetCPUUtilizationPercentage: 80
|
||||
# targetMemoryUtilizationPercentage: 80
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -19,11 +19,12 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casbin/casdoor/conf"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
//_ "github.com/denisenkom/go-mssqldb" // db = mssql
|
||||
_ "github.com/go-sql-driver/mysql" // db = mysql
|
||||
//_ "github.com/lib/pq" // db = postgres
|
||||
"xorm.io/core"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
@ -35,11 +36,15 @@ func InitConfig() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
InitAdapter()
|
||||
InitAdapter(true)
|
||||
}
|
||||
|
||||
func InitAdapter() {
|
||||
func InitAdapter(createDatabase bool) {
|
||||
|
||||
adapter = NewAdapter(beego.AppConfig.String("driverName"), conf.GetBeegoConfDataSourceName(), beego.AppConfig.String("dbName"))
|
||||
if createDatabase {
|
||||
adapter.CreateDatabase()
|
||||
}
|
||||
adapter.createTable()
|
||||
}
|
||||
|
||||
@ -75,6 +80,17 @@ func NewAdapter(driverName string, dataSourceName string, dbName string) *Adapte
|
||||
return a
|
||||
}
|
||||
|
||||
func (a *Adapter) CreateDatabase() error {
|
||||
engine, err := xorm.NewEngine(a.driverName, a.dataSourceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer engine.Close()
|
||||
|
||||
_, err = engine.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s default charset utf8mb4 COLLATE utf8mb4_general_ci", a.dbName))
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *Adapter) open() {
|
||||
dataSourceName := a.dataSourceName + a.dbName
|
||||
if a.driverName != "mysql" {
|
||||
@ -95,6 +111,13 @@ func (a *Adapter) close() {
|
||||
}
|
||||
|
||||
func (a *Adapter) createTable() {
|
||||
showSql, _ := beego.AppConfig.Bool("showSql")
|
||||
a.Engine.ShowSQL(showSql)
|
||||
|
||||
tableNamePrefix := beego.AppConfig.String("tableNamePrefix")
|
||||
tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, tableNamePrefix)
|
||||
a.Engine.SetTableMapper(tbMapper)
|
||||
|
||||
err := a.Engine.Sync2(new(Organization))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -105,6 +128,16 @@ func (a *Adapter) createTable() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Role))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Permission))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Provider))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -145,6 +178,21 @@ func (a *Adapter) createTable() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Cert))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Product))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Payment))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Ldap))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -152,12 +200,17 @@ func (a *Adapter) createTable() {
|
||||
}
|
||||
|
||||
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
|
||||
session := adapter.Engine.Limit(limit, offset).Where("1=1")
|
||||
session := adapter.Engine.Prepare()
|
||||
if offset != -1 && limit != -1 {
|
||||
session.Limit(limit, offset)
|
||||
}
|
||||
if owner != "" {
|
||||
session = session.And("owner=?", owner)
|
||||
}
|
||||
if field != "" && value != "" {
|
||||
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
|
||||
if filterField(field) {
|
||||
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
|
||||
}
|
||||
}
|
||||
if sortField == "" || sortOrder == "" {
|
||||
sortField = "created_time"
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -16,8 +16,9 @@ package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
@ -31,12 +32,14 @@ type Application struct {
|
||||
HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
|
||||
Description string `xorm:"varchar(100)" json:"description"`
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
Cert string `xorm:"varchar(100)" json:"cert"`
|
||||
EnablePassword bool `json:"enablePassword"`
|
||||
EnableSignUp bool `json:"enableSignUp"`
|
||||
EnableSigninSession bool `json:"enableSigninSession"`
|
||||
EnableCodeSignin bool `json:"enableCodeSignin"`
|
||||
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
|
||||
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
|
||||
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
|
||||
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
|
||||
|
||||
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
||||
@ -55,10 +58,7 @@ type Application struct {
|
||||
}
|
||||
|
||||
func GetApplicationCount(owner, field, value string) int {
|
||||
session := adapter.Engine.Where("owner=?", owner)
|
||||
if field != "" && value != "" {
|
||||
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
|
||||
}
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Application{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -88,7 +88,7 @@ func GetPaginationApplications(owner string, offset, limit int, field, value, so
|
||||
return applications
|
||||
}
|
||||
|
||||
func getApplicationsByOrganizationName(owner string, organization string) []*Application {
|
||||
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 {
|
||||
@ -200,24 +200,49 @@ func GetApplicationByClientId(clientId string) *Application {
|
||||
}
|
||||
}
|
||||
|
||||
func GetApplicationByClientIdAndSecret(clientId, clientSecret string) *Application {
|
||||
if util.IsStrsEmpty(clientId, clientSecret) {
|
||||
return nil
|
||||
}
|
||||
|
||||
app := GetApplicationByClientId(clientId)
|
||||
if app == nil || app.ClientSecret != clientSecret {
|
||||
return nil
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func GetApplication(id string) *Application {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getApplication(owner, name)
|
||||
}
|
||||
|
||||
func GetMaskedApplication(application *Application, userId string) *Application {
|
||||
if isUserIdGlobalAdmin(userId) {
|
||||
return application
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if application.ClientSecret != "" {
|
||||
application.ClientSecret = "***"
|
||||
}
|
||||
|
||||
if application.OrganizationObj != nil {
|
||||
if application.OrganizationObj.MasterPassword != "" {
|
||||
application.OrganizationObj.MasterPassword = "***"
|
||||
}
|
||||
if application.OrganizationObj.PasswordType != "" {
|
||||
application.OrganizationObj.PasswordType = "***"
|
||||
}
|
||||
if application.OrganizationObj.PasswordSalt != "" {
|
||||
application.OrganizationObj.PasswordSalt = "***"
|
||||
}
|
||||
}
|
||||
return application
|
||||
}
|
||||
|
||||
func GetMaskedApplications(applications []*Application, userId string) []*Application {
|
||||
if isUserIdGlobalAdmin(userId) {
|
||||
return applications
|
||||
}
|
||||
|
||||
for _, application := range applications {
|
||||
application = GetMaskedApplication(application, userId)
|
||||
}
|
||||
return applications
|
||||
}
|
||||
|
||||
func UpdateApplication(id string, application *Application) bool {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if getApplication(owner, name) == nil {
|
||||
@ -271,3 +296,15 @@ func DeleteApplication(application *Application) bool {
|
||||
func (application *Application) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", application.Owner, application.Name)
|
||||
}
|
||||
|
||||
func CheckRedirectUriValid(application *Application, redirectUri string) bool {
|
||||
var validUri = false
|
||||
for _, tmpUri := range application.RedirectUris {
|
||||
fmt.Println(tmpUri, redirectUri)
|
||||
if strings.Contains(redirectUri, tmpUri) {
|
||||
validUri = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return validUri
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -20,7 +20,7 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casbin/casdoor/proxy"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
)
|
||||
|
||||
var defaultStorageProvider *Provider = nil
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -18,7 +18,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/casbin/casdoor/proxy"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
)
|
||||
|
||||
func TestSyncPermanentAvatars(t *testing.T) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
161
object/cert.go
Normal file
161
object/cert.go
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
type Cert struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Scope string `xorm:"varchar(100)" json:"scope"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
CryptoAlgorithm string `xorm:"varchar(100)" json:"cryptoAlgorithm"`
|
||||
BitSize int `json:"bitSize"`
|
||||
ExpireInYears int `json:"expireInYears"`
|
||||
|
||||
PublicKey string `xorm:"mediumtext" json:"publicKey"`
|
||||
PrivateKey string `xorm:"mediumtext" json:"privateKey"`
|
||||
}
|
||||
|
||||
func GetMaskedCert(cert *Cert) *Cert {
|
||||
if cert == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return cert
|
||||
}
|
||||
|
||||
func GetMaskedCerts(certs []*Cert) []*Cert {
|
||||
for _, cert := range certs {
|
||||
cert = GetMaskedCert(cert)
|
||||
}
|
||||
return certs
|
||||
}
|
||||
|
||||
func GetCertCount(owner, field, value string) int {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Cert{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return int(count)
|
||||
}
|
||||
|
||||
func GetCerts(owner string) []*Cert {
|
||||
certs := []*Cert{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&certs, &Cert{Owner: owner})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return certs
|
||||
}
|
||||
|
||||
func GetPaginationCerts(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Cert {
|
||||
certs := []*Cert{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&certs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return certs
|
||||
}
|
||||
|
||||
func getCert(owner string, name string) *Cert {
|
||||
if owner == "" || name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
cert := Cert{Owner: owner, Name: name}
|
||||
existed, err := adapter.Engine.Get(&cert)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &cert
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetCert(id string) *Cert {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getCert(owner, name)
|
||||
}
|
||||
|
||||
func UpdateCert(id string, cert *Cert) bool {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if getCert(owner, name) == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func AddCert(cert *Cert) bool {
|
||||
if cert.PublicKey == "" || cert.PrivateKey == "" {
|
||||
publicKey, privateKey := generateRsaKeys(cert.BitSize, cert.ExpireInYears, cert.Name, cert.Owner)
|
||||
cert.PublicKey = publicKey
|
||||
cert.PrivateKey = privateKey
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.Insert(cert)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func DeleteCert(cert *Cert) bool {
|
||||
affected, err := adapter.Engine.ID(core.PK{cert.Owner, cert.Name}).Delete(&Cert{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func (p *Cert) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
||||
}
|
||||
|
||||
func getCertByApplication(application *Application) *Cert {
|
||||
if application.Cert != "" {
|
||||
return getCert("admin", application.Cert)
|
||||
} else {
|
||||
return GetDefaultCert()
|
||||
}
|
||||
}
|
||||
|
||||
func GetDefaultCert() *Cert {
|
||||
return getCert("admin", "cert-built-in")
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -18,18 +18,22 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/casbin/casdoor/cred"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/cred"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
)
|
||||
|
||||
var reWhiteSpace *regexp.Regexp
|
||||
var (
|
||||
reWhiteSpace *regexp.Regexp
|
||||
reFieldWhiteList *regexp.Regexp
|
||||
)
|
||||
|
||||
func init() {
|
||||
reWhiteSpace, _ = regexp.Compile(`\s`)
|
||||
reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
|
||||
}
|
||||
|
||||
func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, email string, phone string, affiliation string) string {
|
||||
func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, firstName string, lastName string, email string, phone string, affiliation string) string {
|
||||
if organization == nil {
|
||||
return "organization does not exist"
|
||||
}
|
||||
@ -81,11 +85,19 @@ func CheckUserSignup(application *Application, organization *Organization, usern
|
||||
}
|
||||
|
||||
if application.IsSignupItemVisible("Display name") {
|
||||
if displayName == "" {
|
||||
return "displayName cannot be blank"
|
||||
} else if application.GetSignupItemRule("Display name") == "Personal" {
|
||||
if !isValidPersonalName(displayName) {
|
||||
return "displayName is not valid personal name"
|
||||
if application.GetSignupItemRule("Display name") == "First, last" && (firstName != "" || lastName != "") {
|
||||
if firstName == "" {
|
||||
return "firstName cannot be blank"
|
||||
} else if lastName == "" {
|
||||
return "lastName cannot be blank"
|
||||
}
|
||||
} else {
|
||||
if displayName == "" {
|
||||
return "displayName cannot be blank"
|
||||
} else if application.GetSignupItemRule("Display name") == "Real name" {
|
||||
if !isValidRealName(displayName) {
|
||||
return "displayName is not valid real name"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -179,3 +191,7 @@ func CheckUserPassword(organization string, username string, password string) (*
|
||||
|
||||
return user, ""
|
||||
}
|
||||
|
||||
func filterField(field string) bool {
|
||||
return reFieldWhiteList.MatchString(field)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -16,16 +16,16 @@ package object
|
||||
|
||||
import "regexp"
|
||||
|
||||
var rePersonalName *regexp.Regexp
|
||||
var reRealName *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
rePersonalName, err = regexp.Compile("^[\u4E00-\u9FA5]{2,3}(?:·[\u4E00-\u9FA5]{2,3})*$")
|
||||
reRealName, err = regexp.Compile("^[\u4E00-\u9FA5]{2,3}(?:·[\u4E00-\u9FA5]{2,3})*$")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func isValidPersonalName(s string) bool {
|
||||
return rePersonalName.MatchString(s)
|
||||
func isValidRealName(s string) bool {
|
||||
return reRealName.MatchString(s)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -14,12 +14,23 @@
|
||||
|
||||
package object
|
||||
|
||||
import "github.com/casbin/casdoor/util"
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
//go:embed token_jwt_key.pem
|
||||
var tokenJwtPublicKey string
|
||||
|
||||
//go:embed token_jwt_key.key
|
||||
var tokenJwtPrivateKey string
|
||||
|
||||
func InitDb() {
|
||||
initBuiltInOrganization()
|
||||
initBuiltInUser()
|
||||
initBuiltInApplication()
|
||||
initBuiltInCert()
|
||||
initBuiltInLdap()
|
||||
}
|
||||
|
||||
@ -90,6 +101,7 @@ func initBuiltInApplication() {
|
||||
Logo: "https://cdn.casbin.com/logo/logo_1024x256.png",
|
||||
HomepageUrl: "https://casdoor.org",
|
||||
Organization: "built-in",
|
||||
Cert: "cert-built-in",
|
||||
EnablePassword: true,
|
||||
EnableSignUp: true,
|
||||
Providers: []*ProviderItem{},
|
||||
@ -109,6 +121,28 @@ func initBuiltInApplication() {
|
||||
AddApplication(application)
|
||||
}
|
||||
|
||||
func initBuiltInCert() {
|
||||
cert := getCert("admin", "cert-built-in")
|
||||
if cert != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cert = &Cert{
|
||||
Owner: "admin",
|
||||
Name: "cert-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Built-in Cert",
|
||||
Scope: "JWT",
|
||||
Type: "x509",
|
||||
CryptoAlgorithm: "RSA",
|
||||
BitSize: 4096,
|
||||
ExpireInYears: 20,
|
||||
PublicKey: tokenJwtPublicKey,
|
||||
PrivateKey: tokenJwtPrivateKey,
|
||||
}
|
||||
AddCert(cert)
|
||||
}
|
||||
|
||||
func initBuiltInLdap() {
|
||||
ldap := GetLdap("ldap-built-in")
|
||||
if ldap != nil {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -19,7 +19,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/thanhpk/randstr"
|
||||
)
|
||||
@ -162,7 +162,7 @@ func (l *ldapConn) GetLdapUsers(baseDn string) ([]ldapUser, error) {
|
||||
searchReq := goldap.NewSearchRequest(baseDn,
|
||||
goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false,
|
||||
SearchFilter, SearchAttributes, nil)
|
||||
searchResult, err := l.Conn.Search(searchReq)
|
||||
searchResult, err := l.Conn.SearchWithPaging(searchReq, 100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -65,6 +65,13 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
|
||||
ticker := time.NewTicker(time.Duration(ldap.AutoSync) * time.Minute)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-stopChan:
|
||||
logs.Info(fmt.Sprintf("autoSync goroutine for %s stopped", ldap.Id))
|
||||
return
|
||||
case <-ticker.C:
|
||||
}
|
||||
|
||||
UpdateLdapSyncTime(ldap.Id)
|
||||
//fetch all users
|
||||
conn, err := GetLdapConn(ldap.Host, ldap.Port, ldap.Admin, ldap.Passwd)
|
||||
@ -84,12 +91,6 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
|
||||
} 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:
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -18,6 +18,7 @@ import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
@ -40,22 +41,39 @@ type OidcDiscovery struct {
|
||||
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"`
|
||||
}
|
||||
|
||||
var oidcDiscovery OidcDiscovery
|
||||
func getOriginFromHost(host string) (string, string) {
|
||||
protocol := "https://"
|
||||
if strings.HasPrefix(host, "localhost") {
|
||||
protocol = "http://"
|
||||
}
|
||||
|
||||
if host == "localhost:8000" {
|
||||
return fmt.Sprintf("%s%s", protocol, "localhost:7001"), fmt.Sprintf("%s%s", protocol, "localhost:8000")
|
||||
} else {
|
||||
return fmt.Sprintf("%s%s", protocol, host), fmt.Sprintf("%s%s", protocol, host)
|
||||
}
|
||||
}
|
||||
|
||||
func GetOidcDiscovery(host string) OidcDiscovery {
|
||||
originFrontend, originBackend := getOriginFromHost(host)
|
||||
|
||||
func init() {
|
||||
origin := beego.AppConfig.String("origin")
|
||||
if origin != "" {
|
||||
originFrontend = origin
|
||||
originBackend = 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),
|
||||
oidcDiscovery := OidcDiscovery{
|
||||
Issuer: originBackend,
|
||||
AuthorizationEndpoint: fmt.Sprintf("%s/login/oauth/authorize", originFrontend),
|
||||
TokenEndpoint: fmt.Sprintf("%s/api/login/oauth/access_token", originBackend),
|
||||
UserinfoEndpoint: fmt.Sprintf("%s/api/userinfo", originBackend),
|
||||
JwksUri: fmt.Sprintf("%s/.well-known/jwks", originBackend),
|
||||
ResponseTypesSupported: []string{"id_token"},
|
||||
ResponseModesSupported: []string{"login", "code", "link"},
|
||||
GrantTypesSupported: []string{"password", "authorization_code"},
|
||||
@ -66,25 +84,27 @@ func init() {
|
||||
RequestParameterSupported: true,
|
||||
RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"},
|
||||
}
|
||||
}
|
||||
|
||||
func GetOidcDiscovery() OidcDiscovery {
|
||||
return oidcDiscovery
|
||||
}
|
||||
|
||||
func GetJSONWebKeySet() (jose.JSONWebKeySet, error) {
|
||||
func GetJsonWebKeySet() (jose.JSONWebKeySet, error) {
|
||||
certs := GetCerts("admin")
|
||||
jwks := jose.JSONWebKeySet{}
|
||||
//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)
|
||||
for _, cert := range certs {
|
||||
certPemBlock := []byte(cert.PublicKey)
|
||||
certDerBlock, _ := pem.Decode(certPemBlock)
|
||||
x509Cert, _ := x509.ParseCertificate(certDerBlock.Bytes)
|
||||
|
||||
var jwk jose.JSONWebKey
|
||||
jwk.Key = x509Cert.PublicKey
|
||||
jwk.Certificates = []*x509.Certificate{x509Cert}
|
||||
var jwk jose.JSONWebKey
|
||||
jwk.Key = x509Cert.PublicKey
|
||||
jwk.Certificates = []*x509.Certificate{x509Cert}
|
||||
jwk.KeyID = cert.Name
|
||||
jwks.Keys = append(jwks.Keys, jwk)
|
||||
}
|
||||
|
||||
var jwks jose.JSONWebKeySet
|
||||
jwks.Keys = []jose.JSONWebKey{jwk}
|
||||
return jwks, nil
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -15,10 +15,8 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/casdoor/cred"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/cred"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
@ -39,10 +37,7 @@ type Organization struct {
|
||||
}
|
||||
|
||||
func GetOrganizationCount(owner, field, value string) int {
|
||||
session := adapter.Engine.Where("owner=?", owner)
|
||||
if field != "" && value != "" {
|
||||
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
|
||||
}
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Organization{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -124,7 +119,7 @@ func UpdateOrganization(id string, organization *Organization) bool {
|
||||
}
|
||||
|
||||
if name != organization.Name {
|
||||
applications := getApplicationsByOrganizationName("admin", name)
|
||||
applications := GetApplicationsByOrganizationName("admin", name)
|
||||
for _, application := range applications {
|
||||
application.Organization = organization.Name
|
||||
UpdateApplication(application.GetId(), application)
|
||||
|
129
object/payment.go
Normal file
129
object/payment.go
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
type Payment struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
|
||||
Provider string `xorm:"varchar(100)" json:"provider"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
User string `xorm:"varchar(100)" json:"user"`
|
||||
Good string `xorm:"varchar(100)" json:"good"`
|
||||
Amount string `xorm:"varchar(100)" json:"amount"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
}
|
||||
|
||||
func GetPaymentCount(owner, field, value string) int {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Payment{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return int(count)
|
||||
}
|
||||
|
||||
func GetPayments(owner string) []*Payment {
|
||||
payments := []*Payment{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&payments, &Payment{Owner: owner})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return payments
|
||||
}
|
||||
|
||||
func GetPaginationPayments(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Payment {
|
||||
payments := []*Payment{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&payments)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return payments
|
||||
}
|
||||
|
||||
func getPayment(owner string, name string) *Payment {
|
||||
if owner == "" || name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
payment := Payment{Owner: owner, Name: name}
|
||||
existed, err := adapter.Engine.Get(&payment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &payment
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetPayment(id string) *Payment {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getPayment(owner, name)
|
||||
}
|
||||
|
||||
func UpdatePayment(id string, payment *Payment) bool {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if getPayment(owner, name) == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(payment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func AddPayment(payment *Payment) bool {
|
||||
affected, err := adapter.Engine.Insert(payment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func DeletePayment(payment *Payment) bool {
|
||||
affected, err := adapter.Engine.ID(core.PK{payment.Owner, payment.Name}).Delete(&Payment{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func (payment *Payment) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", payment.Owner, payment.Name)
|
||||
}
|
129
object/permission.go
Normal file
129
object/permission.go
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
type Permission struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
|
||||
Users []string `xorm:"mediumtext" json:"users"`
|
||||
Roles []string `xorm:"mediumtext" json:"roles"`
|
||||
|
||||
ResourceType string `xorm:"varchar(100)" json:"resourceType"`
|
||||
Resources []string `xorm:"mediumtext" json:"resources"`
|
||||
Actions []string `xorm:"mediumtext" json:"actions"`
|
||||
Effect string `xorm:"varchar(100)" json:"effect"`
|
||||
|
||||
IsEnabled bool `json:"isEnabled"`
|
||||
}
|
||||
|
||||
func GetPermissionCount(owner, field, value string) int {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Permission{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return int(count)
|
||||
}
|
||||
|
||||
func GetPermissions(owner string) []*Permission {
|
||||
permissions := []*Permission{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&permissions, &Permission{Owner: owner})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return permissions
|
||||
}
|
||||
|
||||
func GetPaginationPermissions(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Permission {
|
||||
permissions := []*Permission{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&permissions)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return permissions
|
||||
}
|
||||
|
||||
func getPermission(owner string, name string) *Permission {
|
||||
if owner == "" || name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
permission := Permission{Owner: owner, Name: name}
|
||||
existed, err := adapter.Engine.Get(&permission)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &permission
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetPermission(id string) *Permission {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getPermission(owner, name)
|
||||
}
|
||||
|
||||
func UpdatePermission(id string, permission *Permission) bool {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if getPermission(owner, name) == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(permission)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func AddPermission(permission *Permission) bool {
|
||||
affected, err := adapter.Engine.Insert(permission)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func DeletePermission(permission *Permission) bool {
|
||||
affected, err := adapter.Engine.ID(core.PK{permission.Owner, permission.Name}).Delete(&Permission{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func (permission *Permission) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", permission.Owner, permission.Name)
|
||||
}
|
130
object/product.go
Normal file
130
object/product.go
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
type Product struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
|
||||
Image string `xorm:"varchar(100)" json:"image"`
|
||||
Detail string `xorm:"varchar(100)" json:"detail"`
|
||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
Price int `json:"price"`
|
||||
Quantity int `json:"quantity"`
|
||||
Sold int `json:"sold"`
|
||||
Providers []string `xorm:"varchar(100)" json:"providers"`
|
||||
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
}
|
||||
|
||||
func GetProductCount(owner, field, value string) int {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Product{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return int(count)
|
||||
}
|
||||
|
||||
func GetProducts(owner string) []*Product {
|
||||
products := []*Product{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&products, &Product{Owner: owner})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return products
|
||||
}
|
||||
|
||||
func GetPaginationProducts(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Product {
|
||||
products := []*Product{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&products)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return products
|
||||
}
|
||||
|
||||
func getProduct(owner string, name string) *Product {
|
||||
if owner == "" || name == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
product := Product{Owner: owner, Name: name}
|
||||
existed, err := adapter.Engine.Get(&product)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &product
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetProduct(id string) *Product {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getProduct(owner, name)
|
||||
}
|
||||
|
||||
func UpdateProduct(id string, product *Product) bool {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if getProduct(owner, name) == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(product)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func AddProduct(product *Product) bool {
|
||||
affected, err := adapter.Engine.Insert(product)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func DeleteProduct(product *Product) bool {
|
||||
affected, err := adapter.Engine.ID(core.PK{product.Owner, product.Name}).Delete(&Product{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func (product *Product) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", product.Owner, product.Name)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -17,7 +17,7 @@ package object
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
@ -29,6 +29,7 @@ type Provider struct {
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Category string `xorm:"varchar(100)" json:"category"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
SubType string `xorm:"varchar(100)" json:"subType"`
|
||||
Method string `xorm:"varchar(100)" json:"method"`
|
||||
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
||||
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
||||
@ -81,10 +82,7 @@ func GetMaskedProviders(providers []*Provider) []*Provider {
|
||||
}
|
||||
|
||||
func GetProviderCount(owner, field, value string) int {
|
||||
session := adapter.Engine.Where("owner=?", owner)
|
||||
if field != "" && value != "" {
|
||||
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
|
||||
}
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Provider{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -20,7 +20,7 @@ import (
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
var logPostOnly bool
|
||||
@ -47,6 +47,8 @@ type Record struct {
|
||||
RequestUri string `xorm:"varchar(1000)" json:"requestUri"`
|
||||
Action string `xorm:"varchar(1000)" json:"action"`
|
||||
|
||||
ExtendedUser *User `xorm:"-" json:"extendedUser"`
|
||||
|
||||
IsTriggered bool `json:"isTriggered"`
|
||||
}
|
||||
|
||||
@ -100,10 +102,7 @@ func AddRecord(record *Record) bool {
|
||||
}
|
||||
|
||||
func GetRecordCount(field, value string) int {
|
||||
session := adapter.Engine.Where("1=1")
|
||||
if field != "" && value != "" {
|
||||
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
|
||||
}
|
||||
session := GetSession("", -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Record{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -159,6 +158,11 @@ func SendWebhooks(record *Record) error {
|
||||
}
|
||||
|
||||
if matched {
|
||||
if webhook.IsUserExtended {
|
||||
user := getUser(record.Organization, record.User)
|
||||
record.ExtendedUser = user
|
||||
}
|
||||
|
||||
err := sendWebhook(webhook, record)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -17,7 +17,7 @@ package object
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/casdoor/util"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
@ -40,11 +40,8 @@ type Resource struct {
|
||||
}
|
||||
|
||||
func GetResourceCount(owner, user, field, value string) int {
|
||||
session := adapter.Engine.Where("owner=? and user=?", owner, user)
|
||||
if field != "" && value != "" {
|
||||
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
|
||||
}
|
||||
count, err := session.Count(&Resource{})
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
count, err := session.Count(&Resource{User: user})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user