Compare commits

...

117 Commits

Author SHA1 Message Date
eaf5cb66f3 fix: update authz rule list (#528)
* fix: update authz rule list

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: resolve conflicts.

Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-03-03 00:52:28 +08:00
83a6b757a4 fix: password leakage vulnerability caused by pagination (#527)
* fix: password leakage vulnerability caused by pagination

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: unsafe get-app-login response fields

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-03-02 20:58:16 +08:00
2a0dcd746f feat: add token logout endpoint (#526)
Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-03-02 20:37:31 +08:00
22f5ad06ec fix: Make secret optional when using PKCE (#525)
Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-03-02 13:15:14 +08:00
18aa70dfb2 Fix delete-resource authz failure. 2022-03-01 22:37:23 +08:00
697b3e4998 feat: add implicit flow support (#520)
* feat: add implicit flow support

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: idp support in implicit flow

Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-03-01 19:09:59 +08:00
d48d515c36 fix: Missing extendedUser in signup webhook (#522)
Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-03-01 18:25:48 +08:00
a5d166c35f Support language param. 2022-02-28 21:33:10 +08:00
4915963c52 fix: member No.0 bug (#516)
* fix: member No.0 bug

* Update account.go

* fix: member No.0 bug

* fix: member No.0 bug

* Update account.go
2022-02-28 19:42:11 +08:00
759a1421e5 feat: add the 'karma' prop to table User (#518)
* feature: feat : add the 'karma' prop to table User

* feat: add the 'karma' prop to table User
2022-02-28 16:25:09 +08:00
c14bf9fdab Fix bug in first name, last name checking 2022-02-28 13:17:05 +08:00
e19f07c521 Add product detail page. 2022-02-27 23:50:35 +08:00
39ab71c5db Add product pages. 2022-02-27 20:09:19 +08:00
2c97f8a8b7 feat: add two authentication flow types (#512)
* feat: add two authentication flow types

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: delete implicit method

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: use a more appropriate name

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: apply suggestion

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: remove redundant code

Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-02-27 14:05:07 +08:00
21392dcc14 Support user's first name and last name. 2022-02-27 14:02:52 +08:00
953d3d5bc5 Change personal to real name. 2022-02-27 13:44:44 +08:00
ddee97f544 fix: this.props.location undefined (#513)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-02-26 18:39:24 +08:00
c58a6d8725 Set enableSigninSession to false by default. 2022-02-25 23:58:13 +08:00
a5ff9549c1 Remove useless menu item. 2022-02-25 22:35:24 +08:00
fe57dcbff4 Improve translation. 2022-02-25 21:31:15 +08:00
f8c4ca0f00 feat: add cancel buttons on the edit page (#509)
* feat: add cancel buttons on the edit page

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: remove warning and primary type of cancel buttons

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-02-25 18:16:02 +08:00
e738c42bd8 fix: facebook login exceptions (#508)
* Fix the exception caused by "Username" being empty when logging in with facebook

* fix: facebook login missing "Username" exception
2022-02-23 23:58:17 +08:00
cbc8c58e85 fix: oidc jwks endpoint only return default cert (#506)
Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-02-21 23:17:16 +08:00
07c90e048f Update personal name. 2022-02-21 16:01:39 +08:00
a33076ada4 feat: add AD-FS support (#505)
Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-02-20 15:01:48 +08:00
9cabc4035f fix: docker-compose.yml has duplicated label (#502)
the casdoor service config in docker-compose.yml has duplicated restart label
2022-02-20 14:15:57 +08:00
274096fe9d fix: empty iss return (#503)
Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-02-18 12:36:11 +08:00
661abd6b6e feat: add steam support (#497)
* feat: add steam support

Signed-off-by: 0x2a <stevesough@gmail.com>

* fix: wrong name

Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-02-16 19:57:46 +08:00
4122c94205 feat: add pagination for LdapSyncPage and fix the bug Ldap auto-sync cannot disable (#496)
* feat: add pagination for LdapSyncPage

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: Ldap auto sync cannot disable

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-02-15 23:03:53 +08:00
68ef5f8311 test: add tests in strings manipulation (#477)
* test: add tests in strings manipulation

Add tests 
improving functions like BoolToString, CamelToSnakeCase, GetMinLenStr and SnakeString

* Add copyrig

* test: fix tests description

* test: add tests for function manupulate string
2022-02-15 21:56:59 +08:00
e35b058ab4 feat: add helm manifest for k8s and makefile (#444)
Signed-off-by: henrywangx <henrywangx@gmail.com>

Co-authored-by: xiong wang <xiong.wang@inceptio.ai>
2022-02-15 21:47:13 +08:00
7d1f368bc2 Support docx file upload. 2022-02-15 21:21:07 +08:00
0bd86baf4d Fix crash in incremental ID. 2022-02-14 22:58:26 +08:00
adf036d8c7 fix: fix missing username in forgetpage (#487) 2022-02-14 19:47:18 +08:00
2d19d366d4 fix: miss ca-certificates in docker image (#491)
Signed-off-by: Zixuan Liu <nodeces@gmail.com>
2022-02-14 00:18:26 +08:00
db37f53d6c Update license headers. 2022-02-13 23:39:27 +08:00
eacd5f59db Update logo. 2022-02-13 23:34:29 +08:00
9024010081 Update demo site URL. 2022-02-13 20:47:34 +08:00
3aab6c8687 Support from link. 2022-02-12 09:55:06 +08:00
7391773f0e Fix payment list error. 2022-02-12 00:36:53 +08:00
de8163a19b Update https://registry.npm.taobao.org in Dockerfile. 2022-02-11 22:54:15 +08:00
07abe06332 Use utf8mb4 for DB encoding. 2022-02-11 22:38:56 +08:00
0bc29465e5 Improve error handling in WeChatIdProvider. 2022-02-11 20:42:48 +08:00
c37b0111a7 test: add tests in setting file (#481)
* test: add tests in setting file

* Add Copyright

* fix: format importation convention

Co-authored-by: Rafael Firmino <rafaelfirmino@gmail.com>
2022-02-10 17:21:27 +08:00
47d1448c02 test: add step in gitHub action from running tests first (#476)
* Add step for running tests in github actions

The test xlsx/xlsx_test.go is removed becout the path containing
file not exist.

* Fix error in test

* test: This test was supposed to test getUsers, but it does nothing

This test will be rewrite for using a clean database, today its
necessary create a syncer manualy for this test pass.

* Add syncer tests

* Fix Copyright position

* fix: back with test file
2022-02-10 17:15:15 +08:00
eb15afec34 fix: use new dingtalk api and support qrcode method (#486)
Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-02-10 17:14:18 +08:00
e1c54744dc Add cert to ApplicationEditPage.js 2022-02-08 20:59:20 +08:00
612b5f5c2e Improve message in GetOAuthToken(). 2022-02-08 20:52:20 +08:00
bd38552db5 test: add tests in time util package (#474)
* test: add tests in time util package

* Add copyright
2022-02-07 22:21:19 +08:00
256b433e57 fix: IsTokenExpired function adjustment (#475)
* fix: IsTokenExpired function adjustment

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: tokenExpired err

Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-02-07 22:20:36 +08:00
63161d6135 fix: infoflow's parameter error (#480)
* fix: missing state parameter

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: infoflow's parameter error

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: use userid instead of imid

Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-02-07 21:50:51 +08:00
5640d258bb fix: missing state parameter (#478)
Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-02-07 15:54:37 +08:00
f85f4c0cf8 feat: add infoflow idp support (#472)
* feat: add infoflow internal backend support

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* feat: add infoflow idp support

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: copyright and comment

Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-02-05 21:54:38 +08:00
0720794e75 Fix bug in IsTokenExpired(). 2022-02-05 21:16:30 +08:00
940aa2bc2d Add payment pages. 2022-02-05 20:13:15 +08:00
db44957b1f fix: fix proxy for swagger (#471) 2022-02-04 20:00:40 +08:00
e5e1fdae76 FIx: menu redirect to swagger (#470) 2022-02-04 19:08:32 +08:00
80f01074fa fix: 'restart always' instruction is in wrong position (#468) 2022-02-03 21:27:27 +08:00
d943d5cc61 fix: oauth params null value error (#465)
Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-01-30 17:58:54 +08:00
19ed35f964 Add getOriginFromHost(). 2022-01-29 23:43:25 +08:00
5757021e87 fix: prohibit cross-origin access (#462) 2022-01-29 21:52:04 +08:00
259a4e1307 Fix Docker compose on Apple M1 Chip 2022-01-29 10:42:33 +08:00
034d822dd5 Fix empty UserInfo ID in wecom_internal.go 2022-01-29 10:27:45 +08:00
a8502d1173 Fix GetIdProvider() bug. 2022-01-29 09:52:48 +08:00
3c2f7b7fc8 feat: add protection against attacks (#460)
Signed-off-by: 0x2a <stevesough@gmail.com>
2022-01-29 00:32:57 +08:00
fbc73de3bb Support WeCom Internal sub type. 2022-01-28 23:57:54 +08:00
479daf4fa4 Improve code format. 2022-01-28 17:45:41 +08:00
d129202b95 fix: no database check when using accessToken (#461)
Signed-off-by: 0x2a <stevesough@gmail.com>
2022-01-28 15:07:42 +08:00
c1f553440e feat: add wecom internal support (#452)
Signed-off-by: 0x2a <stevesough@gmail.com>
2022-01-28 12:44:45 +08:00
7dcae2d183 fix: add k8s deployments example (#446) 2022-01-28 09:25:25 +08:00
5ec0c7a890 fix: fix the SQL injection vulnerability in field filter (#442)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-01-26 19:36:36 +08:00
051752340d feat: add userinfo endpoint (#447)
* feat: add userinfo endpoint

Signed-off-by: 0x2a <stevesough@gmail.com>

* feat: add scope support

Signed-off-by: 0x2a <stevesough@gmail.com>

* fix: modify the endpoint of discovery

Signed-off-by: 0x2a <stevesough@gmail.com>
2022-01-26 11:56:01 +08:00
c87c001da3 fix: fix the permission page can not open when initial a new project (#449) 2022-01-25 19:39:04 +08:00
12bc419659 fix: baidu's display name error (#440)
Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-01-23 20:32:44 +08:00
d5f18f2d64 Support SilentSignin. 2022-01-23 13:02:55 +08:00
02c06bc93c feat: add baidu support as idp (#438)
* feat: add baidu support as idp

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: add license

Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-01-22 19:36:44 +08:00
40aa9a4693 fix: remove wait-for-it (#436) 2022-01-22 15:50:48 +08:00
630b84f534 feat: add PKCE support (#434)
* feat: add PKCE support

Signed-off-by: Steve0x2a <stevesough@gmail.com>

* fix: error output when challenge is empty

Signed-off-by: Steve0x2a <stevesough@gmail.com>
2022-01-21 09:29:19 +08:00
339a85e4b0 Support tableNamePrefix in authz table. 2022-01-20 14:20:37 +08:00
c22ab44894 Update import path. 2022-01-20 14:11:46 +08:00
c3fb48f473 fix: Add a configuration that can set the table prefix. There is no prefix by default (#432)
* fix: Add a configuration that can set the table prefix. There is no prefix by default

* fix: Add a configuration that can set the table prefix. There is no prefix by default
2022-01-20 13:50:20 +08:00
a111fd672c fix: Add the configuration of whether to print SQL. The default value is false (#429) 2022-01-19 16:58:45 +08:00
9fd175eefd Add ErrorText to syncer. 2022-01-17 21:17:42 +08:00
d9bcce9485 Start syncer dynamically. 2022-01-17 20:09:29 +08:00
d183b9eca9 Change syncer.SyncInterval to second-level. 2022-01-17 19:27:52 +08:00
f24d9ae251 Don't update password in AddUsers(). 2022-01-17 13:26:30 +08:00
030c1caa50 Fix bug in IsGlobalAdmin(). 2022-01-15 23:23:14 +08:00
cee2c608a2 Disable PasswordModal when needed in user edit page. 2022-01-15 21:34:37 +08:00
82d0e895e0 Update users and roles when org is changed in permission edit page. 2022-01-15 21:11:47 +08:00
dee9bac110 Show signupApplication in user edit page. 2022-01-15 18:29:10 +08:00
e7a6986b62 Add index to User.Id 2022-01-14 17:42:11 +08:00
b91b4aec91 Allow global admin to modify username. 2022-01-13 23:20:10 +08:00
fe48c38bc6 feat: support minio (#418)
Signed-off-by: abingcbc <abingcbc626@gmail.com>
2022-01-13 21:48:00 +08:00
1be777c08f Fix GetUserByField()'s bug for idCard. 2022-01-13 12:48:15 +08:00
8d54bfad8a feat: support create database via cmd line (#417) 2022-01-13 11:35:13 +08:00
728fe11a3c Refactor CountDownInput. 2022-01-07 20:34:27 +08:00
69e0f4e40d Add idCard in GetUserByFields(). 2022-01-04 19:52:29 +08:00
ba32a45693 Add ClaimsShort to fix the JWT user's owner and name empty bug. 2022-01-03 22:54:27 +08:00
a4d83af768 refactor: New Crowdin translations by Github Action (#412)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-01-02 23:55:06 +08:00
5b8f6415d9 Add Gitter badge. 2022-01-02 23:34:24 +08:00
5389cb435c Fix Crowdin sync. 2022-01-02 23:16:08 +08:00
9b6131890c Add permission page. 2022-01-01 16:28:33 +08:00
ffc0a0e0d5 fix: refresh_token endpoint does not work (#410)
Signed-off-by: 0x2a <stevesough@gmail.com>
2022-01-01 15:20:49 +08:00
ff22bf507f Add role page. 2022-01-01 15:11:16 +08:00
2d4103d751 Add isUserExtended to webhook. 2022-01-01 11:16:37 +08:00
4611b59b08 Add webhook edit page's preview. 2022-01-01 10:58:39 +08:00
445d3c9d0e feat: support spring security oauth2 (#408)
Signed-off-by: abingcbc <abingcbc626@gmail.com>
2021-12-31 19:55:34 +08:00
dbebd1846f Fix code sign-in link hiding. 2021-12-31 13:36:10 +08:00
2fcc8f5bfe Support app user in SetPassword(). 2021-12-31 13:32:18 +08:00
4b65320a96 Support user uploading via xlsx. 2021-12-31 13:00:35 +08:00
5e8897e41b Make cert work. 2021-12-31 10:02:06 +08:00
ba1646a0c3 Add cert pages. 2021-12-31 00:36:36 +08:00
c1cd187558 Improve UI. 2021-12-29 20:50:49 +08:00
519fd655cf Add GetMaskedApplication() and GetMaskedApplications(). 2021-12-29 20:04:39 +08:00
377ac05928 Don't clear session in SetPassword(). 2021-12-28 23:07:09 +08:00
4f124ff140 fix: refresh token does not return (#401)
Signed-off-by: 0x2a <stevesough@gmail.com>
2021-12-28 19:44:17 +08:00
d5f802ec7d Support IdCard in signup page. 2021-12-28 17:48:24 +08:00
271 changed files with 13751 additions and 2453 deletions

View File

@ -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

View File

@ -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
View File

@ -13,7 +13,8 @@
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
vendor/
bin/
.idea/
*.iml

42
.golangci.yml Normal file
View 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

View File

@ -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
View 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}

View File

@ -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)

View File

@ -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, *, *
`

View File

@ -6,6 +6,8 @@ copyrequestbody = true
driverName = mysql
dataSourceName = root:123456@tcp(localhost:3306)/
dbName = casdoor
tableNamePrefix =
showSql = false
redisEndpoint =
defaultStorageProvider =
isCloudIntranet = false
@ -14,4 +16,4 @@ httpProxy = "127.0.0.1:10808"
verificationCodeTimeout = 10
initScore = 2000
logPostOnly = true
origin = "https://door.casbin.com"
origin =

View File

@ -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.

View File

@ -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

View File

@ -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()
}

View File

@ -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

View File

@ -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
View 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()
}

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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
View 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
View 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
View 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()
}

View File

@ -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

View File

@ -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

View File

@ -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
View 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()
}

View File

@ -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

View File

@ -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

View File

@ -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,30 @@ 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()
}

View File

@ -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"}

View 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")
}
}

View File

@ -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 ...

View File

@ -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 {

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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

View File

@ -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.

View File

@ -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
View 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
View 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
}

View File

@ -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&timestamp=%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
}

View File

@ -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,

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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
View 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
View 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
}

View File

@ -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.

View File

@ -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.

View File

@ -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 {

View File

@ -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.

View File

@ -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
View 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
}

View File

@ -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.

View File

@ -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
View 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
View File

@ -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))
}

View 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/

View 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"

View 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 }}

View 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 }}

View 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"
httpProxy = "127.0.0.1:10808"
verificationCodeTimeout = 10
initScore = 2000
logPostOnly = true
origin = "https://door.casbin.com"

View 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 }}

View 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 }}

View 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 }}

View 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 }}

View 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 }}

View 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

View 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: {}

View File

@ -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"

View File

@ -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
}

View File

@ -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.

View File

@ -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

View File

@ -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) {

View File

@ -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
View 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")
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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.

View File

@ -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 {

View File

@ -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
}

View File

@ -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:
}
}
}

View File

@ -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
}

View File

@ -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
View 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
View 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
View 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)
}

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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