Compare commits

...

57 Commits

Author SHA1 Message Date
IsAurora6
59b6854ccc feat: Optimize the notifications/initialized request and authentication failure handling in MCP. (#4781) 2026-01-08 17:42:36 +08:00
Yang Luo
0daf67c52c feat: fix UTF-8 encoding error in Active Directory syncer (#4783) 2026-01-08 01:50:47 +08:00
Yang Luo
4b612269ea feat: check whether refresh token is expired after SSO logout (#4771) 2026-01-07 19:42:35 +08:00
0xkrypton
f438d39720 feat: fix Telegram OAuth login error: "failed to verify Telegram auth data: data verification failed." (#4776) 2026-01-07 19:41:43 +08:00
Eng Zer Jun
f8df200dbf feat: update github.com/shirou/gopsutil to v4 (#4773) 2026-01-07 00:51:37 +08:00
IsAurora6
cb1b3b767e feat: improve "/api/mcp" check with demo mode (#4772) 2026-01-06 14:48:24 +08:00
IsAurora6
3bec49f16c feat: enhance MCP Permissions and Response Workflow, fix bugs (#4767) 2026-01-05 22:54:12 +08:00
Yang Luo
e28344f0e7 feat: add DingTalk syncer (#4766) 2026-01-05 21:43:57 +08:00
Yang Luo
93fefed6e8 feat: add Casdoor MCP server at "/api/mcp" for application management (#4752) 2026-01-05 21:38:34 +08:00
Yang Luo
ea9abb2f29 feat: fix bugs in ticket pages 2026-01-02 23:17:30 +08:00
Yang Luo
337a8c357b feat: fix error in order APIs 2026-01-02 22:04:51 +08:00
IsAurora6
d8cebfbf04 feat: Fixed the logic for updating order and transaction statuses in payment notifications. (#4749) 2026-01-02 19:30:23 +08:00
Yang Luo
91d5039155 feat: add all API endpoints to webhook Events dropdown (#4748) 2026-01-01 22:39:18 +08:00
DacongDA
5996ee8695 feat: add ID verification to init data template and organization UI (#4744) 2026-01-01 15:16:51 +08:00
Yang Luo
8c9331932b feat: initialize default values for fields like signupItems when adding applications via SDK (#4733) 2025-12-29 20:29:02 +08:00
DacongDA
db594e2096 feat: use org name as TOTP issuer (#4731) 2025-12-29 13:49:01 +08:00
Yang Luo
b46b79ee44 feat: improve error handling of hasGravatar() 2025-12-28 22:36:47 +08:00
Yang Luo
b9dbbca716 chore: improve README 2025-12-28 19:37:51 +08:00
Yang Luo
313cf6d480 fix: add missing ID Verification category to OtherProviderInfo (#4727) 2025-12-27 18:48:11 +08:00
DacongDA
0548597d04 feat: update dependencies (aws-sdk-go, go-git, goth and go-jose) to latest (#4729) 2025-12-27 18:17:18 +08:00
DacongDA
eb8e26748f feat: replace notify with notify2 for notification provider (#4728) 2025-12-27 10:47:36 +08:00
Yang Luo
516a23ab1b feat: fix CAPTCHA modal appearing when provider Rule is set to None (#4725) 2025-12-27 09:46:33 +08:00
DacongDA
9887d80e55 feat: upgrade beego to v2 (#4720) 2025-12-26 12:46:13 +08:00
slavb18
13dd4337a6 feat: Add phone number to CustomUserInfo (#4718) 2025-12-25 09:29:58 +08:00
Yang Luo
36c69a6da1 feat: add Telegram to OAuth provider options in web UI (#4719) 2025-12-25 09:29:36 +08:00
Yang Luo
3f4a60096a feat: add 28 missing User fields to syncer UI dropdown (#4713) 2025-12-24 20:56:11 +08:00
Yang Luo
b6240fa356 feat: improve GetFilteredUsers() 2025-12-24 20:31:09 +08:00
Yang Luo
d61f06b053 feat: add WebauthnCredentials and 27 other User fields to syncer (#4705) 2025-12-24 01:52:52 +08:00
IsAurora6
6fe785b6a4 feat: fix null address causing TypeError in management UI (#4706) 2025-12-24 01:31:47 +08:00
DacongDA
cccddea67e feat: fix unauthorized error when using app API to login (#4702) 2025-12-23 20:29:46 +08:00
IsAurora6
83b8c5477a feat: fix Transaction State field type from pp.PaymentState to string (#4699) 2025-12-21 01:31:54 +08:00
IsAurora6
ac0e069f71 feat: add Adyen payment provider (#4667) 2025-12-21 01:25:17 +08:00
DacongDA
4b25e56048 feat: Make session and cookie timeout configurable per application (#4698) 2025-12-21 01:04:38 +08:00
DacongDA
39740e3d6c feat: add support to delete single session and report err while deleting current session (#4694) 2025-12-18 21:15:57 +08:00
IsAurora6
87c5bf3855 fix: fixed balance and dummy payment errors (#4692) 2025-12-14 22:52:13 +08:00
IsAurora6
c4a28acbd8 feat: fix bug in i18n applyToOtherLanguage() (#4691) 2025-12-14 19:24:01 +08:00
IsAurora6
ee26b896f6 fix: show recharge options UI in product store page (#4682) 2025-12-13 15:46:26 +08:00
Yang Luo
4a8cb9535e feat: enforce failed signin limit for LDAP login (#4686) 2025-12-13 00:30:05 +08:00
Yang Luo
387a22d5f8 feat: add ticket list/edit pages (#4651) 2025-12-12 23:16:47 +08:00
Yang Luo
36cadded1c feat: add missing grant types to OIDC discovery endpoint (#4677) 2025-12-12 23:12:13 +08:00
DacongDA
7d130392d9 feat: add session-level single sign-out with authentication and configurable scope (#4678) 2025-12-12 23:08:01 +08:00
IsAurora6
f82c90b901 feat: Optimise the order confirmation page prompts and fix the issue where the transaction.application field was incorrectly populated as organisation. (#4681) 2025-12-12 21:31:22 +08:00
Yang Luo
1a08d6514e fix: improve IsRedirectUriValid() (#4672) 2025-12-11 22:18:56 +08:00
Yang Luo
4d5bf09b36 feat: fix signup application bug in /sso-logout API 2025-12-11 22:10:24 +08:00
Yang Luo
f050deada7 feat: add GoReleaser workflow for multi-platform binary releases (#4665) 2025-12-10 12:10:23 +08:00
Yang Luo
dee94666e0 fix: disable isValidRealName() check in backend 2025-12-10 12:00:23 +08:00
Yang Luo
b84b7d787b fix: fix isSelf() identity check for users without ID field in account items of user edit page (#4669) 2025-12-10 11:40:05 +08:00
Yang Luo
d425183137 feat: update Swagger docs 2025-12-10 01:55:08 +08:00
Yang Luo
ff7fcd277c feat: fix SAML authentication failure when username attribute is unmapped (#4663) 2025-12-10 01:50:03 +08:00
Yang Luo
ed5c0b2713 feat: remove "Please sign out first" check from signup and login APIs (#4659) 2025-12-09 21:16:54 +08:00
Yang Luo
eb60e43192 feat: use bcrypt password type by default for all organizations (#4654) 2025-12-08 22:11:19 +08:00
Yang Luo
d0170532e6 fix: improve Swagger annotations for session and token APIs (#4652) 2025-12-08 22:04:53 +08:00
Yang Luo
7ddb87cdf8 fix: Fix JWT-Custom token format: always include nonce/scope, add signinMethod and provider to dropdown (#4649) 2025-12-08 17:55:31 +08:00
Yang Luo
fac45f5ac7 feat: add Alibaba Cloud ID verification provider (#4645) 2025-12-08 17:48:52 +08:00
Yang Luo
266d361244 feat: fix "only the last session is displayed" bug by respecting application.EnableExclusiveSignin when adding sessions (#4643) 2025-12-08 17:14:11 +08:00
DacongDA
b454ab1931 feat: fix generated link has no org info bug while using shared application (#4647) 2025-12-08 16:35:17 +08:00
Yang Luo
ff39b6f186 feat: add Jumio ID Verification provider (#4641) 2025-12-08 00:39:34 +08:00
168 changed files with 7795 additions and 1696 deletions

View File

@@ -44,6 +44,12 @@ jobs:
cache-dependency-path: ./web/yarn.lock
- run: yarn install && CI=false yarn run build
working-directory: ./web
- name: Upload build artifacts
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
uses: actions/upload-artifact@v4
with:
name: frontend-build-${{ github.run_id }}
path: ./web/build
backend:
name: Back-end
@@ -146,39 +152,95 @@ jobs:
name: cypress-videos
path: ./web/cypress/videos
release-and-push:
name: Release And Push
tag-release:
name: Create Tag
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
needs: [ frontend, backend, linter, e2e ]
outputs:
new-release-published: ${{ steps.semantic.outputs.new_release_published }}
new-release-version: ${{ steps.semantic.outputs.new_release_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Create Tag with Semantic Release
id: semantic
uses: cycjimmy/semantic-release-action@v4
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
github-release:
name: GitHub Release
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && needs.tag-release.outputs.new-release-published == 'true'
needs: [ tag-release ]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Free disk space
uses: jlumbroso/free-disk-space@v1.3.1
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: true
swap-storage: true
- name: Download frontend build artifacts
uses: actions/download-artifact@v4
with:
name: frontend-build-${{ github.run_id }}
path: ./web/build
- name: Prepare Go caches
run: |
echo "GOMODCACHE=$RUNNER_TEMP/gomod" >> $GITHUB_ENV
echo "GOCACHE=$RUNNER_TEMP/gocache" >> $GITHUB_ENV
go clean -cache -modcache -testcache -fuzzcache
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: '~> v2'
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
docker-release:
name: Docker Release
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && needs.tag-release.outputs.new-release-published == 'true'
needs: [ tag-release ]
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: -1
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 20
- name: Fetch Previous version
id: get-previous-tag
uses: actions-ecosystem/action-get-latest-tag@v1.6.0
- name: Release
run: yarn global add semantic-release@17.4.4 && semantic-release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Fetch Current version
id: get-current-tag
uses: actions-ecosystem/action-get-latest-tag@v1.6.0
- name: Decide Should_Push Or Not
id: should_push
run: |
old_version=${{steps.get-previous-tag.outputs.tag}}
new_version=${{steps.get-current-tag.outputs.tag }}
new_version=${{ needs.tag-release.outputs.new-release-version }}
old_array=(${old_version//\./ })
new_array=(${new_version//\./ })
@@ -217,7 +279,7 @@ jobs:
target: STANDARD
platforms: linux/amd64,linux/arm64
push: true
tags: casbin/casdoor:${{steps.get-current-tag.outputs.tag }},casbin/casdoor:latest
tags: casbin/casdoor:${{ needs.tag-release.outputs.new-release-version }},casbin/casdoor:latest
- name: Push All In One Version to Docker Hub
uses: docker/build-push-action@v3
@@ -227,7 +289,7 @@ jobs:
target: ALLINONE
platforms: linux/amd64,linux/arm64
push: true
tags: casbin/casdoor-all-in-one:${{steps.get-current-tag.outputs.tag }},casbin/casdoor-all-in-one:latest
tags: casbin/casdoor-all-in-one:${{ needs.tag-release.outputs.new-release-version }},casbin/casdoor-all-in-one:latest
- uses: actions/checkout@v3
if: steps.should_push.outputs.push=='true'
@@ -240,8 +302,8 @@ jobs:
if: steps.should_push.outputs.push=='true'
run: |
# Set the appVersion and version of the chart to the current tag
sed -i "s/appVersion: .*/appVersion: ${{steps.get-current-tag.outputs.tag }}/g" ./charts/casdoor/Chart.yaml
sed -i "s/version: .*/version: ${{steps.get-current-tag.outputs.tag }}/g" ./charts/casdoor/Chart.yaml
sed -i "s/appVersion: .*/appVersion: ${{ needs.tag-release.outputs.new-release-version }}/g" ./charts/casdoor/Chart.yaml
sed -i "s/version: .*/version: ${{ needs.tag-release.outputs.new-release-version }}/g" ./charts/casdoor/Chart.yaml
REGISTRY=oci://registry-1.docker.io/casbin
cd charts/casdoor
@@ -255,6 +317,6 @@ jobs:
git config --global user.name "casbin-bot"
git config --global user.email "bot@casbin.org"
git add Chart.yaml index.yaml
git commit -m "chore(helm): bump helm charts appVersion to ${{steps.get-current-tag.outputs.tag }}"
git tag ${{steps.get-current-tag.outputs.tag }}
git commit -m "chore(helm): bump helm charts appVersion to ${{ needs.tag-release.outputs.new-release-version }}"
git tag ${{ needs.tag-release.outputs.new-release-version }}
git push origin HEAD:master --follow-tags

54
.goreleaser.yaml Normal file
View File

@@ -0,0 +1,54 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
# The lines below are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/need to use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
version: 2
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
# you may remove this if you don't need go generate
#- go generate ./...
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
archives:
- format: tar.gz
# this name template makes the OS and Arch compatible with the results of `uname`.
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
# use zip for windows archives
format_overrides:
- goos: windows
format: zip
files:
- src: 'web/build'
dst: './web/build'
- src: 'conf/app.conf'
dst: './conf/app.conf'
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"

View File

@@ -42,20 +42,6 @@
</a>
</p>
<p align="center">
<sup>Sponsored by</sup>
<br>
<a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.casbin.org/img/stytch-white.png">
<source media="(prefers-color-scheme: light)" srcset="https://cdn.casbin.org/img/stytch-charcoal.png">
<img src="https://cdn.casbin.org/img/stytch-charcoal.png" width="275">
</picture>
</a><br/>
<a href="https://stytch.com/docs?utm_source=oss-sponsorship&utm_medium=paid_sponsorship&utm_campaign=casbin"><b>Build auth with fraud prevention, faster.</b><br/> Try Stytch for API-first authentication, user & org management, multi-tenant SSO, MFA, device fingerprinting, and more.</a>
<br>
</p>
## Online demo
- Read-only site: https://door.casdoor.com (any modification operation will fail)

View File

@@ -71,6 +71,9 @@ p, *, *, GET, /api/get-payment, *, *
p, *, *, POST, /api/update-payment, *, *
p, *, *, POST, /api/invoice-payment, *, *
p, *, *, POST, /api/notify-payment, *, *
p, *, *, POST, /api/place-order, *, *
p, *, *, POST, /api/cancel-order, *, *
p, *, *, POST, /api/pay-order, *, *
p, *, *, POST, /api/unlink, *, *
p, *, *, POST, /api/set-password, *, *
p, *, *, POST, /api/send-verification-code, *, *
@@ -129,7 +132,15 @@ p, *, *, GET, /api/faceid-signin-begin, *, *
}
}
func IsAllowed(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
func IsAllowed(subOwner string, subName string, method string, urlPath string, objOwner string, objName string, extraInfo map[string]interface{}) bool {
if urlPath == "/api/mcp" {
if detailPath, ok := extraInfo["detailPathUrl"].(string); ok {
if detailPath == "initialize" || detailPath == "notifications/initialized" || detailPath == "ping" || detailPath == "tools/list" {
return true
}
}
}
if conf.IsDemoMode() {
if !isAllowedInDemoMode(subOwner, subName, method, urlPath, objOwner, objName) {
return false

View File

@@ -21,7 +21,7 @@ import (
"strconv"
"strings"
"github.com/beego/beego"
"github.com/beego/beego/v2/server/web"
)
func init() {
@@ -29,7 +29,7 @@ func init() {
presetConfigItems := []string{"httpport", "appname"}
for _, key := range presetConfigItems {
if value, ok := os.LookupEnv(key); ok {
err := beego.AppConfig.Set(key, value)
err := web.AppConfig.Set(key, value)
if err != nil {
panic(err)
}
@@ -42,12 +42,13 @@ func GetConfigString(key string) string {
return value
}
res := beego.AppConfig.String(key)
res, _ := web.AppConfig.String(key)
if res == "" {
if key == "staticBaseUrl" {
res = "https://cdn.casbin.org"
} else if key == "logConfig" {
res = fmt.Sprintf("{\"filename\": \"logs/%s.log\", \"maxdays\":99999, \"perm\":\"0770\"}", beego.AppConfig.String("appname"))
appname, _ := web.AppConfig.String("appname")
res = fmt.Sprintf("{\"filename\": \"logs/%s.log\", \"maxdays\":99999, \"perm\":\"0770\"}", appname)
}
}

View File

@@ -17,7 +17,7 @@ package conf
import (
"encoding/json"
"github.com/beego/beego"
"github.com/beego/beego/v2/server/web"
)
type Quota struct {
@@ -34,7 +34,7 @@ func init() {
}
func initQuota() {
res := beego.AppConfig.String("quota")
res, _ := web.AppConfig.String("quota")
if res != "" {
err := json.Unmarshal([]byte(res), quota)
if err != nil {

View File

@@ -18,7 +18,7 @@ import (
"os"
"testing"
"github.com/beego/beego"
"github.com/beego/beego/v2/server/web"
"github.com/stretchr/testify/assert"
)
@@ -38,7 +38,7 @@ func TestGetConfString(t *testing.T) {
os.Setenv("appname", "casbin")
os.Setenv("key", "value")
err := beego.LoadAppConfig("ini", "app.conf")
err := web.LoadAppConfig("ini", "app.conf")
assert.Nil(t, err)
for _, scenery := range scenarios {
@@ -62,7 +62,7 @@ func TestGetConfInt(t *testing.T) {
// do some set up job
os.Setenv("httpport", "8001")
err := beego.LoadAppConfig("ini", "app.conf")
err := web.LoadAppConfig("ini", "app.conf")
assert.Nil(t, err)
for _, scenery := range scenarios {
@@ -83,7 +83,7 @@ func TestGetConfBool(t *testing.T) {
{"Should be return false", "copyrequestbody", true},
}
err := beego.LoadAppConfig("ini", "app.conf")
err := web.LoadAppConfig("ini", "app.conf")
assert.Nil(t, err)
for _, scenery := range scenarios {
t.Run(scenery.description, func(t *testing.T) {
@@ -102,7 +102,7 @@ func TestGetConfigQuota(t *testing.T) {
{"default", &Quota{-1, -1, -1, -1}},
}
err := beego.LoadAppConfig("ini", "app.conf")
err := web.LoadAppConfig("ini", "app.conf")
assert.Nil(t, err)
for _, scenery := range scenarios {
quota := GetConfigQuota()
@@ -118,7 +118,7 @@ func TestGetConfigLogs(t *testing.T) {
{"Default log config", `{"adapter":"file", "filename": "logs/casdoor.log", "maxdays":99999, "perm":"0770"}`},
}
err := beego.LoadAppConfig("ini", "app.conf")
err := web.LoadAppConfig("ini", "app.conf")
assert.Nil(t, err)
for _, scenery := range scenarios {
quota := GetConfigString("logConfig")

View File

@@ -15,6 +15,7 @@
package controllers
import (
"context"
"encoding/json"
"fmt"
"net/http"
@@ -80,11 +81,6 @@ type LaravelResponse struct {
// @Success 200 {object} controllers.Response The Response object
// @router /signup [post]
func (c *ApiController) Signup() {
if c.GetSessionUsername() != "" {
c.ResponseError(c.T("account:Please sign out first"), c.GetSessionUsername())
return
}
var authForm form.AuthForm
err := json.Unmarshal(c.Ctx.Input.RequestBody, &authForm)
if err != nil {
@@ -350,7 +346,7 @@ func (c *ApiController) Logout() {
c.ResponseError(err.Error())
return
}
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID())
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID(context.Background()))
if err != nil {
c.ResponseError(err.Error())
return
@@ -403,7 +399,7 @@ func (c *ApiController) Logout() {
return
}
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID())
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID(context.Background()))
if err != nil {
c.ResponseError(err.Error())
return
@@ -436,7 +432,8 @@ func (c *ApiController) Logout() {
// SsoLogout
// @Title SsoLogout
// @Tag Login API
// @Description logout the current user from all applications
// @Description logout the current user from all applications or current session only
// @Param logoutAll query string false "Whether to logout from all sessions. Accepted values: 'true', '1', or empty (default: true). Any other value means false."
// @Success 200 {object} controllers.Response The Response object
// @router /sso-logout [get,post]
func (c *ApiController) SsoLogout() {
@@ -447,6 +444,11 @@ func (c *ApiController) SsoLogout() {
return
}
// Check if user wants to logout from all sessions or just current session
// Default is true for backward compatibility
logoutAll := c.Ctx.Input.Query("logoutAll")
logoutAllSessions := logoutAll == "" || logoutAll == "true" || logoutAll == "1"
c.ClearUserSession()
c.ClearTokenSession()
owner, username, err := util.GetOwnerAndNameFromIdWithError(user)
@@ -454,37 +456,62 @@ func (c *ApiController) SsoLogout() {
c.ResponseError(err.Error())
return
}
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID())
if err != nil {
c.ResponseError(err.Error())
return
}
_, err = object.ExpireTokenByUser(owner, username)
if err != nil {
c.ResponseError(err.Error())
return
}
sessions, err := object.GetUserSessions(owner, username)
currentSessionId := c.Ctx.Input.CruSession.SessionID(context.Background())
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), currentSessionId)
if err != nil {
c.ResponseError(err.Error())
return
}
var tokens []*object.Token
var sessionIds []string
for _, session := range sessions {
sessionIds = append(sessionIds, session.SessionId...)
}
object.DeleteBeegoSession(sessionIds)
_, err = object.DeleteAllUserSessions(owner, username)
if err != nil {
c.ResponseError(err.Error())
return
if logoutAllSessions {
// Logout from all sessions: expire all tokens and delete all sessions
// Get tokens before expiring them (for session-level logout notification)
tokens, err = object.GetTokensByUser(owner, username)
if err != nil {
c.ResponseError(err.Error())
return
}
_, err = object.ExpireTokenByUser(owner, username)
if err != nil {
c.ResponseError(err.Error())
return
}
sessions, err := object.GetUserSessions(owner, username)
if err != nil {
c.ResponseError(err.Error())
return
}
for _, session := range sessions {
sessionIds = append(sessionIds, session.SessionId...)
}
object.DeleteBeegoSession(sessionIds)
_, err = object.DeleteAllUserSessions(owner, username)
if err != nil {
c.ResponseError(err.Error())
return
}
util.LogInfo(c.Ctx, "API: [%s] logged out from all applications", user)
} else {
// Logout from current session only
sessionIds = []string{currentSessionId}
// Only delete the current session's Beego session
object.DeleteBeegoSession(sessionIds)
util.LogInfo(c.Ctx, "API: [%s] logged out from current session", user)
}
// Send SSO logout notifications to all notification providers in the user's signup application
// Now includes session-level information for targeted logout
userObj, err := object.GetUser(user)
if err != nil {
c.ResponseError(err.Error())
@@ -492,15 +519,13 @@ func (c *ApiController) SsoLogout() {
}
if userObj != nil {
err = object.SendSsoLogoutNotifications(userObj)
err = object.SendSsoLogoutNotifications(userObj, sessionIds, tokens)
if err != nil {
c.ResponseError(err.Error())
return
}
}
util.LogInfo(c.Ctx, "API: [%s] logged out from all applications", user)
c.ResponseOk()
}
@@ -517,7 +542,7 @@ func (c *ApiController) GetAccount() {
return
}
managedAccounts := c.Input().Get("managedAccounts")
managedAccounts := c.Ctx.Input.Query("managedAccounts")
if managedAccounts == "1" {
user, err = object.ExtendManagedAccountsWithUser(user)
if err != nil {
@@ -635,8 +660,8 @@ func (c *ApiController) GetUserinfo2() {
// @router /get-captcha [get]
// @Success 200 {object} object.Userinfo The Response object
func (c *ApiController) GetCaptcha() {
applicationId := c.Input().Get("applicationId")
isCurrentProvider := c.Input().Get("isCurrentProvider")
applicationId := c.Ctx.Input.Query("applicationId")
isCurrentProvider := c.Ctx.Input.Query("isCurrentProvider")
captchaProvider, err := object.GetCaptchaProviderByApplication(applicationId, isCurrentProvider, c.GetAcceptLanguage())
if err != nil {

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @Success 200 {array} object.Adapter The Response object
// @router /get-adapters [get]
func (c *ApiController) GetAdapters() {
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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
adapters, err := object.GetAdapters(owner)
@@ -54,7 +54,7 @@ func (c *ApiController) GetAdapters() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
adapters, err := object.GetPaginationAdapters(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -73,7 +73,7 @@ func (c *ApiController) GetAdapters() {
// @Success 200 {object} object.Adapter The Response object
// @router /get-adapter [get]
func (c *ApiController) GetAdapter() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
adapter, err := object.GetAdapter(id)
if err != nil {
@@ -93,7 +93,7 @@ func (c *ApiController) GetAdapter() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-adapter [post]
func (c *ApiController) UpdateAdapter() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var adapter object.Adapter
err := json.Unmarshal(c.Ctx.Input.RequestBody, &adapter)

View File

@@ -18,7 +18,7 @@ import (
"encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -32,14 +32,14 @@ import (
// @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")
field := c.Input().Get("field")
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
organization := c.Input().Get("organization")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
organization := c.Ctx.Input.Query("organization")
var err error
if limit == "" || page == "" {
var applications []*object.Application
@@ -61,7 +61,7 @@ func (c *ApiController) GetApplications() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
application, err := object.GetPaginationApplications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -82,7 +82,7 @@ func (c *ApiController) GetApplications() {
// @router /get-application [get]
func (c *ApiController) GetApplication() {
userId := c.GetSessionUsername()
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
application, err := object.GetApplication(id)
if err != nil {
@@ -90,7 +90,7 @@ func (c *ApiController) GetApplication() {
return
}
if c.Input().Get("withKey") != "" && application != nil && application.Cert != "" {
if c.Ctx.Input.Query("withKey") != "" && application != nil && application.Cert != "" {
cert, err := object.GetCert(util.GetId(application.Owner, application.Cert))
if err != nil {
c.ResponseError(err.Error())
@@ -125,7 +125,7 @@ func (c *ApiController) GetApplication() {
// @router /get-user-application [get]
func (c *ApiController) GetUserApplication() {
userId := c.GetSessionUsername()
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
user, err := object.GetUser(id)
if err != nil {
@@ -159,14 +159,14 @@ func (c *ApiController) GetUserApplication() {
// @router /get-organization-applications [get]
func (c *ApiController) GetOrganizationApplications() {
userId := c.GetSessionUsername()
organization := c.Input().Get("organization")
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")
organization := c.Ctx.Input.Query("organization")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if organization == "" {
c.ResponseError(c.T("general:Missing parameter") + ": organization")
@@ -196,7 +196,7 @@ func (c *ApiController) GetOrganizationApplications() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
applications, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -223,7 +223,7 @@ func (c *ApiController) GetOrganizationApplications() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-application [post]
func (c *ApiController) UpdateApplication() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var application object.Application
err := json.Unmarshal(c.Ctx.Input.RequestBody, &application)

View File

@@ -15,6 +15,7 @@
package controllers
import (
"context"
"encoding/base64"
"encoding/json"
"encoding/xml"
@@ -27,7 +28,7 @@ import (
"strings"
"time"
"github.com/beego/beego"
"github.com/beego/beego/v2/server/web"
"github.com/casdoor/casdoor/captcha"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/form"
@@ -151,14 +152,14 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
resp = &Response{Status: "ok", Msg: "", Data: userId, Data3: user.NeedUpdatePassword}
} else if form.Type == ResponseTypeCode {
clientId := c.Input().Get("clientId")
responseType := c.Input().Get("responseType")
redirectUri := c.Input().Get("redirectUri")
scope := c.Input().Get("scope")
state := c.Input().Get("state")
nonce := c.Input().Get("nonce")
challengeMethod := c.Input().Get("code_challenge_method")
codeChallenge := c.Input().Get("code_challenge")
clientId := c.Ctx.Input.Query("clientId")
responseType := c.Ctx.Input.Query("responseType")
redirectUri := c.Ctx.Input.Query("redirectUri")
scope := c.Ctx.Input.Query("scope")
state := c.Ctx.Input.Query("state")
nonce := c.Ctx.Input.Query("nonce")
challengeMethod := c.Ctx.Input.Query("code_challenge_method")
codeChallenge := c.Ctx.Input.Query("code_challenge")
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
c.ResponseError(c.T("auth:Challenge method should be S256"))
@@ -180,8 +181,8 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
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")
nonce := c.Input().Get("nonce")
scope := c.Ctx.Input.Query("scope")
nonce := c.Ctx.Input.Query("nonce")
token, _ := object.GetTokenByUser(application, user, scope, nonce, c.Ctx.Request.Host)
resp = tokenToResponse(token)
@@ -227,7 +228,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
}
} else if form.Type == ResponseTypeCas {
// not oauth but CAS SSO protocol
service := c.Input().Get("service")
service := c.Ctx.Input.Query("service")
resp = wrapErrorResponse(nil)
if service != "" {
st, err := object.GenerateCasToken(userId, service)
@@ -246,9 +247,18 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
resp = wrapErrorResponse(fmt.Errorf("unknown response type: %s", form.Type))
}
// if user did not check auto signin
if resp.Status == "ok" && !form.AutoSignin {
c.setExpireForSession()
// For all successful logins, set the session expiration; if auto signin is not checked, cap it at 24 hours.
if resp.Status == "ok" {
expireInHours := application.CookieExpireInHours
if expireInHours == 0 {
expireInHours = 720
}
if !form.AutoSignin && expireInHours > 24 {
expireInHours = 24
}
c.setExpireForSession(expireInHours)
}
if application.EnableExclusiveSignin {
@@ -260,7 +270,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
for _, session := range sessions {
for _, sid := range session.SessionId {
err := beego.GlobalSessions.GetProvider().SessionDestroy(sid)
err := web.GlobalSessions.GetProvider().SessionDestroy(context.Background(), sid)
if err != nil {
c.ResponseError(err.Error(), nil)
return
@@ -274,9 +284,9 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
Owner: user.Owner,
Name: user.Name,
Application: application.Name,
SessionId: []string{c.Ctx.Input.CruSession.SessionID()},
SessionId: []string{c.Ctx.Input.CruSession.SessionID(context.Background())},
ExclusiveSignin: true,
ExclusiveSignin: application.EnableExclusiveSignin,
})
if err != nil {
c.ResponseError(err.Error(), nil)
@@ -299,14 +309,14 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
// @Success 200 {object} controllers.Response The Response object
// @router /get-app-login [get]
func (c *ApiController) GetApplicationLogin() {
clientId := c.Input().Get("clientId")
responseType := c.Input().Get("responseType")
redirectUri := c.Input().Get("redirectUri")
scope := c.Input().Get("scope")
state := c.Input().Get("state")
id := c.Input().Get("id")
loginType := c.Input().Get("type")
userCode := c.Input().Get("userCode")
clientId := c.Ctx.Input.Query("clientId")
responseType := c.Ctx.Input.Query("responseType")
redirectUri := c.Ctx.Input.Query("redirectUri")
scope := c.Ctx.Input.Query("scope")
state := c.Ctx.Input.Query("state")
id := c.Ctx.Input.Query("id")
loginType := c.Ctx.Input.Query("type")
userCode := c.Ctx.Input.Query("userCode")
var application *object.Application
var msg string
@@ -417,7 +427,7 @@ func checkMfaEnable(c *ApiController, user *object.User, organization *object.Or
}
if len(mfaAllowList) >= 1 {
c.SetSession("verificationCodeType", verificationType)
c.Ctx.Input.CruSession.SessionRelease(c.Ctx.ResponseWriter)
c.Ctx.Input.CruSession.SessionRelease(context.Background(), c.Ctx.ResponseWriter)
c.ResponseOk(object.NextMfa, mfaAllowList)
return true
}
@@ -454,13 +464,6 @@ func (c *ApiController) Login() {
verificationType := ""
if authForm.Username != "" {
if authForm.Type == ResponseTypeLogin {
if c.GetSessionUsername() != "" {
c.ResponseError(c.T("account:Please sign out first"), c.GetSessionUsername())
return
}
}
var user *object.User
if authForm.SigninMethod == "Face ID" {
if user, err = object.GetUserByFields(authForm.Organization, authForm.Username); err != nil {
@@ -1154,8 +1157,8 @@ func (c *ApiController) Login() {
}
func (c *ApiController) GetSamlLogin() {
providerId := c.Input().Get("id")
relayState := c.Input().Get("relayState")
providerId := c.Ctx.Input.Query("id")
relayState := c.Ctx.Input.Query("relayState")
authURL, method, err := object.GenerateSamlRequest(providerId, relayState, c.Ctx.Request.Host, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
@@ -1165,8 +1168,8 @@ func (c *ApiController) GetSamlLogin() {
}
func (c *ApiController) HandleSamlLogin() {
relayState := c.Input().Get("RelayState")
samlResponse := c.Input().Get("SAMLResponse")
relayState := c.Ctx.Input.Query("RelayState")
samlResponse := c.Ctx.Input.Query("SAMLResponse")
decode, err := base64.StdEncoding.DecodeString(relayState)
if err != nil {
c.ResponseError(err.Error())
@@ -1198,9 +1201,9 @@ func (c *ApiController) HandleOfficialAccountEvent() {
c.ResponseError(err.Error())
return
}
signature := c.Input().Get("signature")
timestamp := c.Input().Get("timestamp")
nonce := c.Input().Get("nonce")
signature := c.Ctx.Input.Query("signature")
timestamp := c.Ctx.Input.Query("timestamp")
nonce := c.Ctx.Input.Query("nonce")
var data struct {
MsgType string `xml:"MsgType"`
Event string `xml:"Event"`
@@ -1258,7 +1261,7 @@ func (c *ApiController) HandleOfficialAccountEvent() {
// @Param ticket query string true "The eventId of QRCode"
// @Success 200 {object} controllers.Response The Response object
func (c *ApiController) GetWebhookEventType() {
ticket := c.Input().Get("ticket")
ticket := c.Ctx.Input.Query("ticket")
idp.Lock.RLock()
_, ok := idp.WechatCacheMap[ticket]
@@ -1278,7 +1281,7 @@ func (c *ApiController) GetWebhookEventType() {
// @Param id query string true "The id ( owner/name ) of provider"
// @Success 200 {object} controllers.Response The Response object
func (c *ApiController) GetQRCode() {
providerId := c.Input().Get("id")
providerId := c.Ctx.Input.Query("id")
provider, err := object.GetProvider(providerId)
if err != nil {
c.ResponseError(err.Error())
@@ -1306,9 +1309,9 @@ func (c *ApiController) GetQRCode() {
// @Success 200 {object} controllers.Response The Response object
// @router /get-captcha-status [get]
func (c *ApiController) GetCaptchaStatus() {
organization := c.Input().Get("organization")
userId := c.Input().Get("userId")
applicationName := c.Input().Get("application")
organization := c.Ctx.Input.Query("organization")
userId := c.Ctx.Input.Query("userId")
applicationName := c.Ctx.Input.Query("application")
application, err := object.GetApplication(fmt.Sprintf("admin/%s", applicationName))
if err != nil {
@@ -1351,8 +1354,8 @@ func (c *ApiController) Callback() {
// @router /device-auth [post]
// @Success 200 {object} object.DeviceAuthResponse The Response object
func (c *ApiController) DeviceAuth() {
clientId := c.Input().Get("client_id")
scope := c.Input().Get("scope")
clientId := c.Ctx.Input.Query("client_id")
scope := c.Ctx.Input.Query("scope")
application, err := object.GetApplicationByClientId(clientId)
if err != nil {
c.Data["json"] = object.TokenError{

View File

@@ -15,11 +15,12 @@
package controllers
import (
"context"
"strings"
"time"
"github.com/beego/beego"
"github.com/beego/beego/logs"
"github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -27,7 +28,7 @@ import (
// ApiController
// controller for handlers under /api uri
type ApiController struct {
beego.Controller
web.Controller
}
// RootController
@@ -168,6 +169,7 @@ func (c *ApiController) GetSessionApplication() *object.Application {
func (c *ApiController) ClearUserSession() {
c.SetSessionUsername("")
c.SetSessionData(nil)
_ = c.SessionRegenerateID()
}
func (c *ApiController) ClearTokenSession() {
@@ -236,16 +238,19 @@ func (c *ApiController) setMfaUserSession(userId string) {
}
func (c *ApiController) getMfaUserSession() string {
userId := c.Ctx.Input.CruSession.Get(object.MfaSessionUserId)
userId := c.Ctx.Input.CruSession.Get(context.Background(), object.MfaSessionUserId)
if userId == nil {
return ""
}
return userId.(string)
}
func (c *ApiController) setExpireForSession() {
func (c *ApiController) setExpireForSession(cookieExpireInHours int64) {
timestamp := time.Now().Unix()
timestamp += 3600 * 24
if cookieExpireInHours == 0 {
cookieExpireInHours = 720
}
timestamp += 3600 * cookieExpireInHours
c.SetSessionData(&SessionData{
ExpireTime: timestamp,
})

View File

@@ -41,8 +41,8 @@ func queryUnescape(service string) string {
}
func (c *RootController) CasValidate() {
ticket := c.Input().Get("ticket")
service := c.Input().Get("service")
ticket := c.Ctx.Input.Query("ticket")
service := c.Ctx.Input.Query("service")
c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
if service == "" || ticket == "" {
c.Ctx.Output.Body([]byte("no\n"))
@@ -60,8 +60,8 @@ func (c *RootController) CasValidate() {
}
func (c *RootController) CasServiceValidate() {
ticket := c.Input().Get("ticket")
format := c.Input().Get("format")
ticket := c.Ctx.Input.Query("ticket")
format := c.Ctx.Input.Query("format")
if !strings.HasPrefix(ticket, "ST") {
c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format)
}
@@ -75,8 +75,8 @@ func (c *RootController) CasProxyValidate() {
}
func (c *RootController) CasP3ServiceValidate() {
ticket := c.Input().Get("ticket")
format := c.Input().Get("format")
ticket := c.Ctx.Input.Query("ticket")
format := c.Ctx.Input.Query("format")
if !strings.HasPrefix(ticket, "ST") {
c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format)
}
@@ -84,10 +84,10 @@ func (c *RootController) CasP3ServiceValidate() {
}
func (c *RootController) CasP3ProxyValidate() {
ticket := c.Input().Get("ticket")
format := c.Input().Get("format")
service := c.Input().Get("service")
pgtUrl := c.Input().Get("pgtUrl")
ticket := c.Ctx.Input.Query("ticket")
format := c.Ctx.Input.Query("format")
service := c.Ctx.Input.Query("service")
pgtUrl := c.Ctx.Input.Query("pgtUrl")
serviceResponse := object.CasServiceResponse{
Xmlns: "http://www.yale.edu/tp/cas",
@@ -161,9 +161,9 @@ func (c *RootController) CasP3ProxyValidate() {
}
func (c *RootController) CasProxy() {
pgt := c.Input().Get("pgt")
targetService := c.Input().Get("targetService")
format := c.Input().Get("format")
pgt := c.Ctx.Input.Query("pgt")
targetService := c.Ctx.Input.Query("targetService")
format := c.Ctx.Input.Query("format")
if pgt == "" || targetService == "" {
c.sendCasProxyResponseErr(InvalidRequest, "pgt and targetService must exist", format)
return
@@ -200,7 +200,7 @@ func (c *RootController) CasProxy() {
func (c *RootController) SamlValidate() {
c.Ctx.Output.Header("Content-Type", "text/xml; charset=utf-8")
target := c.Input().Get("TARGET")
target := c.Ctx.Input.Query("TARGET")
body := c.Ctx.Input.RequestBody
envelopRequest := struct {
XMLName xml.Name `xml:"Envelope"`

View File

@@ -34,11 +34,11 @@ import (
// @Success 200 {object} controllers.Response The Response object
// @router /enforce [post]
func (c *ApiController) Enforce() {
permissionId := c.Input().Get("permissionId")
modelId := c.Input().Get("modelId")
resourceId := c.Input().Get("resourceId")
enforcerId := c.Input().Get("enforcerId")
owner := c.Input().Get("owner")
permissionId := c.Ctx.Input.Query("permissionId")
modelId := c.Ctx.Input.Query("modelId")
resourceId := c.Ctx.Input.Query("resourceId")
enforcerId := c.Ctx.Input.Query("enforcerId")
owner := c.Ctx.Input.Query("owner")
params := []string{permissionId, modelId, resourceId, enforcerId, owner}
nonEmpty := 0
@@ -180,10 +180,10 @@ func (c *ApiController) Enforce() {
// @Success 200 {object} controllers.Response The Response object
// @router /batch-enforce [post]
func (c *ApiController) BatchEnforce() {
permissionId := c.Input().Get("permissionId")
modelId := c.Input().Get("modelId")
enforcerId := c.Input().Get("enforcerId")
owner := c.Input().Get("owner")
permissionId := c.Ctx.Input.Query("permissionId")
modelId := c.Ctx.Input.Query("modelId")
enforcerId := c.Ctx.Input.Query("enforcerId")
owner := c.Ctx.Input.Query("owner")
params := []string{permissionId, modelId, enforcerId, owner}
nonEmpty := 0
@@ -304,7 +304,7 @@ func (c *ApiController) BatchEnforce() {
}
func (c *ApiController) GetAllObjects() {
userId := c.Input().Get("userId")
userId := c.Ctx.Input.Query("userId")
if userId == "" {
userId = c.GetSessionUsername()
if userId == "" {
@@ -323,7 +323,7 @@ func (c *ApiController) GetAllObjects() {
}
func (c *ApiController) GetAllActions() {
userId := c.Input().Get("userId")
userId := c.Ctx.Input.Query("userId")
if userId == "" {
userId = c.GetSessionUsername()
if userId == "" {
@@ -342,7 +342,7 @@ func (c *ApiController) GetAllActions() {
}
func (c *ApiController) GetAllRoles() {
userId := c.Input().Get("userId")
userId := c.Ctx.Input.Query("userId")
if userId == "" {
userId = c.GetSessionUsername()
if userId == "" {

View File

@@ -169,8 +169,8 @@ func (c *ApiController) RunCasbinCommand() {
return
}
language := c.Input().Get("language")
argString := c.Input().Get("args")
language := c.Ctx.Input.Query("language")
argString := c.Ctx.Input.Query("args")
if language == "" {
language = "go"
@@ -262,10 +262,10 @@ func (c *ApiController) RunCasbinCommand() {
// @Param hash string The SHA-256 hash string
// @Return error Returns error if validation fails, nil if successful
func validateIdentifier(c *ApiController) error {
language := c.Input().Get("language")
args := c.Input().Get("args")
hash := c.Input().Get("m")
timestamp := c.Input().Get("t")
language := c.Ctx.Input.Query("language")
args := c.Ctx.Input.Query("args")
hash := c.Ctx.Input.Query("m")
timestamp := c.Ctx.Input.Query("t")
if hash == "" || timestamp == "" || language == "" || args == "" {
return fmt.Errorf("invalid identifier")

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
certs, err := object.GetMaskedCerts(object.GetCerts(owner))
@@ -54,7 +54,7 @@ func (c *ApiController) GetCerts() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
certs, err := object.GetMaskedCerts(object.GetPaginationCerts(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil {
c.ResponseError(err.Error())
@@ -72,12 +72,12 @@ func (c *ApiController) GetCerts() {
// @Success 200 {array} object.Cert The Response object
// @router /get-global-certs [get]
func (c *ApiController) GetGlobalCerts() {
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")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
certs, err := object.GetMaskedCerts(object.GetGlobalCerts())
@@ -95,7 +95,7 @@ func (c *ApiController) GetGlobalCerts() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
certs, err := object.GetMaskedCerts(object.GetPaginationGlobalCerts(paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil {
c.ResponseError(err.Error())
@@ -114,7 +114,7 @@ func (c *ApiController) GetGlobalCerts() {
// @Success 200 {object} object.Cert The Response object
// @router /get-cert [get]
func (c *ApiController) GetCert() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
cert, err := object.GetCert(id)
if err != nil {
c.ResponseError(err.Error())
@@ -133,7 +133,7 @@ func (c *ApiController) GetCert() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-cert [post]
func (c *ApiController) UpdateCert() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var cert object.Cert
err := json.Unmarshal(c.Ctx.Input.RequestBody, &cert)

View File

@@ -15,7 +15,7 @@ import (
"strings"
"time"
"github.com/beego/beego"
"github.com/beego/beego/v2/server/web"
"github.com/casdoor/casdoor/proxy"
"github.com/casdoor/casdoor/util"
)
@@ -446,13 +446,13 @@ func downloadCLI() error {
// @Success 200 {object} controllers.Response The Response object
// @router /refresh-engines [post]
func (c *ApiController) RefreshEngines() {
if !beego.AppConfig.DefaultBool("isDemoMode", false) {
if !web.AppConfig.DefaultBool("isDemoMode", false) {
c.ResponseError("refresh engines is only available in demo mode")
return
}
hash := c.Input().Get("m")
timestamp := c.Input().Get("t")
hash := c.Ctx.Input.Query("m")
timestamp := c.Ctx.Input.Query("t")
if hash == "" || timestamp == "" {
c.ResponseError("invalid identifier")
@@ -498,7 +498,7 @@ func (c *ApiController) RefreshEngines() {
// @Title ScheduleCLIUpdater
// @Description Start periodic CLI update scheduler
func ScheduleCLIUpdater() {
if !beego.AppConfig.DefaultBool("isDemoMode", false) {
if !web.AppConfig.DefaultBool("isDemoMode", false) {
return
}
@@ -526,7 +526,7 @@ func DownloadCLI() error {
// @Title InitCLIDownloader
// @Description Initialize CLI downloader and start update scheduler
func InitCLIDownloader() {
if !beego.AppConfig.DefaultBool("isDemoMode", false) {
if !web.AppConfig.DefaultBool("isDemoMode", false) {
return
}

View File

@@ -18,7 +18,7 @@ import (
"encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
@@ -32,13 +32,13 @@ import (
// @Success 200 {array} object.Enforcer
// @router /get-enforcers [get]
func (c *ApiController) GetEnforcers() {
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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
enforcers, err := object.GetEnforcers(owner)
@@ -56,7 +56,7 @@ func (c *ApiController) GetEnforcers() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
enforcers, err := object.GetPaginationEnforcers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -75,8 +75,8 @@ func (c *ApiController) GetEnforcers() {
// @Success 200 {object} object.Enforcer
// @router /get-enforcer [get]
func (c *ApiController) GetEnforcer() {
id := c.Input().Get("id")
loadModelCfg := c.Input().Get("loadModelCfg")
id := c.Ctx.Input.Query("id")
loadModelCfg := c.Ctx.Input.Query("loadModelCfg")
enforcer, err := object.GetEnforcer(id)
if err != nil {
@@ -105,7 +105,7 @@ func (c *ApiController) GetEnforcer() {
// @Success 200 {object} object.Enforcer
// @router /update-enforcer [post]
func (c *ApiController) UpdateEnforcer() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
enforcer := object.Enforcer{}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &enforcer)
@@ -165,8 +165,8 @@ func (c *ApiController) DeleteEnforcer() {
// @Success 200 {array} xormadapter.CasbinRule
// @router /get-policies [get]
func (c *ApiController) GetPolicies() {
id := c.Input().Get("id")
adapterId := c.Input().Get("adapterId")
id := c.Ctx.Input.Query("id")
adapterId := c.Ctx.Input.Query("adapterId")
if adapterId != "" {
adapter, err := object.GetAdapter(adapterId)
@@ -207,7 +207,7 @@ func (c *ApiController) GetPolicies() {
// @Success 200 {array} xormadapter.CasbinRule
// @router /get-filtered-policies [post]
func (c *ApiController) GetFilteredPolicies() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var filters []object.Filter
err := json.Unmarshal(c.Ctx.Input.RequestBody, &filters)
@@ -234,7 +234,7 @@ func (c *ApiController) GetFilteredPolicies() {
// @Success 200 {object} Response
// @router /update-policy [post]
func (c *ApiController) UpdatePolicy() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var policies []xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policies)
@@ -261,7 +261,7 @@ func (c *ApiController) UpdatePolicy() {
// @Success 200 {object} Response
// @router /add-policy [post]
func (c *ApiController) AddPolicy() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var policy xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
@@ -288,7 +288,7 @@ func (c *ApiController) AddPolicy() {
// @Success 200 {object} Response
// @router /remove-policy [post]
func (c *ApiController) RemovePolicy() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var policy xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)

View File

@@ -33,8 +33,8 @@ import (
// @Success 200 {object} controllers.Response The Response object
// @router /faceid-signin-begin [get]
func (c *ApiController) FaceIDSigninBegin() {
userOwner := c.Input().Get("owner")
userName := c.Input().Get("name")
userOwner := c.Ctx.Input.Query("owner")
userName := c.Ctx.Input.Query("name")
user, err := object.GetUserByFields(userOwner, userName)
if err != nil {

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -46,13 +46,13 @@ func (c *ApiController) GetGlobalForms() {
// @Success 200 {array} object.Form The Response object
// @router /get-forms [get]
func (c *ApiController) GetForms() {
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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
forms, err := object.GetForms(owner)
@@ -70,7 +70,7 @@ func (c *ApiController) GetForms() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
forms, err := object.GetPaginationForms(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -88,7 +88,7 @@ func (c *ApiController) GetForms() {
// @Success 200 {object} object.Form The Response object
// @router /get-form [get]
func (c *ApiController) GetForm() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
form, err := object.GetForm(id)
if err != nil {
@@ -108,7 +108,7 @@ func (c *ApiController) GetForm() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-form [post]
func (c *ApiController) UpdateForm() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var form object.Form
err := json.Unmarshal(c.Ctx.Input.RequestBody, &form)

View File

@@ -23,7 +23,7 @@ import "github.com/casdoor/casdoor/object"
// @Success 200 {object} controllers.Response The Response object
// @router /get-dashboard [get]
func (c *ApiController) GetDashboard() {
owner := c.Input().Get("owner")
owner := c.Ctx.Input.Query("owner")
data, err := object.GetDashboard(owner)
if err != nil {

View File

@@ -17,7 +17,7 @@ import (
"encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,14 +30,14 @@ import (
// @Success 200 {array} object.Group The Response object
// @router /get-groups [get]
func (c *ApiController) GetGroups() {
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")
withTree := c.Input().Get("withTree")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
withTree := c.Ctx.Input.Query("withTree")
if limit == "" || page == "" {
groups, err := object.GetGroups(owner)
@@ -66,7 +66,7 @@ func (c *ApiController) GetGroups() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
groups, err := object.GetPaginationGroups(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -109,7 +109,7 @@ func (c *ApiController) GetGroups() {
// @Success 200 {object} object.Group The Response object
// @router /get-group [get]
func (c *ApiController) GetGroup() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
group, err := object.GetGroup(id)
if err != nil {
@@ -135,7 +135,7 @@ func (c *ApiController) GetGroup() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-group [post]
func (c *ApiController) UpdateGroup() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var group object.Group
err := json.Unmarshal(c.Ctx.Input.RequestBody, &group)

View File

@@ -19,7 +19,7 @@ import (
"fmt"
"strings"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -32,13 +32,13 @@ import (
// @Success 200 {array} object.Invitation The Response object
// @router /get-invitations [get]
func (c *ApiController) GetInvitations() {
owner := c.Input().Get("owner")
limit := c.Input().Get("pageSize")
page := c.Input().Get("p")
field := c.Input().Get("field")
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
invitations, err := object.GetInvitations(owner)
@@ -56,7 +56,7 @@ func (c *ApiController) GetInvitations() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
invitations, err := object.GetPaginationInvitations(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -75,7 +75,7 @@ func (c *ApiController) GetInvitations() {
// @Success 200 {object} object.Invitation The Response object
// @router /get-invitation [get]
func (c *ApiController) GetInvitation() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
invitation, err := object.GetInvitation(id)
if err != nil {
@@ -94,8 +94,8 @@ func (c *ApiController) GetInvitation() {
// @Success 200 {object} object.Invitation The Response object
// @router /get-invitation-info [get]
func (c *ApiController) GetInvitationCodeInfo() {
code := c.Input().Get("code")
applicationId := c.Input().Get("applicationId")
code := c.Ctx.Input.Query("code")
applicationId := c.Ctx.Input.Query("applicationId")
application, err := object.GetApplication(applicationId)
if err != nil {
@@ -125,7 +125,7 @@ func (c *ApiController) GetInvitationCodeInfo() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-invitation [post]
func (c *ApiController) UpdateInvitation() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var invitation object.Invitation
err := json.Unmarshal(c.Ctx.Input.RequestBody, &invitation)
@@ -184,7 +184,7 @@ func (c *ApiController) DeleteInvitation() {
// @Success 200 {object} controllers.Response The Response object
// @router /verify-invitation [get]
func (c *ApiController) VerifyInvitation() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
payment, attachInfo, err := object.VerifyInvitation(id)
if err != nil {
@@ -204,7 +204,7 @@ func (c *ApiController) VerifyInvitation() {
// @Success 200 {object} controllers.Response The Response object
// @router /send-invitation [post]
func (c *ApiController) SendInvitation() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var destinations []string
err := json.Unmarshal(c.Ctx.Input.RequestBody, &destinations)
@@ -234,10 +234,19 @@ func (c *ApiController) SendInvitation() {
return
}
application, err := object.GetApplicationByOrganizationName(invitation.Owner)
if err != nil {
c.ResponseError(err.Error())
return
var application *object.Application
if invitation.Application != "" {
application, err = object.GetApplication(fmt.Sprintf("admin/%s-org-%s", invitation.Application, invitation.Owner))
if err != nil {
c.ResponseError(err.Error())
return
}
} else {
application, err = object.GetApplicationByOrganizationName(invitation.Owner)
if err != nil {
c.ResponseError(err.Error())
return
}
}
if application == nil {
@@ -245,6 +254,10 @@ func (c *ApiController) SendInvitation() {
return
}
if application.IsShared {
application.Name = fmt.Sprintf("%s-org-%s", application.Name, invitation.Owner)
}
provider, err := application.GetEmailProvider("Invitation")
if err != nil {
c.ResponseError(err.Error())

View File

@@ -46,7 +46,7 @@ type LdapSyncResp struct {
// @Success 200 {object} controllers.LdapResp The Response object
// @router /get-ldap-users [get]
func (c *ApiController) GetLdapUsers() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
_, ldapId, err := util.GetOwnerAndNameFromIdWithError(id)
if err != nil {
@@ -114,7 +114,7 @@ func (c *ApiController) GetLdapUsers() {
// @Success 200 {array} object.Ldap The Response object
// @router /get-ldaps [get]
func (c *ApiController) GetLdaps() {
owner := c.Input().Get("owner")
owner := c.Ctx.Input.Query("owner")
c.ResponseOk(object.GetMaskedLdaps(object.GetLdaps(owner)))
}
@@ -127,7 +127,7 @@ func (c *ApiController) GetLdaps() {
// @Success 200 {object} object.Ldap The Response object
// @router /get-ldap [get]
func (c *ApiController) GetLdap() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
if util.IsStringsEmpty(id) {
c.ResponseError(c.T("general:Missing parameter"))
@@ -266,7 +266,7 @@ func (c *ApiController) DeleteLdap() {
// @Success 200 {object} controllers.LdapSyncResp The Response object
// @router /sync-ldap-users [post]
func (c *ApiController) SyncLdapUsers() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
owner, ldapId, err := util.GetOwnerAndNameFromIdWithError(id)
if err != nil {

View File

@@ -64,7 +64,14 @@ func (c *ApiController) MfaSetupInitiate() {
return
}
mfaProps, err := MfaUtil.Initiate(user.GetId())
issuer := ""
if organization != nil && organization.DisplayName != "" {
issuer = organization.DisplayName
} else if organization != nil {
issuer = organization.Name
}
mfaProps, err := MfaUtil.Initiate(user.GetId(), issuer)
if err != nil {
c.ResponseError(err.Error())
return

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @Success 200 {array} object.Model The Response object
// @router /get-models [get]
func (c *ApiController) GetModels() {
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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
models, err := object.GetModels(owner)
@@ -54,7 +54,7 @@ func (c *ApiController) GetModels() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
models, err := object.GetPaginationModels(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -73,7 +73,7 @@ func (c *ApiController) GetModels() {
// @Success 200 {object} object.Model The Response object
// @router /get-model [get]
func (c *ApiController) GetModel() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
model, err := object.GetModel(id)
if err != nil {
@@ -93,7 +93,7 @@ func (c *ApiController) GetModel() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-model [post]
func (c *ApiController) UpdateModel() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var model object.Model
err := json.Unmarshal(c.Ctx.Input.RequestBody, &model)

View File

@@ -85,11 +85,12 @@ func (c *RootController) GetJwksByApplication() {
// @Success 200 {object} object.WebFinger
// @router /.well-known/webfinger [get]
func (c *RootController) GetWebFinger() {
resource := c.Input().Get("resource")
resource := c.Ctx.Input.Query("resource")
rels := []string{}
host := c.Ctx.Request.Host
for key, value := range c.Input() {
inputs, _ := c.Input()
for key, value := range inputs {
if strings.HasPrefix(key, "rel") {
rels = append(rels, value...)
}
@@ -115,11 +116,12 @@ func (c *RootController) GetWebFinger() {
// @router /.well-known/:application/webfinger [get]
func (c *RootController) GetWebFingerByApplication() {
application := c.Ctx.Input.Param(":application")
resource := c.Input().Get("resource")
resource := c.Ctx.Input.Query("resource")
rels := []string{}
host := c.Ctx.Request.Host
for key, value := range c.Input() {
inputs, _ := c.Input()
for key, value := range inputs {
if strings.HasPrefix(key, "rel") {
rels = append(rels, value...)
}

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @Success 200 {array} object.Order The Response object
// @router /get-orders [get]
func (c *ApiController) GetOrders() {
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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
orders, err := object.GetOrders(owner)
@@ -54,7 +54,7 @@ func (c *ApiController) GetOrders() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
orders, err := object.GetPaginationOrders(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -74,8 +74,8 @@ func (c *ApiController) GetOrders() {
// @Success 200 {array} object.Order The Response object
// @router /get-user-orders [get]
func (c *ApiController) GetUserOrders() {
owner := c.Input().Get("owner")
user := c.Input().Get("user")
owner := c.Ctx.Input.Query("owner")
user := c.Ctx.Input.Query("user")
orders, err := object.GetUserOrders(owner, user)
if err != nil {
@@ -94,7 +94,7 @@ func (c *ApiController) GetUserOrders() {
// @Success 200 {object} object.Order The Response object
// @router /get-order [get]
func (c *ApiController) GetOrder() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
order, err := object.GetOrder(id)
if err != nil {
@@ -114,7 +114,7 @@ func (c *ApiController) GetOrder() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-order [post]
func (c *ApiController) UpdateOrder() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var order object.Order
err := json.Unmarshal(c.Ctx.Input.RequestBody, &order)

View File

@@ -34,11 +34,11 @@ import (
// @Success 200 {object} object.Order The Response object
// @router /place-order [post]
func (c *ApiController) PlaceOrder() {
productId := c.Input().Get("productId")
pricingName := c.Input().Get("pricingName")
planName := c.Input().Get("planName")
customPriceStr := c.Input().Get("customPrice")
paidUserName := c.Input().Get("userName")
productId := c.Ctx.Input.Query("productId")
pricingName := c.Ctx.Input.Query("pricingName")
planName := c.Ctx.Input.Query("planName")
customPriceStr := c.Ctx.Input.Query("customPrice")
paidUserName := c.Ctx.Input.Query("userName")
if productId == "" {
c.ResponseError(c.T("general:ProductId is required"))
@@ -107,10 +107,10 @@ func (c *ApiController) PlaceOrder() {
// @Success 200 {object} controllers.Response The Response object
// @router /pay-order [post]
func (c *ApiController) PayOrder() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
host := c.Ctx.Request.Host
providerName := c.Input().Get("providerName")
paymentEnv := c.Input().Get("paymentEnv")
providerName := c.Ctx.Input.Query("providerName")
paymentEnv := c.Ctx.Input.Query("paymentEnv")
order, err := object.GetOrder(id)
if err != nil {
@@ -129,7 +129,7 @@ func (c *ApiController) PayOrder() {
return
}
payment, attachInfo, err := object.PayOrder(providerName, host, paymentEnv, order)
payment, attachInfo, err := object.PayOrder(providerName, host, paymentEnv, order, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
@@ -146,7 +146,7 @@ func (c *ApiController) PayOrder() {
// @Success 200 {object} controllers.Response The Response object
// @router /cancel-order [post]
func (c *ApiController) CancelOrder() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
order, err := object.GetOrder(id)
if err != nil {
c.ResponseError(err.Error())

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,14 +30,14 @@ import (
// @Success 200 {array} object.Organization The Response object
// @router /get-organizations [get]
func (c *ApiController) GetOrganizations() {
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")
organizationName := c.Input().Get("organizationName")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
organizationName := c.Ctx.Input.Query("organizationName")
isGlobalAdmin := c.IsGlobalAdmin()
if limit == "" || page == "" {
@@ -71,7 +71,7 @@ func (c *ApiController) GetOrganizations() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
organizations, err := object.GetMaskedOrganizations(object.GetPaginationOrganizations(owner, organizationName, paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil {
c.ResponseError(err.Error())
@@ -91,7 +91,7 @@ func (c *ApiController) GetOrganizations() {
// @Success 200 {object} object.Organization The Response object
// @router /get-organization [get]
func (c *ApiController) GetOrganization() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
organization, err := object.GetMaskedOrganization(object.GetOrganization(id))
if err != nil {
c.ResponseError(err.Error())
@@ -114,7 +114,7 @@ func (c *ApiController) GetOrganization() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-organization [post]
func (c *ApiController) UpdateOrganization() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var organization object.Organization
err := json.Unmarshal(c.Ctx.Input.RequestBody, &organization)
@@ -205,7 +205,7 @@ func (c *ApiController) DeleteOrganization() {
// @router /get-default-application [get]
func (c *ApiController) GetDefaultApplication() {
userId := c.GetSessionUsername()
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
application, err := object.GetDefaultApplication(id)
if err != nil {
@@ -225,7 +225,7 @@ func (c *ApiController) GetDefaultApplication() {
// @Success 200 {array} object.Organization The Response object
// @router /get-organization-names [get]
func (c *ApiController) GetOrganizationNames() {
owner := c.Input().Get("owner")
owner := c.Ctx.Input.Query("owner")
organizationNames, err := object.GetOrganizationsByFields(owner, []string{"name", "display_name"}...)
if err != nil {
c.ResponseError(err.Error())

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
payments, err := object.GetPayments(owner)
@@ -54,7 +54,7 @@ func (c *ApiController) GetPayments() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
payments, err := object.GetPaginationPayments(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -75,8 +75,8 @@ func (c *ApiController) GetPayments() {
// @Success 200 {array} object.Payment The Response object
// @router /get-user-payments [get]
func (c *ApiController) GetUserPayments() {
owner := c.Input().Get("owner")
user := c.Input().Get("user")
owner := c.Ctx.Input.Query("owner")
user := c.Ctx.Input.Query("user")
payments, err := object.GetUserPayments(owner, user)
if err != nil {
@@ -95,7 +95,7 @@ func (c *ApiController) GetUserPayments() {
// @Success 200 {object} object.Payment The Response object
// @router /get-payment [get]
func (c *ApiController) GetPayment() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
payment, err := object.GetPayment(id)
if err != nil {
@@ -115,7 +115,7 @@ func (c *ApiController) GetPayment() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-payment [post]
func (c *ApiController) UpdatePayment() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var payment object.Payment
err := json.Unmarshal(c.Ctx.Input.RequestBody, &payment)
@@ -179,7 +179,7 @@ func (c *ApiController) NotifyPayment() {
body := c.Ctx.Input.RequestBody
payment, err := object.NotifyPayment(body, owner, paymentName)
payment, err := object.NotifyPayment(body, owner, paymentName, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
@@ -196,7 +196,7 @@ func (c *ApiController) NotifyPayment() {
// @Success 200 {object} controllers.Response The Response object
// @router /invoice-payment [post]
func (c *ApiController) InvoicePayment() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
payment, err := object.GetPayment(id)
if err != nil {

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
permissions, err := object.GetPermissions(owner)
@@ -54,7 +54,7 @@ func (c *ApiController) GetPermissions() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
permissions, err := object.GetPaginationPermissions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -94,7 +94,7 @@ func (c *ApiController) GetPermissionsBySubmitter() {
// @Success 200 {array} object.Permission The Response object
// @router /get-permissions-by-role [get]
func (c *ApiController) GetPermissionsByRole() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
permissions, err := object.GetPermissionsByRole(id)
if err != nil {
c.ResponseError(err.Error())
@@ -112,7 +112,7 @@ func (c *ApiController) GetPermissionsByRole() {
// @Success 200 {object} object.Permission The Response object
// @router /get-permission [get]
func (c *ApiController) GetPermission() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
permission, err := object.GetPermission(id)
if err != nil {
@@ -132,7 +132,7 @@ func (c *ApiController) GetPermission() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-permission [post]
func (c *ApiController) UpdatePermission() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var permission object.Permission
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permission)

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @Success 200 {array} object.Plan The Response object
// @router /get-plans [get]
func (c *ApiController) GetPlans() {
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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
plans, err := object.GetPlans(owner)
@@ -54,7 +54,7 @@ func (c *ApiController) GetPlans() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
plan, err := object.GetPaginatedPlans(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -74,8 +74,8 @@ func (c *ApiController) GetPlans() {
// @Success 200 {object} object.Plan The Response object
// @router /get-plan [get]
func (c *ApiController) GetPlan() {
id := c.Input().Get("id")
includeOption := c.Input().Get("includeOption") == "true"
id := c.Ctx.Input.Query("id")
includeOption := c.Ctx.Input.Query("includeOption") == "true"
plan, err := object.GetPlan(id)
if err != nil {
@@ -107,7 +107,7 @@ func (c *ApiController) GetPlan() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-plan [post]
func (c *ApiController) UpdatePlan() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
owner := util.GetOwnerFromId(id)
var plan object.Plan
err := json.Unmarshal(c.Ctx.Input.RequestBody, &plan)

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @Success 200 {array} object.Pricing The Response object
// @router /get-pricings [get]
func (c *ApiController) GetPricings() {
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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
pricings, err := object.GetPricings(owner)
@@ -54,7 +54,7 @@ func (c *ApiController) GetPricings() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
pricing, err := object.GetPaginatedPricings(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -73,7 +73,7 @@ func (c *ApiController) GetPricings() {
// @Success 200 {object} object.Pricing The Response object
// @router /get-pricing [get]
func (c *ApiController) GetPricing() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
pricing, err := object.GetPricing(id)
if err != nil {
@@ -93,7 +93,7 @@ func (c *ApiController) GetPricing() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-pricing [post]
func (c *ApiController) UpdatePricing() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var pricing object.Pricing
err := json.Unmarshal(c.Ctx.Input.RequestBody, &pricing)

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
products, err := object.GetProducts(owner)
@@ -54,7 +54,7 @@ func (c *ApiController) GetProducts() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
products, err := object.GetPaginationProducts(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -73,7 +73,7 @@ func (c *ApiController) GetProducts() {
// @Success 200 {object} object.Product The Response object
// @router /get-product [get]
func (c *ApiController) GetProduct() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
product, err := object.GetProduct(id)
if err != nil {
@@ -99,7 +99,7 @@ func (c *ApiController) GetProduct() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-product [post]
func (c *ApiController) UpdateProduct() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var product object.Product
err := json.Unmarshal(c.Ctx.Input.RequestBody, &product)

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @Success 200 {array} object.Provider The Response object
// @router /get-providers [get]
func (c *ApiController) GetProviders() {
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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
ok, isMaskEnabled := c.IsMaskedEnabled()
if !ok {
@@ -59,7 +59,7 @@ func (c *ApiController) GetProviders() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
paginationProviders, err := object.GetPaginationProviders(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -78,12 +78,12 @@ func (c *ApiController) GetProviders() {
// @Success 200 {array} object.Provider The Response object
// @router /get-global-providers [get]
func (c *ApiController) GetGlobalProviders() {
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")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
ok, isMaskEnabled := c.IsMaskedEnabled()
if !ok {
@@ -106,7 +106,7 @@ func (c *ApiController) GetGlobalProviders() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
paginationGlobalProviders, err := object.GetPaginationGlobalProviders(paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -126,7 +126,7 @@ func (c *ApiController) GetGlobalProviders() {
// @Success 200 {object} object.Provider The Response object
// @router /get-provider [get]
func (c *ApiController) GetProvider() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
ok, isMaskEnabled := c.IsMaskedEnabled()
if !ok {
@@ -164,7 +164,7 @@ func (c *ApiController) requireProviderPermission(provider *object.Provider) boo
// @Success 200 {object} controllers.Response The Response object
// @router /update-provider [post]
func (c *ApiController) UpdateProvider() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var provider object.Provider
err := json.Unmarshal(c.Ctx.Input.RequestBody, &provider)

View File

@@ -19,7 +19,7 @@ import (
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -38,13 +38,13 @@ func (c *ApiController) GetRecords() {
return
}
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")
organizationName := c.Input().Get("organizationName")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
organizationName := c.Ctx.Input.Query("organizationName")
if limit == "" || page == "" {
records, err := object.GetRecords()
@@ -66,7 +66,7 @@ func (c *ApiController) GetRecords() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
records, err := object.GetPaginationRecords(paginator.Offset(), limit, field, value, sortField, sortOrder, filterRecord)
if err != nil {
c.ResponseError(err.Error())

View File

@@ -24,7 +24,7 @@ import (
"path/filepath"
"strings"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -44,14 +44,14 @@ import (
// @Success 200 {array} object.Resource The Response object
// @router /get-resources [get]
func (c *ApiController) GetResources() {
owner := c.Input().Get("owner")
user := c.Input().Get("user")
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")
owner := c.Ctx.Input.Query("owner")
user := c.Ctx.Input.Query("user")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
isOrgAdmin, ok := c.IsOrgAdmin()
if !ok {
@@ -93,7 +93,7 @@ func (c *ApiController) GetResources() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
resources, err := object.GetPaginationResources(owner, user, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -112,7 +112,7 @@ func (c *ApiController) GetResources() {
// @Success 200 {object} object.Resource The Response object
// @router /get-resource [get]
func (c *ApiController) GetResource() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
resource, err := object.GetResource(id)
if err != nil {
@@ -132,7 +132,7 @@ func (c *ApiController) GetResource() {
// @Success 200 {object} controllers.Response Success or error
// @router /update-resource [post]
func (c *ApiController) UpdateResource() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var resource object.Resource
err := json.Unmarshal(c.Ctx.Input.RequestBody, &resource)
@@ -178,9 +178,11 @@ func (c *ApiController) DeleteResource() {
}
if resource.Provider != "" {
c.Input().Set("provider", resource.Provider)
inputs, _ := c.Input()
inputs.Set("provider", resource.Provider)
}
c.Input().Set("fullFilePath", resource.Name)
inputs, _ := c.Input()
inputs.Set("fullFilePath", resource.Name)
provider, err := c.GetProviderFromContext("Storage")
if err != nil {
c.ResponseError(err.Error())
@@ -188,7 +190,7 @@ func (c *ApiController) DeleteResource() {
}
_, resource.Name = refineFullFilePath(resource.Name)
tag := c.Input().Get("tag")
tag := c.Ctx.Input.Query("tag")
if tag == "Direct" {
resource.Name = path.Join(provider.PathPrefix, resource.Name)
}
@@ -218,14 +220,14 @@ func (c *ApiController) DeleteResource() {
// @Success 200 {object} object.Resource FileUrl, objectKey
// @router /upload-resource [post]
func (c *ApiController) UploadResource() {
owner := c.Input().Get("owner")
username := c.Input().Get("user")
application := c.Input().Get("application")
tag := c.Input().Get("tag")
parent := c.Input().Get("parent")
fullFilePath := c.Input().Get("fullFilePath")
createdTime := c.Input().Get("createdTime")
description := c.Input().Get("description")
owner := c.Ctx.Input.Query("owner")
username := c.Ctx.Input.Query("user")
application := c.Ctx.Input.Query("application")
tag := c.Ctx.Input.Query("tag")
parent := c.Ctx.Input.Query("parent")
fullFilePath := c.Ctx.Input.Query("fullFilePath")
createdTime := c.Ctx.Input.Query("createdTime")
description := c.Ctx.Input.Query("description")
file, header, err := c.GetFile("file")
if err != nil {

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
roles, err := object.GetRoles(owner)
@@ -54,7 +54,7 @@ func (c *ApiController) GetRoles() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
roles, err := object.GetPaginationRoles(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -73,7 +73,7 @@ func (c *ApiController) GetRoles() {
// @Success 200 {object} object.Role The Response object
// @router /get-role [get]
func (c *ApiController) GetRole() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
role, err := object.GetRole(id)
if err != nil {
@@ -93,7 +93,7 @@ func (c *ApiController) GetRole() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-role [post]
func (c *ApiController) UpdateRole() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var role object.Role
err := json.Unmarshal(c.Ctx.Input.RequestBody, &role)

View File

@@ -23,7 +23,7 @@ import (
func (c *ApiController) GetSamlMeta() {
host := c.Ctx.Request.Host
paramApp := c.Input().Get("application")
paramApp := c.Ctx.Input.Query("application")
application, err := object.GetApplication(paramApp)
if err != nil {
c.ResponseError(err.Error())
@@ -57,10 +57,10 @@ func (c *ApiController) HandleSamlRedirect() {
owner := c.Ctx.Input.Param(":owner")
application := c.Ctx.Input.Param(":application")
relayState := c.Input().Get("RelayState")
samlRequest := c.Input().Get("SAMLRequest")
username := c.Input().Get("username")
loginHint := c.Input().Get("login_hint")
relayState := c.Ctx.Input.Query("RelayState")
samlRequest := c.Ctx.Input.Query("SAMLRequest")
username := c.Ctx.Input.Query("username")
loginHint := c.Ctx.Input.Query("login_hint")
targetURL := object.GetSamlRedirectAddress(owner, application, relayState, samlRequest, host, username, loginHint)

View File

@@ -15,9 +15,11 @@
package controllers
import (
"context"
"encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +32,13 @@ import (
// @Success 200 {array} string The Response object
// @router /get-sessions [get]
func (c *ApiController) GetSessions() {
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")
owner := c.Input().Get("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
owner := c.Ctx.Input.Query("owner")
if limit == "" || page == "" {
sessions, err := object.GetSessions(owner)
@@ -53,7 +55,7 @@ func (c *ApiController) GetSessions() {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
sessions, err := object.GetPaginationSessions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -68,11 +70,11 @@ func (c *ApiController) GetSessions() {
// @Title GetSingleSession
// @Tag Session API
// @Description Get session for one user in one application.
// @Param sessionPkId query string true "The id(organization/user/application) of session"
// @Param sessionPkId query string true "The session ID in format: organization/user/application (e.g., built-in/admin/app-built-in)"
// @Success 200 {array} string The Response object
// @router /get-session [get]
func (c *ApiController) GetSingleSession() {
id := c.Input().Get("sessionPkId")
id := c.Ctx.Input.Query("sessionPkId")
session, err := object.GetSingleSession(id)
if err != nil {
@@ -87,8 +89,8 @@ func (c *ApiController) GetSingleSession() {
// @Title UpdateSession
// @Tag Session API
// @Description Update session for one user in one application.
// @Param id query string true "The id(organization/user/application) of session"
// @Success 200 {array} string The Response object
// @Param body body object.Session true "The session object to update"
// @Success 200 {object} controllers.Response The Response object
// @router /update-session [post]
func (c *ApiController) UpdateSession() {
var session object.Session
@@ -106,9 +108,8 @@ func (c *ApiController) UpdateSession() {
// @Title AddSession
// @Tag Session API
// @Description Add session for one user in one application. If there are other existing sessions, join the session into the list.
// @Param id query string true "The id(organization/user/application) of session"
// @Param sessionId query string true "sessionId to be added"
// @Success 200 {array} string The Response object
// @Param body body object.Session true "The session object to add"
// @Success 200 {object} controllers.Response The Response object
// @router /add-session [post]
func (c *ApiController) AddSession() {
var session object.Session
@@ -126,8 +127,8 @@ func (c *ApiController) AddSession() {
// @Title DeleteSession
// @Tag Session API
// @Description Delete session for one user in one application.
// @Param id query string true "The id(organization/user/application) of session"
// @Success 200 {array} string The Response object
// @Param body body object.Session true "The session object to delete"
// @Success 200 {object} controllers.Response The Response object
// @router /delete-session [post]
func (c *ApiController) DeleteSession() {
var session object.Session
@@ -137,7 +138,21 @@ func (c *ApiController) DeleteSession() {
return
}
c.Data["json"] = wrapActionResponse(object.DeleteSession(util.GetSessionId(session.Owner, session.Name, session.Application)))
curSessionId := c.Ctx.Input.CruSession.SessionID(context.Background())
sessionId := c.Ctx.Input.Query("sessionId")
if curSessionId == sessionId && sessionId != "" {
c.ResponseError(fmt.Sprintf(c.T("session:session id %s is the current session and cannot be deleted"), curSessionId))
return
}
if sessionId != "" {
c.Data["json"] = wrapActionResponse(object.DeleteSessionId(util.GetSessionId(session.Owner, session.Name, session.Application), sessionId))
c.ServeJSON()
return
}
c.Data["json"] = wrapActionResponse(object.DeleteSession(util.GetSessionId(session.Owner, session.Name, session.Application), curSessionId))
c.ServeJSON()
}
@@ -145,13 +160,13 @@ func (c *ApiController) DeleteSession() {
// @Title IsSessionDuplicated
// @Tag Session API
// @Description Check if there are other different sessions for one user in one application.
// @Param sessionPkId query string true "The id(organization/user/application) of session"
// @Param sessionId query string true "sessionId to be checked"
// @Param sessionPkId query string true "The session ID in format: organization/user/application (e.g., built-in/admin/app-built-in)"
// @Param sessionId query string true "The specific session ID to check"
// @Success 200 {array} string The Response object
// @router /is-session-duplicated [get]
func (c *ApiController) IsSessionDuplicated() {
id := c.Input().Get("sessionPkId")
sessionId := c.Input().Get("sessionId")
id := c.Ctx.Input.Query("sessionPkId")
sessionId := c.Ctx.Input.Query("sessionId")
isUserSessionDuplicated, err := object.IsSessionDuplicated(id, sessionId)
if err != nil {

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @Success 200 {array} object.Subscription The Response object
// @router /get-subscriptions [get]
func (c *ApiController) GetSubscriptions() {
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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
subscriptions, err := object.GetSubscriptions(owner)
@@ -54,7 +54,7 @@ func (c *ApiController) GetSubscriptions() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
subscription, err := object.GetPaginationSubscriptions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -73,7 +73,7 @@ func (c *ApiController) GetSubscriptions() {
// @Success 200 {object} object.Subscription The Response object
// @router /get-subscription [get]
func (c *ApiController) GetSubscription() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
subscription, err := object.GetSubscription(id)
if err != nil {
@@ -93,7 +93,7 @@ func (c *ApiController) GetSubscription() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-subscription [post]
func (c *ApiController) UpdateSubscription() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var subscription object.Subscription
err := json.Unmarshal(c.Ctx.Input.RequestBody, &subscription)

View File

@@ -18,7 +18,7 @@ import (
"encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -31,14 +31,14 @@ import (
// @Success 200 {array} object.Syncer The Response object
// @router /get-syncers [get]
func (c *ApiController) GetSyncers() {
owner := c.Input().Get("owner")
limit := c.Input().Get("pageSize")
page := c.Input().Get("p")
field := c.Input().Get("field")
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
organization := c.Input().Get("organization")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
organization := c.Ctx.Input.Query("organization")
if limit == "" || page == "" {
syncers, err := object.GetMaskedSyncers(object.GetOrganizationSyncers(owner, organization))
@@ -56,7 +56,7 @@ func (c *ApiController) GetSyncers() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
syncers, err := object.GetMaskedSyncers(object.GetPaginationSyncers(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil {
c.ResponseError(err.Error())
@@ -75,7 +75,7 @@ func (c *ApiController) GetSyncers() {
// @Success 200 {object} object.Syncer The Response object
// @router /get-syncer [get]
func (c *ApiController) GetSyncer() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
syncer, err := object.GetMaskedSyncer(object.GetSyncer(id))
if err != nil {
@@ -95,7 +95,7 @@ func (c *ApiController) GetSyncer() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-syncer [post]
func (c *ApiController) UpdateSyncer() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var syncer object.Syncer
err := json.Unmarshal(c.Ctx.Input.RequestBody, &syncer)
@@ -154,7 +154,7 @@ func (c *ApiController) DeleteSyncer() {
// @Success 200 {object} controllers.Response The Response object
// @router /run-syncer [get]
func (c *ApiController) RunSyncer() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
syncer, err := object.GetSyncer(id)
if err != nil {
c.ResponseError(err.Error())

271
controllers/ticket.go Normal file
View File

@@ -0,0 +1,271 @@
// Copyright 2024 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controllers
import (
"encoding/json"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// GetTickets
// @Title GetTickets
// @Tag Ticket API
// @Description get tickets
// @Param owner query string true "The owner of tickets"
// @Success 200 {array} object.Ticket The Response object
// @router /get-tickets [get]
func (c *ApiController) GetTickets() {
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
user := c.getCurrentUser()
isAdmin := c.IsAdmin()
var tickets []*object.Ticket
var err error
if limit == "" || page == "" {
if isAdmin {
tickets, err = object.GetTickets(owner)
} else {
tickets, err = object.GetUserTickets(owner, user.GetId())
}
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(tickets)
} else {
limit := util.ParseInt(limit)
var count int64
if isAdmin {
count, err = object.GetTicketCount(owner, field, value)
} else {
// For non-admin users, only show their own tickets
tickets, err = object.GetUserTickets(owner, user.GetId())
if err != nil {
c.ResponseError(err.Error())
return
}
count = int64(len(tickets))
}
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
if isAdmin {
tickets, err = object.GetPaginationTickets(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
}
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(tickets, paginator.Nums())
}
}
// GetTicket
// @Title GetTicket
// @Tag Ticket API
// @Description get ticket
// @Param id query string true "The id ( owner/name ) of the ticket"
// @Success 200 {object} object.Ticket The Response object
// @router /get-ticket [get]
func (c *ApiController) GetTicket() {
id := c.Ctx.Input.Query("id")
ticket, err := object.GetTicket(id)
if err != nil {
c.ResponseError(err.Error())
return
}
// Check permission: user can only view their own tickets unless they are admin
user := c.getCurrentUser()
isAdmin := c.IsAdmin()
if ticket != nil && !isAdmin && ticket.User != user.GetId() {
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}
c.ResponseOk(ticket)
}
// UpdateTicket
// @Title UpdateTicket
// @Tag Ticket API
// @Description update ticket
// @Param id query string true "The id ( owner/name ) of the ticket"
// @Param body body object.Ticket true "The details of the ticket"
// @Success 200 {object} controllers.Response The Response object
// @router /update-ticket [post]
func (c *ApiController) UpdateTicket() {
id := c.Ctx.Input.Query("id")
var ticket object.Ticket
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ticket)
if err != nil {
c.ResponseError(err.Error())
return
}
// Check permission
user := c.getCurrentUser()
isAdmin := c.IsAdmin()
existingTicket, err := object.GetTicket(id)
if err != nil {
c.ResponseError(err.Error())
return
}
if existingTicket == nil {
c.ResponseError(c.T("ticket:Ticket not found"))
return
}
// Normal users can only close their own tickets
if !isAdmin {
if existingTicket.User != user.GetId() {
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}
// Normal users can only change state to "Closed"
if ticket.State != "Closed" && ticket.State != existingTicket.State {
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}
// Preserve original fields that users shouldn't modify
ticket.Owner = existingTicket.Owner
ticket.Name = existingTicket.Name
ticket.User = existingTicket.User
ticket.CreatedTime = existingTicket.CreatedTime
}
c.Data["json"] = wrapActionResponse(object.UpdateTicket(id, &ticket))
c.ServeJSON()
}
// AddTicket
// @Title AddTicket
// @Tag Ticket API
// @Description add ticket
// @Param body body object.Ticket true "The details of the ticket"
// @Success 200 {object} controllers.Response The Response object
// @router /add-ticket [post]
func (c *ApiController) AddTicket() {
var ticket object.Ticket
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ticket)
if err != nil {
c.ResponseError(err.Error())
return
}
// Set the user field to the current user
user := c.getCurrentUser()
ticket.User = user.GetId()
c.Data["json"] = wrapActionResponse(object.AddTicket(&ticket))
c.ServeJSON()
}
// DeleteTicket
// @Title DeleteTicket
// @Tag Ticket API
// @Description delete ticket
// @Param body body object.Ticket true "The details of the ticket"
// @Success 200 {object} controllers.Response The Response object
// @router /delete-ticket [post]
func (c *ApiController) DeleteTicket() {
var ticket object.Ticket
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ticket)
if err != nil {
c.ResponseError(err.Error())
return
}
// Only admins can delete tickets
if !c.IsAdmin() {
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}
c.Data["json"] = wrapActionResponse(object.DeleteTicket(&ticket))
c.ServeJSON()
}
// AddTicketMessage
// @Title AddTicketMessage
// @Tag Ticket API
// @Description add a message to a ticket
// @Param id query string true "The id ( owner/name ) of the ticket"
// @Param body body object.TicketMessage true "The message to add"
// @Success 200 {object} controllers.Response The Response object
// @router /add-ticket-message [post]
func (c *ApiController) AddTicketMessage() {
id := c.Ctx.Input.Query("id")
var message object.TicketMessage
err := json.Unmarshal(c.Ctx.Input.RequestBody, &message)
if err != nil {
c.ResponseError(err.Error())
return
}
// Check permission
user := c.getCurrentUser()
isAdmin := c.IsAdmin()
ticket, err := object.GetTicket(id)
if err != nil {
c.ResponseError(err.Error())
return
}
if ticket == nil {
c.ResponseError(c.T("ticket:Ticket not found"))
return
}
// Users can only add messages to their own tickets, admins can add to any ticket
if !isAdmin && ticket.User != user.GetId() {
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}
// Set the author and admin flag
message.Author = user.GetId()
message.IsAdmin = isAdmin
c.Data["json"] = wrapActionResponse(object.AddTicketMessage(id, &message))
c.ServeJSON()
}

View File

@@ -19,7 +19,7 @@ import (
"fmt"
"time"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -28,20 +28,20 @@ import (
// @Title GetTokens
// @Tag Token API
// @Description get tokens
// @Param owner query string true "The owner of tokens"
// @Param owner query string true "The organization name (e.g., built-in)"
// @Param pageSize query string true "The size of each page"
// @Param p query string true "The number of the page"
// @Success 200 {array} object.Token The Response object
// @router /get-tokens [get]
func (c *ApiController) GetTokens() {
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")
organization := c.Input().Get("organization")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
organization := c.Ctx.Input.Query("organization")
if limit == "" || page == "" {
token, err := object.GetTokens(owner, organization)
if err != nil {
@@ -58,7 +58,7 @@ func (c *ApiController) GetTokens() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
tokens, err := object.GetPaginationTokens(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -73,11 +73,11 @@ func (c *ApiController) GetTokens() {
// @Title GetToken
// @Tag Token API
// @Description get token
// @Param id query string true "The id ( owner/name ) of token"
// @Param id query string true "The token ID in format: organization/token-name (e.g., built-in/token-123456)"
// @Success 200 {object} object.Token The Response object
// @router /get-token [get]
func (c *ApiController) GetToken() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
token, err := object.GetToken(id)
if err != nil {
c.ResponseError(err.Error())
@@ -91,12 +91,12 @@ func (c *ApiController) GetToken() {
// @Title UpdateToken
// @Tag Token API
// @Description update token
// @Param id query string true "The id ( owner/name ) of token"
// @Param id query string true "The token ID in format: organization/token-name (e.g., built-in/token-123456)"
// @Param body body object.Token true "Details of the token"
// @Success 200 {object} controllers.Response The Response object
// @router /update-token [post]
func (c *ApiController) UpdateToken() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var token object.Token
err := json.Unmarshal(c.Ctx.Input.RequestBody, &token)
@@ -160,19 +160,19 @@ func (c *ApiController) DeleteToken() {
// @Success 401 {object} object.TokenError The Response object
// @router /login/oauth/access_token [post]
func (c *ApiController) GetOAuthToken() {
clientId := c.Input().Get("client_id")
clientSecret := c.Input().Get("client_secret")
grantType := c.Input().Get("grant_type")
code := c.Input().Get("code")
verifier := c.Input().Get("code_verifier")
scope := c.Input().Get("scope")
nonce := c.Input().Get("nonce")
username := c.Input().Get("username")
password := c.Input().Get("password")
tag := c.Input().Get("tag")
avatar := c.Input().Get("avatar")
refreshToken := c.Input().Get("refresh_token")
deviceCode := c.Input().Get("device_code")
clientId := c.Ctx.Input.Query("client_id")
clientSecret := c.Ctx.Input.Query("client_secret")
grantType := c.Ctx.Input.Query("grant_type")
code := c.Ctx.Input.Query("code")
verifier := c.Ctx.Input.Query("code_verifier")
scope := c.Ctx.Input.Query("scope")
nonce := c.Ctx.Input.Query("nonce")
username := c.Ctx.Input.Query("username")
password := c.Ctx.Input.Query("password")
tag := c.Ctx.Input.Query("tag")
avatar := c.Ctx.Input.Query("avatar")
refreshToken := c.Ctx.Input.Query("refresh_token")
deviceCode := c.Ctx.Input.Query("device_code")
if clientId == "" && clientSecret == "" {
clientId, clientSecret, _ = c.Ctx.Request.BasicAuth()
@@ -288,11 +288,11 @@ func (c *ApiController) GetOAuthToken() {
// @Success 401 {object} object.TokenError The Response object
// @router /login/oauth/refresh_token [post]
func (c *ApiController) RefreshToken() {
grantType := c.Input().Get("grant_type")
refreshToken := c.Input().Get("refresh_token")
scope := c.Input().Get("scope")
clientId := c.Input().Get("client_id")
clientSecret := c.Input().Get("client_secret")
grantType := c.Ctx.Input.Query("grant_type")
refreshToken := c.Ctx.Input.Query("refresh_token")
scope := c.Ctx.Input.Query("scope")
clientId := c.Ctx.Input.Query("client_id")
clientSecret := c.Ctx.Input.Query("client_secret")
host := c.Ctx.Request.Host
if clientId == "" {
@@ -342,11 +342,11 @@ func (c *ApiController) ResponseTokenError(errorMsg string) {
// @Success 401 {object} object.TokenError The Response object
// @router /login/oauth/introspect [post]
func (c *ApiController) IntrospectToken() {
tokenValue := c.Input().Get("token")
tokenValue := c.Ctx.Input.Query("token")
clientId, clientSecret, ok := c.Ctx.Request.BasicAuth()
if !ok {
clientId = c.Input().Get("client_id")
clientSecret = c.Input().Get("client_secret")
clientId = c.Ctx.Input.Query("client_id")
clientSecret = c.Ctx.Input.Query("client_secret")
if clientId == "" || clientSecret == "" {
c.ResponseTokenError(object.InvalidRequest)
return
@@ -369,7 +369,7 @@ func (c *ApiController) IntrospectToken() {
c.ServeJSON()
}
tokenTypeHint := c.Input().Get("token_type_hint")
tokenTypeHint := c.Ctx.Input.Query("token_type_hint")
var token *object.Token
if tokenTypeHint != "" {
token, err = object.GetTokenByTokenValue(tokenValue, tokenTypeHint)

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -30,13 +30,13 @@ import (
// @Success 200 {array} object.Transaction The Response object
// @router /get-transactions [get]
func (c *ApiController) GetTransactions() {
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")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
var transactions []*object.Transaction
@@ -86,7 +86,7 @@ func (c *ApiController) GetTransactions() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
transactions, err := object.GetPaginationTransactions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -105,7 +105,7 @@ func (c *ApiController) GetTransactions() {
// @Success 200 {object} object.Transaction The Response object
// @router /get-transaction [get]
func (c *ApiController) GetTransaction() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
transaction, err := object.GetTransaction(id)
if err != nil {
@@ -146,7 +146,7 @@ func (c *ApiController) GetTransaction() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-transaction [post]
func (c *ApiController) UpdateTransaction() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var transaction object.Transaction
err := json.Unmarshal(c.Ctx.Input.RequestBody, &transaction)
@@ -175,7 +175,7 @@ func (c *ApiController) AddTransaction() {
return
}
dryRunParam := c.Input().Get("dryRun")
dryRunParam := c.Ctx.Input.Query("dryRun")
dryRun := dryRunParam != ""
affected, transactionId, err := object.AddTransaction(&transaction, c.GetAcceptLanguage(), dryRun)

View File

@@ -19,7 +19,7 @@ import (
"fmt"
"strings"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
@@ -32,12 +32,12 @@ import (
// @Success 200 {array} object.User The Response object
// @router /get-global-users [get]
func (c *ApiController) GetGlobalUsers() {
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")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
users, err := object.GetMaskedUsers(object.GetGlobalUsers())
@@ -55,7 +55,7 @@ func (c *ApiController) GetGlobalUsers() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
users, err := object.GetPaginationGlobalUsers(paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -80,14 +80,14 @@ func (c *ApiController) GetGlobalUsers() {
// @Success 200 {array} object.User The Response object
// @router /get-users [get]
func (c *ApiController) GetUsers() {
owner := c.Input().Get("owner")
groupName := c.Input().Get("groupName")
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")
owner := c.Ctx.Input.Query("owner")
groupName := c.Ctx.Input.Query("groupName")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
if limit == "" || page == "" {
if groupName != "" {
@@ -115,7 +115,7 @@ func (c *ApiController) GetUsers() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
users, err := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder, groupName)
if err != nil {
c.ResponseError(err.Error())
@@ -144,11 +144,11 @@ func (c *ApiController) GetUsers() {
// @Success 200 {object} object.User The Response object
// @router /get-user [get]
func (c *ApiController) GetUser() {
id := c.Input().Get("id")
email := c.Input().Get("email")
phone := c.Input().Get("phone")
userId := c.Input().Get("userId")
owner := c.Input().Get("owner")
id := c.Ctx.Input.Query("id")
email := c.Ctx.Input.Query("email")
phone := c.Ctx.Input.Query("phone")
userId := c.Ctx.Input.Query("userId")
owner := c.Ctx.Input.Query("owner")
var err error
var userFromUserId *object.User
if userId != "" && owner != "" {
@@ -259,10 +259,10 @@ func (c *ApiController) GetUser() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-user [post]
func (c *ApiController) UpdateUser() {
id := c.Input().Get("id")
userId := c.Input().Get("userId")
owner := c.Input().Get("owner")
columnsStr := c.Input().Get("columns")
id := c.Ctx.Input.Query("id")
userId := c.Ctx.Input.Query("userId")
owner := c.Ctx.Input.Query("owner")
columnsStr := c.Ctx.Input.Query("columns")
var user object.User
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
@@ -336,7 +336,7 @@ func (c *ApiController) UpdateUser() {
}
isAdmin := c.IsAdmin()
allowDisplayNameEmpty := c.Input().Get("allowEmpty") != ""
allowDisplayNameEmpty := c.Ctx.Input.Query("allowEmpty") != ""
if pass, err := object.CheckPermissionForUpdateUser(oldUser, &user, isAdmin, allowDisplayNameEmpty, c.GetAcceptLanguage()); !pass {
c.ResponseError(err)
return
@@ -690,9 +690,9 @@ func (c *ApiController) CheckUserPassword() {
// @Success 200 {array} object.User The Response object
// @router /get-sorted-users [get]
func (c *ApiController) GetSortedUsers() {
owner := c.Input().Get("owner")
sorter := c.Input().Get("sorter")
limit := util.ParseInt(c.Input().Get("limit"))
owner := c.Ctx.Input.Query("owner")
sorter := c.Ctx.Input.Query("sorter")
limit := util.ParseInt(c.Ctx.Input.Query("limit"))
users, err := object.GetMaskedUsers(object.GetSortedUsers(owner, sorter, limit))
if err != nil {
@@ -712,8 +712,8 @@ func (c *ApiController) GetSortedUsers() {
// @Success 200 {int} int The count of filtered users for an organization
// @router /get-user-count [get]
func (c *ApiController) GetUserCount() {
owner := c.Input().Get("owner")
isOnline := c.Input().Get("isOnline")
owner := c.Ctx.Input.Query("owner")
isOnline := c.Ctx.Input.Query("isOnline")
var count int64
var err error
@@ -777,3 +777,133 @@ func (c *ApiController) RemoveUserFromGroup() {
c.ResponseOk(affected)
}
// VerifyIdentification
// @Title VerifyIdentification
// @Tag User API
// @Description verify user's real identity using ID Verification provider
// @Param owner query string false "The owner of the user (optional, defaults to logged-in user)"
// @Param name query string false "The name of the user (optional, defaults to logged-in user)"
// @Param provider query string false "The name of the ID Verification provider (optional, auto-selected if not provided)"
// @Success 200 {object} controllers.Response The Response object
// @router /verify-identification [post]
func (c *ApiController) VerifyIdentification() {
owner := c.Ctx.Input.Query("owner")
name := c.Ctx.Input.Query("name")
providerName := c.Ctx.Input.Query("provider")
// If user not specified, use logged-in user
if owner == "" || name == "" {
loggedInUser := c.GetSessionUsername()
if loggedInUser == "" {
c.ResponseError(c.T("general:Please login first"))
return
}
var err error
owner, name, err = util.GetOwnerAndNameFromIdWithError(loggedInUser)
if err != nil {
c.ResponseError(err.Error())
return
}
} else {
// If user is specified, check if current user has permission to verify other users
// Only admins can verify other users
loggedInUser := c.GetSessionUsername()
if loggedInUser != util.GetId(owner, name) && !c.IsAdmin() {
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}
}
user, err := object.GetUser(util.GetId(owner, name))
if err != nil {
c.ResponseError(err.Error())
return
}
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(owner, name)))
return
}
if user.IdCard == "" || user.IdCardType == "" || user.RealName == "" {
c.ResponseError(c.T("user:ID card information and real name are required"))
return
}
if user.IsVerified {
c.ResponseError(c.T("user:User is already verified"))
return
}
var provider *object.Provider
// If provider not specified, find suitable IDV provider from user's application
if providerName == "" {
application, err := object.GetApplicationByUser(user)
if err != nil {
c.ResponseError(err.Error())
return
}
if application == nil {
c.ResponseError(c.T("user:No application found for user"))
return
}
// Find IDV provider from application
idvProvider, err := object.GetIdvProviderByApplication(util.GetId(application.Owner, application.Name), "false", c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
if idvProvider == nil {
c.ResponseError(c.T("provider:No ID Verification provider configured"))
return
}
provider = idvProvider
} else {
provider, err = object.GetProvider(providerName)
if err != nil {
c.ResponseError(err.Error())
return
}
if provider == nil {
c.ResponseError(fmt.Sprintf(c.T("provider:The provider: %s does not exist"), providerName))
return
}
if provider.Category != "ID Verification" {
c.ResponseError(c.T("provider:Provider is not an ID Verification provider"))
return
}
}
idvProvider := object.GetIdvProviderFromProvider(provider)
if idvProvider == nil {
c.ResponseError(c.T("provider:Failed to initialize ID Verification provider"))
return
}
verified, err := idvProvider.VerifyIdentity(user.IdCardType, user.IdCard, user.RealName)
if err != nil {
c.ResponseError(err.Error())
return
}
if !verified {
c.ResponseError(c.T("user:Identity verification failed"))
return
}
// Set IsVerified to true upon successful verification
user.IsVerified = true
_, err = object.UpdateUser(user.GetId(), user, []string{"is_verified"}, false)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(user.RealName)
}

View File

@@ -106,7 +106,7 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
}
if object.IsAppUser(userId) {
tmpUserId := c.Input().Get("userId")
tmpUserId := c.Ctx.Input.Query("userId")
if tmpUserId != "" {
userId = tmpUserId
}
@@ -172,7 +172,7 @@ func (c *ApiController) IsOrgAdmin() (bool, bool) {
// IsMaskedEnabled ...
func (c *ApiController) IsMaskedEnabled() (bool, bool) {
isMaskEnabled := true
withSecret := c.Input().Get("withSecret")
withSecret := c.Ctx.Input.Query("withSecret")
if withSecret == "1" {
isMaskEnabled = false
@@ -202,14 +202,14 @@ func refineFullFilePath(fullFilePath string) (string, string) {
}
func (c *ApiController) GetProviderFromContext(category string) (*object.Provider, error) {
providerName := c.Input().Get("provider")
providerName := c.Ctx.Input.Query("provider")
if providerName == "" {
field := c.Input().Get("field")
value := c.Input().Get("value")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
if field == "provider" && value != "" {
providerName = value
} else {
fullFilePath := c.Input().Get("fullFilePath")
fullFilePath := c.Ctx.Input.Query("fullFilePath")
providerName, _ = refineFullFilePath(fullFilePath)
}
}

View File

@@ -20,7 +20,7 @@ import (
"fmt"
"strings"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/captcha"
"github.com/casdoor/casdoor/form"
"github.com/casdoor/casdoor/object"
@@ -49,14 +49,14 @@ func (c *ApiController) GetVerifications() {
return
}
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")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
owner := c.Input().Get("owner")
owner := c.Ctx.Input.Query("owner")
// For global admin with organizationName parameter, use it to filter
// For org admin, use their organization
if c.IsGlobalAdmin() && owner != "" {
@@ -79,7 +79,7 @@ func (c *ApiController) GetVerifications() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
payments, err := object.GetPaginationVerifications(organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
@@ -100,8 +100,8 @@ func (c *ApiController) GetVerifications() {
// @Success 200 {array} object.Verification The Response object
// @router /get-user-payments [get]
func (c *ApiController) GetUserVerifications() {
owner := c.Input().Get("owner")
user := c.Input().Get("user")
owner := c.Ctx.Input.Query("owner")
user := c.Ctx.Input.Query("user")
payments, err := object.GetUserVerifications(owner, user)
if err != nil {
@@ -120,7 +120,7 @@ func (c *ApiController) GetUserVerifications() {
// @Success 200 {object} object.Verification The Response object
// @router /get-payment [get]
func (c *ApiController) GetVerification() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
payment, err := object.GetVerification(id)
if err != nil {

View File

@@ -126,8 +126,8 @@ func (c *ApiController) WebAuthnSigninBegin() {
return
}
userOwner := c.Input().Get("owner")
userName := c.Input().Get("name")
userOwner := c.Ctx.Input.Query("owner")
userName := c.Ctx.Input.Query("name")
var options *protocol.CredentialAssertion
var sessionData *webauthn.SessionData
@@ -171,8 +171,8 @@ func (c *ApiController) WebAuthnSigninBegin() {
// @Success 200 {object} controllers.Response "The Response object"
// @router /webauthn/signin/finish [post]
func (c *ApiController) WebAuthnSigninFinish() {
responseType := c.Input().Get("responseType")
clientId := c.Input().Get("clientId")
responseType := c.Ctx.Input.Query("responseType")
clientId := c.Ctx.Input.Query("clientId")
webauthnObj, err := object.GetWebAuthnObject(c.Ctx.Request.Host)
if err != nil {
c.ResponseError(err.Error())

View File

@@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/beego/beego/v2/core/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@@ -31,14 +31,14 @@ import (
// @router /get-webhooks [get]
// @Security test_apiKey
func (c *ApiController) GetWebhooks() {
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")
organization := c.Input().Get("organization")
owner := c.Ctx.Input.Query("owner")
limit := c.Ctx.Input.Query("pageSize")
page := c.Ctx.Input.Query("p")
field := c.Ctx.Input.Query("field")
value := c.Ctx.Input.Query("value")
sortField := c.Ctx.Input.Query("sortField")
sortOrder := c.Ctx.Input.Query("sortOrder")
organization := c.Ctx.Input.Query("organization")
if limit == "" || page == "" {
webhooks, err := object.GetWebhooks(owner, organization)
@@ -56,7 +56,7 @@ func (c *ApiController) GetWebhooks() {
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
webhooks, err := object.GetPaginationWebhooks(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
@@ -76,7 +76,7 @@ func (c *ApiController) GetWebhooks() {
// @Success 200 {object} object.Webhook The Response object
// @router /get-webhook [get]
func (c *ApiController) GetWebhook() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
webhook, err := object.GetWebhook(id)
if err != nil {
@@ -96,7 +96,7 @@ func (c *ApiController) GetWebhook() {
// @Success 200 {object} controllers.Response The Response object
// @router /update-webhook [post]
func (c *ApiController) UpdateWebhook() {
id := c.Input().Get("id")
id := c.Ctx.Input.Query("id")
var webhook object.Webhook
err := json.Unmarshal(c.Ctx.Input.RequestBody, &webhook)

184
go.mod
View File

@@ -6,20 +6,22 @@ require (
github.com/Masterminds/squirrel v1.5.3
github.com/NdoleStudio/lemonsqueezy-go v1.2.4
github.com/PaddleHQ/paddle-go-sdk v1.0.0
github.com/adyen/adyen-go-api-library/v11 v11.0.0
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
github.com/alibabacloud-go/cloudauth-20190307/v3 v3.9.2
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4
github.com/alibabacloud-go/facebody-20191230/v5 v5.1.2
github.com/alibabacloud-go/openapi-util v0.1.0
github.com/alibabacloud-go/tea v1.3.2
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
github.com/aws/aws-sdk-go v1.45.5
github.com/beego/beego v1.12.12
github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0
github.com/beego/beego/v2 v2.3.8
github.com/beevik/etree v1.1.0
github.com/casbin/casbin/v2 v2.77.2
github.com/casdoor/go-sms-sender v0.25.0
github.com/casdoor/gomail/v2 v2.2.0
github.com/casdoor/ldapserver v1.2.0
github.com/casdoor/notify v1.0.1
github.com/casdoor/notify2 v1.6.0
github.com/casdoor/oss v1.8.0
github.com/casdoor/xorm-adapter/v3 v3.1.0
github.com/casvisor/casvisor-go-sdk v1.4.0
@@ -28,12 +30,13 @@ require (
github.com/elimity-com/scim v0.0.0-20230426070224-941a5eac92f3
github.com/fogleman/gg v1.3.0
github.com/go-asn1-ber/asn1-ber v1.5.5
github.com/go-git/go-git/v5 v5.13.0
github.com/go-git/go-git/v5 v5.16.3
github.com/go-jose/go-jose/v4 v4.1.2
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-mysql-org/go-mysql v1.7.0
github.com/go-pay/gopay v1.5.115
github.com/go-pay/util v0.0.4
github.com/go-sql-driver/mysql v1.6.0
github.com/go-sql-driver/mysql v1.8.1
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/go-webauthn/webauthn v0.10.2
github.com/golang-jwt/jwt/v5 v5.2.2
@@ -42,19 +45,19 @@ require (
github.com/lestrrat-go/jwx v1.2.29
github.com/lib/pq v1.10.9
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
github.com/markbates/goth v1.79.0
github.com/markbates/goth v1.82.0
github.com/mitchellh/mapstructure v1.5.0
github.com/nyaruka/phonenumbers v1.2.2
github.com/polarsource/polar-go v0.12.0
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.11.1
github.com/prometheus/client_model v0.4.0
github.com/prometheus/client_golang v1.19.0
github.com/prometheus/client_model v0.6.0
github.com/qiangmzsx/string-adapter/v2 v2.1.0
github.com/robfig/cron/v3 v3.0.1
github.com/russellhaering/gosaml2 v0.9.0
github.com/russellhaering/goxmldsig v1.2.0
github.com/sendgrid/sendgrid-go v3.14.0+incompatible
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/sendgrid/sendgrid-go v3.16.0+incompatible
github.com/shirou/gopsutil/v4 v4.25.9
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
github.com/stretchr/testify v1.11.1
github.com/stripe/stripe-go/v74 v74.29.0
@@ -63,33 +66,39 @@ require (
github.com/xorm-io/builder v0.3.13
github.com/xorm-io/core v0.7.4
github.com/xorm-io/xorm v1.1.6
golang.org/x/crypto v0.39.0
golang.org/x/net v0.40.0
golang.org/x/oauth2 v0.17.0
golang.org/x/text v0.26.0
google.golang.org/api v0.150.0
gopkg.in/square/go-jose.v2 v2.6.0
golang.org/x/crypto v0.40.0
golang.org/x/net v0.41.0
golang.org/x/oauth2 v0.27.0
golang.org/x/text v0.27.0
google.golang.org/api v0.215.0
layeh.com/radius v0.0.0-20231213012653-1006025d24f8
maunium.net/go/mautrix v0.16.0
maunium.net/go/mautrix v0.22.1
modernc.org/sqlite v1.18.2
)
require (
cloud.google.com/go v0.110.8 // indirect
cloud.google.com/go/compute v1.23.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.3 // indirect
cloud.google.com/go/storage v1.35.1 // indirect
cel.dev/expr v0.18.0 // indirect
cloud.google.com/go v0.116.0 // indirect
cloud.google.com/go/auth v0.13.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/iam v1.2.2 // indirect
cloud.google.com/go/monitoring v1.21.2 // indirect
cloud.google.com/go/storage v1.47.0 // indirect
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/Azure/azure-storage-blob-go v0.15.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20221121042443-a3fd332d56d9 // indirect
github.com/SherClockHolmes/webpush-go v1.2.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20240116134246-a8cbe886bab0 // indirect
github.com/SherClockHolmes/webpush-go v1.4.0 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/darabonba-number v1.0.4 // indirect
github.com/alibabacloud-go/debug v1.0.1 // indirect
@@ -105,34 +114,44 @@ require (
github.com/aliyun/credentials-go v1.3.10 // indirect
github.com/apistd/uni-go-sdk v0.0.2 // indirect
github.com/atc0005/go-teams-notify/v2 v2.13.0 // indirect
github.com/aws/aws-sdk-go v1.45.5 // indirect
github.com/aws/smithy-go v1.24.0 // indirect
github.com/baidubce/bce-sdk-go v0.9.156 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blinkbean/dingtalk v0.0.0-20210905093040-7d935c0f7e19 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/bwmarrin/discordgo v0.27.1 // indirect
github.com/blinkbean/dingtalk v1.1.3 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/bwmarrin/discordgo v0.28.1 // indirect
github.com/caarlos0/go-reddit/v3 v3.0.1 // indirect
github.com/casdoor/casdoor-go-sdk v0.50.0 // indirect
github.com/casdoor/go-reddit/v2 v2.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
github.com/cschomburg/go-pushbullet v0.0.0-20171206132031-67759df45fbb // indirect
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/dghubble/oauth1 v0.7.2 // indirect
github.com/dghubble/sling v1.4.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/dghubble/oauth1 v0.7.3 // indirect
github.com/dghubble/sling v1.4.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/di-wu/parser v0.2.2 // indirect
github.com/di-wu/xsd-datetime v1.0.0 // indirect
github.com/drswork/go-twitter v0.0.0-20221107160839-dea1b6ed53d7 // indirect
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
github.com/ebitengine/purego v0.9.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/envoyproxy/go-control-plane v0.13.1 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
github.com/ggicci/httpin v0.19.0 // indirect
github.com/ggicci/owl v0.8.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.0 // indirect
github.com/go-lark/lark v1.9.0 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-lark/lark v1.15.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-pay/crypto v0.0.1 // indirect
github.com/go-pay/errgroup v0.0.3 // indirect
@@ -140,23 +159,20 @@ require (
github.com/go-pay/xlog v0.0.3 // indirect
github.com/go-pay/xtime v0.0.2 // indirect
github.com/go-webauthn/x v0.1.9 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.0 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gregdel/pushover v1.2.1 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/gregdel/pushover v1.3.1 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@@ -167,17 +183,17 @@ require (
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/line/line-bot-sdk-go v7.8.0+incompatible // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/markbates/going v1.0.0 // indirect
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-ieproxy v0.0.1 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mileusna/viber v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -186,14 +202,17 @@ require (
github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 // indirect
github.com/pingcap/log v0.0.0-20210625125904-98ed8e2eb1c7 // indirect
github.com/pingcap/tidb/parser v0.0.0-20221126021158-6b02a5d8ba7d // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/qiniu/go-sdk/v7 v7.12.1 // indirect
github.com/redis/go-redis/v9 v9.5.5 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/rs/zerolog v1.30.0 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/scim2/filter-parser/v2 v2.2.0 // indirect
github.com/sendgrid/rest v2.6.9+incompatible // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
@@ -201,61 +220,66 @@ require (
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/slack-go/slack v0.12.3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/slack-go/slack v0.15.0 // indirect
github.com/spyzhov/ajson v0.8.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.744 // indirect
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.744 // indirect
github.com/tidwall/gjson v1.16.0 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/tklauser/numcpus v0.4.0 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/twilio/twilio-go v1.13.0 // indirect
github.com/ucloud/ucloud-sdk-go v0.22.5 // indirect
github.com/utahta/go-linenotify v0.5.0 // indirect
github.com/volcengine/volc-sdk-golang v1.0.117 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.mau.fi/util v0.0.0-20230805171708-199bf3eec776 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mau.fi/util v0.8.3 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
go.opentelemetry.io/otel v1.32.0 // indirect
go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/image v0.0.0-20190802002840-cff245a6509b // indirect
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e // indirect
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.33.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
google.golang.org/grpc v1.68.0 // indirect
google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3 // indirect
google.golang.org/protobuf v1.36.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.1.1 // indirect
maunium.net/go/maulogger/v2 v2.4.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.37.0 // indirect
modernc.org/ccgo/v3 v3.16.9 // indirect
modernc.org/libc v1.18.0 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.3.0 // indirect
modernc.org/opt v0.1.1 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
)

1465
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -141,10 +141,26 @@ func parseAllWords(category string) *I18nData {
return &data
}
// copyI18nData creates a deep copy of an I18nData structure to prevent shared reference issues
// between language translations. This ensures each language starts with fresh English defaults
// rather than inheriting values from previously processed languages.
func copyI18nData(src *I18nData) *I18nData {
dst := I18nData{}
for namespace, pairs := range *src {
dst[namespace] = make(map[string]string)
for key, value := range pairs {
dst[namespace][key] = value
}
}
return &dst
}
func applyToOtherLanguage(category string, language string, newData *I18nData) {
oldData := readI18nFile(category, language)
println(oldData)
applyData(newData, oldData)
writeI18nFile(category, language, newData)
// Create a copy of newData to avoid modifying the shared data across languages
dataCopy := copyI18nData(newData)
applyData(dataCopy, oldData)
writeI18nFile(category, language, dataCopy)
}

View File

@@ -90,6 +90,7 @@ type CustomUserInfo struct {
DisplayName string `mapstructure:"displayName"`
Email string `mapstructure:"email"`
AvatarUrl string `mapstructure:"avatarUrl"`
Phone string `mapstructure:"phone"`
}
func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
@@ -153,6 +154,7 @@ func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
Username: customUserinfo.Username,
DisplayName: customUserinfo.DisplayName,
Email: customUserinfo.Email,
Phone: customUserinfo.Phone,
AvatarUrl: customUserinfo.AvatarUrl,
}
return userInfo, nil

View File

@@ -20,6 +20,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"math"
"net/http"
"sort"
"strconv"
@@ -65,7 +66,7 @@ func (idp *TelegramIdProvider) GetToken(code string) (*oauth2.Token, error) {
}
// Create a token with the user ID as access token
userId, ok := authData["id"].(float64)
userId, ok := telegramAsInt64(authData["id"])
if !ok {
return nil, fmt.Errorf("invalid user id in auth data")
}
@@ -77,7 +78,7 @@ func (idp *TelegramIdProvider) GetToken(code string) (*oauth2.Token, error) {
}
token := &oauth2.Token{
AccessToken: fmt.Sprintf("telegram_%d", int64(userId)),
AccessToken: fmt.Sprintf("telegram_%d", userId),
TokenType: "Bearer",
}
@@ -97,6 +98,7 @@ func (idp *TelegramIdProvider) verifyTelegramAuth(authData map[string]interface{
if !ok {
return fmt.Errorf("hash not found in auth data")
}
hash = strings.TrimSpace(hash)
// Prepare data check string
var dataCheckArr []string
@@ -104,13 +106,14 @@ func (idp *TelegramIdProvider) verifyTelegramAuth(authData map[string]interface{
if key == "hash" {
continue
}
dataCheckArr = append(dataCheckArr, fmt.Sprintf("%s=%v", key, value))
dataCheckArr = append(dataCheckArr, fmt.Sprintf("%s=%s", key, telegramAsString(value)))
}
sort.Strings(dataCheckArr)
dataCheckString := strings.Join(dataCheckArr, "\n")
// Calculate secret key
secretKey := sha256.Sum256([]byte(idp.ClientSecret))
clientSecret := strings.TrimSpace(idp.ClientSecret)
secretKey := sha256.Sum256([]byte(clientSecret))
// Calculate hash
h := hmac.New(sha256.New, secretKey[:])
@@ -139,7 +142,7 @@ func (idp *TelegramIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
}
// Extract user information from auth data
userId, ok := authData["id"].(float64)
userId, ok := telegramAsInt64(authData["id"])
if !ok {
return nil, fmt.Errorf("invalid user id in auth data")
}
@@ -155,11 +158,11 @@ func (idp *TelegramIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
displayName = username
}
if displayName == "" {
displayName = strconv.FormatInt(int64(userId), 10)
displayName = strconv.FormatInt(userId, 10)
}
userInfo := UserInfo{
Id: strconv.FormatInt(int64(userId), 10),
Id: strconv.FormatInt(userId, 10),
Username: username,
DisplayName: displayName,
AvatarUrl: photoUrl,
@@ -167,3 +170,38 @@ func (idp *TelegramIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
return &userInfo, nil
}
func telegramAsInt64(v interface{}) (int64, bool) {
switch t := v.(type) {
case float64:
if t != math.Trunc(t) {
return 0, false
}
if t > float64(math.MaxInt64) || t < float64(math.MinInt64) {
return 0, false
}
return int64(t), true
case string:
i, err := strconv.ParseInt(t, 10, 64)
if err != nil {
return 0, false
}
return i, true
default:
return 0, false
}
}
func telegramAsString(v interface{}) string {
switch t := v.(type) {
case string:
return t
case float64:
if t == math.Trunc(t) && t <= float64(math.MaxInt64) && t >= float64(math.MinInt64) {
return strconv.FormatInt(int64(t), 10)
}
return strconv.FormatFloat(t, 'g', -1, 64)
default:
return fmt.Sprint(v)
}
}

111
idv/aliyun.go Normal file
View File

@@ -0,0 +1,111 @@
// Copyright 2025 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 idv
import (
"fmt"
cloudauth "github.com/alibabacloud-go/cloudauth-20190307/v3/client"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
"github.com/alibabacloud-go/tea/tea"
)
const (
// DefaultAlibabaCloudEndpoint is the default endpoint for Alibaba Cloud ID verification service
DefaultAlibabaCloudEndpoint = "cloudauth.cn-shanghai.aliyuncs.com"
)
type AlibabaCloudIdvProvider struct {
ClientId string
ClientSecret string
Endpoint string
}
func NewAlibabaCloudIdvProvider(clientId string, clientSecret string, endpoint string) *AlibabaCloudIdvProvider {
return &AlibabaCloudIdvProvider{
ClientId: clientId,
ClientSecret: clientSecret,
Endpoint: endpoint,
}
}
func (provider *AlibabaCloudIdvProvider) VerifyIdentity(idCardType string, idCard string, realName string) (bool, error) {
if provider.ClientId == "" || provider.ClientSecret == "" {
return false, fmt.Errorf("Alibaba Cloud credentials not configured")
}
if idCard == "" || realName == "" {
return false, fmt.Errorf("ID card and real name are required")
}
// Default endpoint if not configured
endpoint := provider.Endpoint
if endpoint == "" {
endpoint = DefaultAlibabaCloudEndpoint
}
// Create client configuration
config := &openapi.Config{
AccessKeyId: tea.String(provider.ClientId),
AccessKeySecret: tea.String(provider.ClientSecret),
Endpoint: tea.String(endpoint),
}
// Create Alibaba Cloud Auth client
client, err := cloudauth.NewClient(config)
if err != nil {
return false, fmt.Errorf("failed to create Alibaba Cloud client: %v", err)
}
// Prepare verification request using Id2MetaVerify API
// This API verifies Chinese ID card number and real name
// Reference: https://help.aliyun.com/zh/id-verification/financial-grade-id-verification/server-side-integration-2
request := &cloudauth.Id2MetaVerifyRequest{
IdentifyNum: tea.String(idCard),
UserName: tea.String(realName),
ParamType: tea.String("normal"),
}
// Send verification request
response, err := client.Id2MetaVerify(request)
if err != nil {
return false, fmt.Errorf("failed to verify identity with Alibaba Cloud: %v", err)
}
// Check response
if response == nil || response.Body == nil {
return false, fmt.Errorf("empty response from Alibaba Cloud")
}
// Check if the API call was successful
if response.Body.Code == nil || *response.Body.Code != "200" {
message := "unknown error"
if response.Body.Message != nil {
message = *response.Body.Message
}
return false, fmt.Errorf("Alibaba Cloud API error: %s", message)
}
// Check verification result
// BizCode "1" means verification passed
if response.Body.ResultObject != nil && response.Body.ResultObject.BizCode != nil {
if *response.Body.ResultObject.BizCode == "1" {
return true, nil
}
return false, fmt.Errorf("identity verification failed: BizCode=%s", *response.Body.ResultObject.BizCode)
}
return false, fmt.Errorf("identity verification failed: missing result")
}

143
idv/jumio.go Normal file
View File

@@ -0,0 +1,143 @@
// Copyright 2025 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 idv
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
type JumioIdvProvider struct {
ClientId string
ClientSecret string
Endpoint string
}
type JumioInitiateRequest struct {
CustomerInternalReference string `json:"customerInternalReference"`
UserReference string `json:"userReference"`
WorkflowId string `json:"workflowId,omitempty"`
}
type JumioInitiateResponse struct {
TransactionReference string `json:"transactionReference"`
RedirectUrl string `json:"redirectUrl"`
}
type JumioVerificationData struct {
IdCard string `json:"idNumber"`
RealName string `json:"firstName"`
Type string `json:"type"`
}
func NewJumioIdvProvider(clientId string, clientSecret string, endpoint string) *JumioIdvProvider {
return &JumioIdvProvider{
ClientId: clientId,
ClientSecret: clientSecret,
Endpoint: endpoint,
}
}
func (provider *JumioIdvProvider) VerifyIdentity(idCardType string, idCard string, realName string) (bool, error) {
if provider.ClientId == "" || provider.ClientSecret == "" {
return false, fmt.Errorf("Jumio credentials not configured")
}
if provider.Endpoint == "" {
return false, fmt.Errorf("Jumio endpoint not configured")
}
if idCard == "" || realName == "" {
return false, fmt.Errorf("ID card and real name are required")
}
// Jumio ID Verification implementation
// This implementation follows Jumio's API workflow:
// 1. Initiate a verification session
// 2. User would normally go through verification flow (redirected to Jumio)
// 3. Check verification status
// For automated verification, we simulate the process
client := &http.Client{
Timeout: 30 * time.Second,
}
// Prepare the initiation request
initiateReq := JumioInitiateRequest{
CustomerInternalReference: fmt.Sprintf("user_%s", idCard),
UserReference: realName,
}
reqBody, err := json.Marshal(initiateReq)
if err != nil {
return false, fmt.Errorf("failed to marshal request: %v", err)
}
// Create HTTP request to Jumio API
req, err := http.NewRequest("POST", fmt.Sprintf("%s/api/v4/initiate", provider.Endpoint), bytes.NewBuffer(reqBody))
if err != nil {
return false, fmt.Errorf("failed to create request: %v", err)
}
// Set authentication headers
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", "Casdoor/1.0")
req.SetBasicAuth(provider.ClientId, provider.ClientSecret)
// Send request
resp, err := client.Do(req)
if err != nil {
return false, fmt.Errorf("failed to send request to Jumio: %v", err)
}
defer resp.Body.Close()
// Read response
body, err := io.ReadAll(resp.Body)
if err != nil {
return false, fmt.Errorf("failed to read response: %v", err)
}
// Check response status
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
return false, fmt.Errorf("Jumio API returned error status %d: %s", resp.StatusCode, string(body))
}
// Parse response
var initiateResp JumioInitiateResponse
if err := json.Unmarshal(body, &initiateResp); err != nil {
return false, fmt.Errorf("failed to parse Jumio response: %v", err)
}
// In a real implementation, the user would be redirected to initiateResp.RedirectUrl
// to complete the verification process. Here we simulate successful verification.
// For production, you would need to:
// 1. Store the transaction reference
// 2. Redirect user to RedirectUrl or provide it to them
// 3. Implement a webhook to receive verification results
// 4. Query the transaction status using the transaction reference
// Simulate verification check (in production, this would be a webhook callback or status query)
if initiateResp.TransactionReference != "" {
// Successfully initiated verification session
// In a real scenario, return would depend on actual verification completion
return true, nil
}
return false, fmt.Errorf("verification could not be initiated")
}

29
idv/provider.go Normal file
View File

@@ -0,0 +1,29 @@
// Copyright 2025 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 idv
type IdvProvider interface {
VerifyIdentity(idCardType string, idCard string, realName string) (bool, error)
}
func GetIdvProvider(typ string, clientId string, clientSecret string, endpoint string) IdvProvider {
if typ == "Jumio" {
return NewJumioIdvProvider(clientId, clientSecret, endpoint)
} else if typ == "Alibaba Cloud" {
return NewAlibabaCloudIdvProvider(clientId, clientSecret, endpoint)
}
// Default to Jumio for backward compatibility
return NewJumioIdvProvider(clientId, clientSecret, endpoint)
}

View File

@@ -6,7 +6,7 @@
"displayName": "",
"websiteUrl": "",
"favicon": "",
"passwordType": "plain",
"passwordType": "bcrypt",
"passwordSalt": "",
"passwordOptions": [
"AtLeast6"
@@ -76,7 +76,44 @@
"enableSoftDeletion": false,
"isProfilePublic": true,
"disableSignin": false,
"accountItems": []
"accountItems": [
{"name": "Organization", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
{"name": "ID", "visible": true, "viewRule": "Public", "modifyRule": "Immutable"},
{"name": "Name", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
{"name": "Display name", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Avatar", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "User type", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
{"name": "Password", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
{"name": "Email", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Phone", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Country code", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
{"name": "Country/Region", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Location", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Affiliation", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Title", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "ID card type", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "ID card", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Real name", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "ID verification", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
{"name": "Homepage", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Bio", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
{"name": "Tag", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
{"name": "Signup application", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
{"name": "Register type", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
{"name": "Register source", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
{"name": "Roles", "visible": true, "viewRule": "Public", "modifyRule": "Immutable"},
{"name": "Permissions", "visible": true, "viewRule": "Public", "modifyRule": "Immutable"},
{"name": "Groups", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
{"name": "3rd-party logins", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
{"name": "Properties", "visible": true, "viewRule": "Admin", "modifyRule": "Admin"},
{"name": "Is admin", "visible": true, "viewRule": "Admin", "modifyRule": "Admin"},
{"name": "Is forbidden", "visible": true, "viewRule": "Admin", "modifyRule": "Admin"},
{"name": "Is deleted", "visible": true, "viewRule": "Admin", "modifyRule": "Admin"},
{"name": "Multi-factor authentication", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
{"name": "WebAuthn credentials", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
{"name": "Managed accounts", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
{"name": "MFA accounts", "visible": true, "viewRule": "Self", "modifyRule": "Self"}
]
}
],
"applications": [

View File

@@ -281,7 +281,7 @@ func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int)
}
return filteredUsers, ldap.LDAPResultSuccess
}
if m.Client.IsGlobalAdmin || org == m.Client.OrgName {
if m.Client.IsGlobalAdmin || (m.Client.IsOrgAdmin && org == m.Client.OrgName) {
filteredUsers, err = object.GetUsersWithFilter(org, buildSafeCondition(r.Filter()))
if err != nil {
panic(err)
@@ -349,7 +349,7 @@ func GetFilteredGroups(m *ldap.Message, baseDN string, filterStr string) ([]*obj
if err != nil {
panic(err)
}
} else if m.Client.IsGlobalAdmin || org == m.Client.OrgName {
} else if m.Client.IsGlobalAdmin || (m.Client.IsOrgAdmin && org == m.Client.OrgName) {
groups, err = object.GetGroups(org)
if err != nil {
panic(err)

65
main.go
View File

@@ -18,9 +18,9 @@ import (
"encoding/json"
"fmt"
"github.com/beego/beego"
"github.com/beego/beego/logs"
_ "github.com/beego/beego/session/redis"
"github.com/beego/beego/v2/core/logs"
"github.com/beego/beego/v2/server/web"
_ "github.com/beego/beego/v2/server/web/session/redis"
"github.com/casdoor/casdoor/authz"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/controllers"
@@ -33,6 +33,20 @@ import (
)
func main() {
web.BConfig.WebConfig.Session.SessionOn = true
web.BConfig.WebConfig.Session.SessionName = "casdoor_session_id"
if conf.GetConfigString("redisEndpoint") == "" {
web.BConfig.WebConfig.Session.SessionProvider = "file"
web.BConfig.WebConfig.Session.SessionProviderConfig = "./tmp"
} else {
web.BConfig.WebConfig.Session.SessionProvider = "redis"
web.BConfig.WebConfig.Session.SessionProviderConfig = conf.GetConfigString("redisEndpoint")
}
web.BConfig.WebConfig.Session.SessionCookieLifeTime = 3600 * 24 * 30
web.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600 * 24 * 30
// web.BConfig.WebConfig.Session.SessionCookieSameSite = http.SameSiteNoneMode
routers.InitAPI()
object.InitFlag()
object.InitAdapter()
object.CreateTables()
@@ -62,35 +76,22 @@ func main() {
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
util.SafeGoroutine(func() { controllers.InitCLIDownloader() })
// beego.DelStaticPath("/static")
// beego.SetStaticPath("/static", "web/build/static")
// web.DelStaticPath("/static")
// web.SetStaticPath("/static", "web/build/static")
beego.BConfig.WebConfig.DirectoryIndex = true
beego.SetStaticPath("/swagger", "swagger")
beego.SetStaticPath("/files", "files")
web.BConfig.WebConfig.DirectoryIndex = true
web.SetStaticPath("/swagger", "swagger")
web.SetStaticPath("/files", "files")
// https://studygolang.com/articles/2303
beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.TimeoutFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
beego.InsertFilter("*", beego.BeforeRouter, routers.FieldValidationFilter)
beego.InsertFilter("*", beego.AfterExec, routers.AfterRecordMessage, false)
beego.BConfig.WebConfig.Session.SessionOn = true
beego.BConfig.WebConfig.Session.SessionName = "casdoor_session_id"
if conf.GetConfigString("redisEndpoint") == "" {
beego.BConfig.WebConfig.Session.SessionProvider = "file"
beego.BConfig.WebConfig.Session.SessionProviderConfig = "./tmp"
} else {
beego.BConfig.WebConfig.Session.SessionProvider = "redis"
beego.BConfig.WebConfig.Session.SessionProviderConfig = conf.GetConfigString("redisEndpoint")
}
beego.BConfig.WebConfig.Session.SessionCookieLifeTime = 3600 * 24 * 30
beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600 * 24 * 30
// beego.BConfig.WebConfig.Session.SessionCookieSameSite = http.SameSiteNoneMode
web.InsertFilter("*", web.BeforeRouter, routers.StaticFilter)
web.InsertFilter("*", web.BeforeRouter, routers.AutoSigninFilter)
web.InsertFilter("*", web.BeforeRouter, routers.CorsFilter)
web.InsertFilter("*", web.BeforeRouter, routers.TimeoutFilter)
web.InsertFilter("*", web.BeforeRouter, routers.ApiFilter)
web.InsertFilter("*", web.BeforeRouter, routers.PrometheusFilter)
web.InsertFilter("*", web.BeforeRouter, routers.RecordMessage)
web.InsertFilter("*", web.BeforeRouter, routers.FieldValidationFilter)
web.InsertFilter("*", web.AfterExec, routers.AfterRecordMessage, web.WithReturnOnOutput(false))
var logAdapter string
logConfigMap := make(map[string]interface{})
@@ -112,7 +113,7 @@ func main() {
panic(err)
}
port := beego.AppConfig.DefaultInt("httpport", 8000)
port := web.AppConfig.DefaultInt("httpport", 8000)
// logs.SetLevel(logs.LevelInformational)
logs.SetLogFuncCall(false)
@@ -125,5 +126,5 @@ func main() {
go radius.StartRadiusServer()
go object.ClearThroughputPerSecond()
beego.Run(fmt.Sprintf(":%v", port))
web.Run(fmt.Sprintf(":%v", port))
}

117
mcp/application.go Normal file
View File

@@ -0,0 +1,117 @@
// Copyright 2026 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 mcp
import (
"fmt"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// handleGetApplicationsTool handles the get_applications MCP tool
func (c *McpController) handleGetApplicationsTool(id interface{}, args GetApplicationsArgs) {
userId := c.GetSessionUsername()
applications, err := object.GetApplications(args.Owner)
if err != nil {
c.SendToolErrorResult(id, err.Error())
return
}
maskedApps := object.GetMaskedApplications(applications, userId)
c.SendToolResult(id, util.StructToJsonFormatted(maskedApps))
}
// handleGetApplicationTool handles the get_application MCP tool
func (c *McpController) handleGetApplicationTool(id interface{}, args GetApplicationArgs) {
userId := c.GetSessionUsername()
application, err := object.GetApplication(args.Id)
if err != nil {
c.SendToolErrorResult(id, err.Error())
return
}
maskedApp := object.GetMaskedApplication(application, userId)
c.SendToolResult(id, util.StructToJsonFormatted(maskedApp))
}
// handleAddApplicationTool handles the add_application MCP tool
func (c *McpController) handleAddApplicationTool(id interface{}, args AddApplicationArgs) {
count, err := object.GetApplicationCount("", "", "")
if err != nil {
c.SendToolErrorResult(id, err.Error())
return
}
if err := checkQuotaForApplication(int(count)); err != nil {
c.SendToolErrorResult(id, err.Error())
return
}
if err = object.CheckIpWhitelist(args.Application.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.SendToolErrorResult(id, err.Error())
return
}
affected, err := object.AddApplication(&args.Application)
if err != nil {
c.SendToolErrorResult(id, err.Error())
return
}
c.SendToolResult(id, FormatOperationResult("add", "application", affected))
}
// handleUpdateApplicationTool handles the update_application MCP tool
func (c *McpController) handleUpdateApplicationTool(id interface{}, args UpdateApplicationArgs) {
if err := object.CheckIpWhitelist(args.Application.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.SendToolErrorResult(id, err.Error())
return
}
affected, err := object.UpdateApplication(args.Id, &args.Application, c.IsGlobalAdmin(), c.GetAcceptLanguage())
if err != nil {
c.SendToolErrorResult(id, err.Error())
return
}
c.SendToolResult(id, FormatOperationResult("update", "application", affected))
}
// handleDeleteApplicationTool handles the delete_application MCP tool
func (c *McpController) handleDeleteApplicationTool(id interface{}, args DeleteApplicationArgs) {
affected, err := object.DeleteApplication(&args.Application)
if err != nil {
c.SendToolErrorResult(id, err.Error())
return
}
c.SendToolResult(id, FormatOperationResult("delete", "application", affected))
}
// checkQuotaForApplication checks if the application quota is exceeded
func checkQuotaForApplication(count int) error {
quota := conf.GetConfigQuota().Application
if quota == -1 {
return nil
}
if count >= quota {
return fmt.Errorf("application quota is exceeded")
}
return nil
}

115
mcp/auth.go Normal file
View File

@@ -0,0 +1,115 @@
// Copyright 2026 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 mcp
import (
"time"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// SessionData represents session metadata
type SessionData struct {
ExpireTime int64
}
// GetSessionUsername returns the username from session
func (c *McpController) GetSessionUsername() string {
// check if user session expired
sessionData := c.GetSessionData()
if sessionData != nil &&
sessionData.ExpireTime != 0 &&
sessionData.ExpireTime < time.Now().Unix() {
c.ClearUserSession()
return ""
}
user := c.GetSession("username")
if user == nil {
return ""
}
return user.(string)
}
// GetSessionData retrieves session data
func (c *McpController) GetSessionData() *SessionData {
session := c.GetSession("SessionData")
if session == nil {
return nil
}
sessionData := &SessionData{}
err := util.JsonToStruct(session.(string), sessionData)
if err != nil {
return nil
}
return sessionData
}
// ClearUserSession clears the user session
func (c *McpController) ClearUserSession() {
c.SetSession("username", "")
c.DelSession("SessionData")
_ = c.SessionRegenerateID()
}
// IsGlobalAdmin checks if the current user is a global admin
func (c *McpController) IsGlobalAdmin() bool {
isGlobalAdmin, _ := c.isGlobalAdmin()
return isGlobalAdmin
}
func (c *McpController) isGlobalAdmin() (bool, *object.User) {
username := c.GetSessionUsername()
if object.IsAppUser(username) {
// e.g., "app/app-casnode"
return true, nil
}
user := c.getCurrentUser()
if user == nil {
return false, nil
}
return user.IsGlobalAdmin(), user
}
func (c *McpController) getCurrentUser() *object.User {
var user *object.User
var err error
userId := c.GetSessionUsername()
if userId == "" {
user = nil
} else {
user, err = object.GetUser(userId)
if err != nil {
return nil
}
}
return user
}
// GetAcceptLanguage returns the Accept-Language header value
func (c *McpController) GetAcceptLanguage() string {
language := c.Ctx.Request.Header.Get("Accept-Language")
if len(language) > 2 {
language = language[0:2]
}
return language
}

407
mcp/base.go Normal file
View File

@@ -0,0 +1,407 @@
// Copyright 2026 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 mcp
import (
"encoding/json"
"fmt"
"github.com/beego/beego/v2/server/web"
"github.com/casdoor/casdoor/object"
)
// MCP JSON-RPC 2.0 structures
type McpRequest struct {
JSONRPC string `json:"jsonrpc"`
ID interface{} `json:"id"`
Method string `json:"method"`
Params json.RawMessage `json:"params,omitempty"`
}
type McpResponse struct {
JSONRPC string `json:"jsonrpc"`
ID interface{} `json:"id"`
Result interface{} `json:"result,omitempty"`
Error *McpError `json:"error,omitempty"`
}
type McpError struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
type McpInitializeParams struct {
ProtocolVersion string `json:"protocolVersion"`
Capabilities map[string]interface{} `json:"capabilities"`
ClientInfo McpImplementation `json:"clientInfo"`
}
type McpImplementation struct {
Name string `json:"name"`
Version string `json:"version"`
}
type McpInitializeResult struct {
ProtocolVersion string `json:"protocolVersion"`
Capabilities McpServerCapabilities `json:"capabilities"`
ServerInfo McpImplementation `json:"serverInfo"`
}
type McpServerCapabilities struct {
Tools map[string]interface{} `json:"tools,omitempty"`
}
type McpListToolsResult struct {
Tools []McpTool `json:"tools"`
}
type McpTool struct {
Name string `json:"name"`
Description string `json:"description"`
InputSchema map[string]interface{} `json:"inputSchema"`
}
type McpCallToolParams struct {
Name string `json:"name"`
Arguments json.RawMessage `json:"arguments,omitempty"`
}
// Tool-specific argument structs
type GetApplicationsArgs struct {
Owner string `json:"owner"`
}
type GetApplicationArgs struct {
Id string `json:"id"`
}
type AddApplicationArgs struct {
Application object.Application `json:"application"`
}
type UpdateApplicationArgs struct {
Id string `json:"id"`
Application object.Application `json:"application"`
}
type DeleteApplicationArgs struct {
Application object.Application `json:"application"`
}
type McpCallToolResult struct {
Content []TextContent `json:"content"`
IsError bool `json:"isError,omitempty"`
}
type TextContent struct {
Type string `json:"type"`
Text string `json:"text"`
}
// MCPController handles MCP protocol requests
type McpController struct {
web.Controller
}
func (c *McpController) Prepare() {
c.EnableRender = false
}
// SendMcpResponse sends a successful MCP response
func (c *McpController) SendMcpResponse(id interface{}, result interface{}) {
resp := GetMcpResponse(id, result, nil)
// Set proper HTTP headers for MCP responses
c.Ctx.Output.Header("Content-Type", "application/json")
c.Data["json"] = resp
c.ServeJSON()
}
// SendMcpError sends an MCP error response
func (c *McpController) SendMcpError(id interface{}, code int, message string, data interface{}) {
resp := GetMcpResponse(id, nil, &McpError{
Code: code,
Message: message,
Data: data,
})
// Set proper HTTP headers for MCP responses
c.Ctx.Output.Header("Content-Type", "application/json")
c.Data["json"] = resp
c.ServeJSON()
}
// GetMcpResponse returns a McpResponse object
func GetMcpResponse(id interface{}, result interface{}, err *McpError) McpResponse {
resp := McpResponse{
JSONRPC: "2.0",
ID: id,
Result: result,
Error: err,
}
return resp
}
// sendInvalidParamsError sends an invalid params error
func (c *McpController) sendInvalidParamsError(id interface{}, details string) {
c.SendMcpError(id, -32602, "Invalid params", details)
}
// SendToolResult sends a successful tool execution result
func (c *McpController) SendToolResult(id interface{}, text string) {
result := McpCallToolResult{
Content: []TextContent{
{
Type: "text",
Text: text,
},
},
}
c.SendMcpResponse(id, result)
}
// SendToolErrorResult sends a tool execution error result
func (c *McpController) SendToolErrorResult(id interface{}, errorMsg string) {
result := McpCallToolResult{
Content: []TextContent{
{
Type: "text",
Text: errorMsg,
},
},
IsError: true,
}
c.SendMcpResponse(id, result)
}
// FormatOperationResult formats the result of CRUD operations in a clear, descriptive way
func FormatOperationResult(operation, resourceType string, affected bool) string {
if affected {
// Map operation to past tense
pastTense := operation + "d"
if operation == "add" {
pastTense = "added"
} else if operation == "update" {
pastTense = "updated"
} else if operation == "delete" {
pastTense = "deleted"
}
return fmt.Sprintf("Successfully %s %s", pastTense, resourceType)
}
return fmt.Sprintf("No changes were made to %s (may already exist or not found)", resourceType)
}
// HandleMcp handles MCP protocol requests
// @Title HandleMcp
// @Tag MCP API
// @Description handle MCP (Model Context Protocol) requests
// @Success 200 {object} McpResponse The Response object
// @router /mcp [post]
func (c *McpController) HandleMcp() {
var req McpRequest
err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
if err != nil {
c.SendMcpError(nil, -32700, "Parse error", err.Error())
return
}
// Handle different MCP methods
switch req.Method {
case "initialize":
c.handleInitialize(req)
case "notifications/initialized":
c.handleNotificationsInitialized(req)
case "ping":
c.handlePing(req)
case "tools/list":
c.handleToolsList(req)
case "tools/call":
c.handleToolsCall(req)
default:
c.SendMcpError(req.ID, -32601, "Method not found", fmt.Sprintf("Method '%s' not found", req.Method))
}
}
func (c *McpController) handleInitialize(req McpRequest) {
var params McpInitializeParams
if req.Params != nil {
err := json.Unmarshal(req.Params, &params)
if err != nil {
c.sendInvalidParamsError(req.ID, err.Error())
return
}
}
result := McpInitializeResult{
ProtocolVersion: "2024-11-05",
Capabilities: McpServerCapabilities{
Tools: map[string]interface{}{
"listChanged": true,
},
},
ServerInfo: McpImplementation{
Name: "Casdoor MCP Server",
Version: "1.0.0",
},
}
c.SendMcpResponse(req.ID, result)
}
func (c *McpController) handleNotificationsInitialized(req McpRequest) {
c.Ctx.Output.SetStatus(202)
}
func (c *McpController) handlePing(req McpRequest) {
// ping method is used to check if the server is alive and responsive
// Return an empty object as result to indicate server is active
c.SendMcpResponse(req.ID, map[string]interface{}{})
}
func (c *McpController) handleToolsList(req McpRequest) {
tools := []McpTool{
{
Name: "get_applications",
Description: "Get all applications for a specific owner",
InputSchema: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"owner": map[string]interface{}{
"type": "string",
"description": "The owner of applications",
},
},
"required": []string{"owner"},
},
},
{
Name: "get_application",
Description: "Get the detail of a specific application",
InputSchema: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]interface{}{
"type": "string",
"description": "The id (owner/name) of the application",
},
},
"required": []string{"id"},
},
},
{
Name: "add_application",
Description: "Add a new application",
InputSchema: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"application": map[string]interface{}{
"type": "object",
"description": "The application object to add",
},
},
"required": []string{"application"},
},
},
{
Name: "update_application",
Description: "Update an existing application",
InputSchema: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"id": map[string]interface{}{
"type": "string",
"description": "The id (owner/name) of the application",
},
"application": map[string]interface{}{
"type": "object",
"description": "The updated application object",
},
},
"required": []string{"id", "application"},
},
},
{
Name: "delete_application",
Description: "Delete an application",
InputSchema: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"application": map[string]interface{}{
"type": "object",
"description": "The application object to delete",
},
},
"required": []string{"application"},
},
},
}
result := McpListToolsResult{
Tools: tools,
}
c.SendMcpResponse(req.ID, result)
}
func (c *McpController) handleToolsCall(req McpRequest) {
var params McpCallToolParams
err := json.Unmarshal(req.Params, &params)
if err != nil {
c.sendInvalidParamsError(req.ID, err.Error())
return
}
// Route to the appropriate tool handler
switch params.Name {
case "get_applications":
var args GetApplicationsArgs
if err := json.Unmarshal(params.Arguments, &args); err != nil {
c.sendInvalidParamsError(req.ID, err.Error())
return
}
c.handleGetApplicationsTool(req.ID, args)
case "get_application":
var args GetApplicationArgs
if err := json.Unmarshal(params.Arguments, &args); err != nil {
c.sendInvalidParamsError(req.ID, err.Error())
return
}
c.handleGetApplicationTool(req.ID, args)
case "add_application":
var args AddApplicationArgs
if err := json.Unmarshal(params.Arguments, &args); err != nil {
c.sendInvalidParamsError(req.ID, err.Error())
return
}
c.handleAddApplicationTool(req.ID, args)
case "update_application":
var args UpdateApplicationArgs
if err := json.Unmarshal(params.Arguments, &args); err != nil {
c.sendInvalidParamsError(req.ID, err.Error())
return
}
c.handleUpdateApplicationTool(req.ID, args)
case "delete_application":
var args DeleteApplicationArgs
if err := json.Unmarshal(params.Arguments, &args); err != nil {
c.sendInvalidParamsError(req.ID, err.Error())
return
}
c.handleDeleteApplicationTool(req.ID, args)
default:
c.SendMcpError(req.ID, -32602, "Invalid tool name", fmt.Sprintf("Tool '%s' not found", params.Name))
}
}

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/bark"
"github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/bark"
)
func NewBarkProvider(deviceKey string) (notify.Notifier, error) {

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/cucloud"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/cucloud"
)
func NewCucloudProvider(accessKey, secretKey, topicName, messageTitle, cloudRegionCode, accountId, notifyType string) (notify.Notifier, error) {

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/dingding"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/dingding"
)
func NewDingTalkProvider(token string, secret string) (notify.Notifier, error) {

View File

@@ -16,8 +16,8 @@ package notification
import (
"github.com/casdoor/casdoor/proxy"
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/discord"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/discord"
)
func NewDiscordProvider(token string, channelId string) (*notify.Notify, error) {

View File

@@ -18,8 +18,8 @@ import (
"context"
"strings"
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/googlechat"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/googlechat"
"google.golang.org/api/chat/v1"
"google.golang.org/api/option"
)

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/lark"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/lark"
)
func NewLarkProvider(webhookURL string) (notify.Notifier, error) {

View File

@@ -16,8 +16,8 @@ package notification
import (
"github.com/casdoor/casdoor/proxy"
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/line"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/line"
)
func NewLineProvider(channelSecret string, accessToken string, receiver string) (*notify.Notify, error) {

View File

@@ -16,8 +16,8 @@ package notification
import (
"github.com/casdoor/casdoor/proxy"
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/matrix"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/matrix"
"maunium.net/go/mautrix/id"
)

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/msteams"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/msteams"
)
func NewMicrosoftTeamsProvider(webhookURL string) (notify.Notifier, error) {

View File

@@ -14,7 +14,7 @@
package notification
import "github.com/casdoor/notify"
import notify "github.com/casdoor/notify2"
func GetNotificationProvider(typ string, clientId string, clientSecret string, clientId2 string, clientSecret2 string, appId string, receiver string, method string, title string, metaData string, regionId string) (notify.Notifier, error) {
if typ == "Telegram" {

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/pushbullet"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/pushbullet"
)
func NewPushbulletProvider(apiToken string, deviceNickname string) (notify.Notifier, error) {

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/pushover"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/pushover"
)
func NewPushoverProvider(appToken string, recipientID string) (notify.Notifier, error) {

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/reddit"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/reddit"
)
func NewRedditProvider(clientId string, clientSecret string, username string, password string, recipient string) (notify.Notifier, error) {

View File

@@ -18,8 +18,8 @@ import (
"fmt"
"strings"
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/rocketchat"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/rocketchat"
)
func NewRocketChatProvider(clientId string, clientSecret string, endpoint string, channelName string) (notify.Notifier, error) {

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/slack"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/slack"
)
func NewSlackProvider(apiToken string, channelID string) (*notify.Notify, error) {

View File

@@ -18,8 +18,8 @@ import (
"strconv"
"github.com/casdoor/casdoor/proxy"
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/telegram"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/telegram"
api "github.com/go-telegram-bot-api/telegram-bot-api"
)

View File

@@ -16,8 +16,8 @@ package notification
import (
"github.com/casdoor/casdoor/proxy"
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/twitter"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/twitter"
)
func NewTwitterProvider(consumerKey string, consumerSecret string, accessToken string, accessTokenSecret string, twitterId string) (*notify.Notify, error) {

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/viber"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/viber"
)
func NewViberProvider(senderName string, appKey string, webhookURL string, receiverId string) (notify.Notifier, error) {

View File

@@ -15,8 +15,8 @@
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/webpush"
notify "github.com/casdoor/notify2"
"github.com/casdoor/notify2/service/webpush"
)
func NewWebpushProvider(publicKey string, privateKey string, endpoint string) (*notify.Notify, error) {

View File

@@ -22,7 +22,7 @@ import (
"net/http"
"time"
"github.com/casdoor/notify"
notify "github.com/casdoor/notify2"
)
// wecomService encapsulates the WeCom webhook client

View File

@@ -121,6 +121,7 @@ type Application struct {
TokenAttributes []*JwtItem `xorm:"mediumtext" json:"tokenAttributes"`
ExpireInHours float64 `json:"expireInHours"`
RefreshExpireInHours float64 `json:"refreshExpireInHours"`
CookieExpireInHours int64 `json:"cookieExpireInHours"`
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
@@ -365,6 +366,22 @@ func extendApplicationWithSigninMethods(application *Application) (err error) {
return
}
func extendApplicationWithSignupItems(application *Application) (err error) {
if len(application.SignupItems) == 0 {
application.SignupItems = []*SignupItem{
{Name: "ID", Visible: false, Required: true, Prompted: false, Rule: "Random"},
{Name: "Username", Visible: true, Required: true, Prompted: false, Rule: "None"},
{Name: "Display name", Visible: true, Required: true, Prompted: false, Rule: "None"},
{Name: "Password", Visible: true, Required: true, Prompted: false, Rule: "None"},
{Name: "Confirm password", Visible: true, Required: true, Prompted: false, Rule: "None"},
{Name: "Email", Visible: true, Required: true, Prompted: false, Rule: "None"},
{Name: "Phone", Visible: true, Required: true, Prompted: false, Rule: "None"},
{Name: "Agreement", Visible: true, Required: true, Prompted: false, Rule: "None"},
}
}
return
}
func getApplication(owner string, name string) (*Application, error) {
if owner == "" || name == "" {
return nil, nil
@@ -728,6 +745,22 @@ func AddApplication(application *Application) (bool, error) {
return false, nil
}
// Initialize default values for required fields to prevent UI errors
err = extendApplicationWithSignupItems(application)
if err != nil {
return false, err
}
err = extendApplicationWithSigninItems(application)
if err != nil {
return false, err
}
err = extendApplicationWithSigninMethods(application)
if err != nil {
return false, err
}
for _, providerItem := range application.Providers {
providerItem.Provider = nil
}
@@ -771,6 +804,9 @@ func (application *Application) IsRedirectUriValid(redirectUri string) bool {
}
for _, targetUri := range application.RedirectUris {
if targetUri == "" {
continue
}
targetUriRegex := regexp.MustCompile(targetUri)
if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) {
return true

View File

@@ -119,9 +119,9 @@ func CheckUserSignup(application *Application, organization *Organization, authF
if authForm.Name == "" {
return i18n.Translate(lang, "check:DisplayName cannot be blank")
} else if application.GetSignupItemRule("Display name") == "Real name" {
if !isValidRealName(authForm.Name) {
return i18n.Translate(lang, "check:DisplayName is not valid real name")
}
// if !isValidRealName(authForm.Name) {
// return i18n.Translate(lang, "check:DisplayName is not valid real name")
// }
}
}
}
@@ -292,7 +292,20 @@ func CheckPasswordComplexity(user *User, password string, lang string) string {
return CheckPasswordComplexityByOrg(organization, password, lang)
}
func CheckLdapUserPassword(user *User, password string, lang string) error {
func CheckLdapUserPassword(user *User, password string, lang string, options ...bool) error {
enableCaptcha := false
if len(options) > 0 {
enableCaptcha = options[0]
}
// check the login error times
if !enableCaptcha {
err := checkSigninErrorTimes(user, lang)
if err != nil {
return err
}
}
ldaps, err := GetLdaps(user.Owner)
if err != nil {
return err
@@ -340,7 +353,7 @@ func CheckLdapUserPassword(user *User, password string, lang string) error {
if !hit {
return fmt.Errorf("user not exist")
}
return fmt.Errorf(i18n.Translate(lang, "check:LDAP user name or password incorrect"))
return recordSigninErrorInfo(user, lang, enableCaptcha)
}
return resetUserSigninErrorTimes(user)
}
@@ -383,22 +396,14 @@ func CheckUserPassword(organization string, username string, password string, la
return nil, fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect"))
}
// check the login error times
if !enableCaptcha {
err = checkSigninErrorTimes(user, lang)
if err != nil {
return nil, err
}
}
// only for LDAP users
err = CheckLdapUserPassword(user, password, lang)
err = CheckLdapUserPassword(user, password, lang, enableCaptcha)
if err != nil {
if err.Error() == "user not exist" {
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
}
return nil, recordSigninErrorInfo(user, lang, enableCaptcha)
return nil, err
}
} else {
err = CheckPassword(user, password, lang, enableCaptcha)

View File

@@ -63,6 +63,10 @@ func getBuiltInAccountItems() []*AccountItem {
{Name: "Location", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Affiliation", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Title", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "ID card type", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "ID card", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Real name", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "ID verification", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Homepage", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
@@ -101,7 +105,7 @@ func initBuiltInOrganization() bool {
DisplayName: "Built-in Organization",
WebsiteUrl: "https://example.com",
Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", conf.GetConfigString("staticBaseUrl")),
PasswordType: "plain",
PasswordType: "bcrypt",
PasswordOptions: []string{"AtLeast6"},
CountryCodes: []string{"US", "ES", "FR", "DE", "GB", "CN", "JP", "KR", "VN", "ID", "SG", "IN"},
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
@@ -210,6 +214,8 @@ func initBuiltInApplication() {
TokenFields: []string{},
ExpireInHours: 168,
FormOffset: 2,
CookieExpireInHours: 720,
}
_, err = AddApplication(application)
if err != nil {

View File

@@ -5,7 +5,7 @@ import (
"sync"
"time"
"github.com/beego/beego/logs"
"github.com/beego/beego/v2/core/logs"
"github.com/casdoor/casdoor/util"
)

View File

@@ -32,7 +32,7 @@ type MfaProps struct {
}
type MfaInterface interface {
Initiate(userId string) (*MfaProps, error)
Initiate(userId string, issuer string) (*MfaProps, error)
SetupVerify(passcode string) error
Enable(user *User) error
Verify(passcode string) error

View File

@@ -31,7 +31,7 @@ type PushMfa struct {
challengeExp time.Time
}
func (mfa *PushMfa) Initiate(userId string) (*MfaProps, error) {
func (mfa *PushMfa) Initiate(userId string, issuer string) (*MfaProps, error) {
mfaProps := MfaProps{
MfaType: mfa.MfaType,
}

View File

@@ -29,7 +29,7 @@ type RadiusMfa struct {
provider *Provider
}
func (mfa *RadiusMfa) Initiate(userId string) (*MfaProps, error) {
func (mfa *RadiusMfa) Initiate(userId string, issuer string) (*MfaProps, error) {
mfaProps := MfaProps{
MfaType: mfa.MfaType,
}

View File

@@ -35,7 +35,7 @@ func TestRadiusMfaUtil(t *testing.T) {
}
// Test Initiate
mfaProps, err := radiusMfa.Initiate("test/user")
mfaProps, err := radiusMfa.Initiate("test/user", "")
if err != nil {
t.Errorf("Initiate failed: %v", err)
}

View File

@@ -24,7 +24,7 @@ type SmsMfa struct {
*MfaProps
}
func (mfa *SmsMfa) Initiate(userId string) (*MfaProps, error) {
func (mfa *SmsMfa) Initiate(userId string, issuer string) (*MfaProps, error) {
mfaProps := MfaProps{
MfaType: mfa.MfaType,
}

View File

@@ -33,12 +33,11 @@ type TotpMfa struct {
digits otp.Digits
}
func (mfa *TotpMfa) Initiate(userId string) (*MfaProps, error) {
//issuer := beego.AppConfig.String("appname")
//if issuer == "" {
// issuer = "casdoor"
//}
issuer := "Casdoor"
func (mfa *TotpMfa) Initiate(userId string, issuer string) (*MfaProps, error) {
// Use the provided issuer (application display name), or fall back to "Casdoor" if not provided
if issuer == "" {
issuer = "Casdoor"
}
key, err := totp.Generate(totp.GenerateOpts{
Issuer: issuer,

115
object/mfa_totp_test.go Normal file
View File

@@ -0,0 +1,115 @@
// Copyright 2025 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"strings"
"testing"
)
func TestTotpMfaUtil(t *testing.T) {
// Test creating a new TOTP MFA util
config := &MfaProps{
MfaType: TotpType,
}
totpMfa := NewTotpMfaUtil(config)
if totpMfa == nil {
t.Error("NewTotpMfaUtil returned nil")
return
}
if totpMfa.MfaType != TotpType {
t.Errorf("Expected MFA type %s, got %s", TotpType, totpMfa.MfaType)
}
}
func TestTotpMfaInitiate_WithCustomIssuer(t *testing.T) {
totpMfa := NewTotpMfaUtil(nil)
// Test with custom issuer (application display name)
customIssuer := "My Application"
mfaProps, err := totpMfa.Initiate("test/user", customIssuer)
if err != nil {
t.Errorf("Initiate failed: %v", err)
}
if mfaProps == nil {
t.Error("Initiate returned nil mfaProps")
return
}
if mfaProps.MfaType != TotpType {
t.Errorf("Expected MFA type %s, got %s", TotpType, mfaProps.MfaType)
}
if mfaProps.Secret == "" {
t.Error("Secret should not be empty")
}
if mfaProps.URL == "" {
t.Error("URL should not be empty")
}
// Verify the URL contains the custom issuer (URL-encoded or plain)
if !strings.Contains(mfaProps.URL, customIssuer) && !strings.Contains(mfaProps.URL, "My%20Application") {
t.Errorf("URL should contain custom issuer '%s', got: %s", customIssuer, mfaProps.URL)
}
// Verify the URL contains the user ID
if !strings.Contains(mfaProps.URL, "test/user") {
t.Errorf("URL should contain user ID 'test/user', got: %s", mfaProps.URL)
}
}
func TestTotpMfaInitiate_WithEmptyIssuer(t *testing.T) {
totpMfa := NewTotpMfaUtil(nil)
// Test with empty issuer (should default to "Casdoor")
mfaProps, err := totpMfa.Initiate("test/user", "")
if err != nil {
t.Errorf("Initiate failed: %v", err)
}
if mfaProps == nil {
t.Error("Initiate returned nil mfaProps")
return
}
// Verify the URL contains the default issuer "Casdoor"
if !strings.Contains(mfaProps.URL, "Casdoor") {
t.Errorf("URL should contain default issuer 'Casdoor', got: %s", mfaProps.URL)
}
}
func TestGetMfaUtil_Totp(t *testing.T) {
config := &MfaProps{
MfaType: TotpType,
Secret: "testsecret",
}
mfaUtil := GetMfaUtil(TotpType, config)
if mfaUtil == nil {
t.Error("GetMfaUtil returned nil for TOTP type")
return
}
totpMfa, ok := mfaUtil.(*TotpMfa)
if !ok {
t.Error("GetMfaUtil did not return TotpMfa type")
}
if totpMfa.MfaType != TotpType {
t.Errorf("Expected MFA type %s, got %s", TotpType, totpMfa.MfaType)
}
}

View File

@@ -18,9 +18,12 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/casdoor/casdoor/notification"
"github.com/casdoor/notify"
"github.com/casdoor/casdoor/util"
notify "github.com/casdoor/notify2"
)
func getNotificationClient(provider *Provider) (notify.Notifier, error) {
@@ -43,9 +46,55 @@ func SendNotification(provider *Provider, content string) error {
return err
}
// SsoLogoutNotification represents the structure of a session-level SSO logout notification
// This includes session information and a signature for authentication
type SsoLogoutNotification struct {
// User information
Owner string `json:"owner"`
Name string `json:"name"`
DisplayName string `json:"displayName"`
Email string `json:"email"`
Phone string `json:"phone"`
Id string `json:"id"`
// Event type
Event string `json:"event"`
// Session-level information for targeted logout
SessionIds []string `json:"sessionIds"` // List of session IDs being logged out
AccessTokenHashes []string `json:"accessTokenHashes"` // Hashes of access tokens being expired
// Authentication fields to prevent malicious logout requests
Nonce string `json:"nonce"` // Random nonce for replay protection
Timestamp int64 `json:"timestamp"` // Unix timestamp of the notification
Signature string `json:"signature"` // HMAC-SHA256 signature for verification
}
// GetTokensByUser retrieves all tokens for a specific user
func GetTokensByUser(owner, username string) ([]*Token, error) {
tokens := []*Token{}
err := ormer.Engine.Where("organization = ? and user = ?", owner, username).Find(&tokens)
if err != nil {
return nil, err
}
return tokens, nil
}
// generateLogoutSignature generates an HMAC-SHA256 signature for the logout notification
// The signature is computed over the critical fields to prevent tampering
func generateLogoutSignature(clientSecret string, owner string, name string, nonce string, timestamp int64, sessionIds []string, accessTokenHashes []string) string {
// Create a deterministic string from all fields that need to be verified
// Use strings.Join to avoid trailing separators and improve performance
sessionIdsStr := strings.Join(sessionIds, ",")
tokenHashesStr := strings.Join(accessTokenHashes, ",")
data := fmt.Sprintf("%s|%s|%s|%d|%s|%s", owner, name, nonce, timestamp, sessionIdsStr, tokenHashesStr)
return util.GetHmacSha256(clientSecret, data)
}
// SendSsoLogoutNotifications sends logout notifications to all notification providers
// configured in the user's signup application
func SendSsoLogoutNotifications(user *User) error {
func SendSsoLogoutNotifications(user *User, sessionIds []string, tokens []*Token) error {
if user == nil {
return nil
}
@@ -56,7 +105,7 @@ func SendSsoLogoutNotifications(user *User) error {
}
// Get the user's signup application
application, err := GetApplication(user.SignupApplication)
application, err := GetApplicationByUser(user)
if err != nil {
return fmt.Errorf("failed to get signup application: %w", err)
}
@@ -65,22 +114,50 @@ func SendSsoLogoutNotifications(user *User) error {
return fmt.Errorf("signup application not found: %s", user.SignupApplication)
}
// Prepare sanitized user data for notification
// Only include safe, non-sensitive fields
sanitizedData := map[string]interface{}{
"owner": user.Owner,
"name": user.Name,
"displayName": user.DisplayName,
"email": user.Email,
"phone": user.Phone,
"id": user.Id,
"event": "sso-logout",
// Extract access token hashes from tokens
accessTokenHashes := make([]string, 0, len(tokens))
for _, token := range tokens {
if token.AccessTokenHash != "" {
accessTokenHashes = append(accessTokenHashes, token.AccessTokenHash)
}
}
userData, err := json.Marshal(sanitizedData)
// Generate nonce and timestamp for replay protection
nonce := util.GenerateId()
timestamp := time.Now().Unix()
// Generate signature using the application's client secret
signature := generateLogoutSignature(
application.ClientSecret,
user.Owner,
user.Name,
nonce,
timestamp,
sessionIds,
accessTokenHashes,
)
// Prepare the notification data
notificationObj := SsoLogoutNotification{
Owner: user.Owner,
Name: user.Name,
DisplayName: user.DisplayName,
Email: user.Email,
Phone: user.Phone,
Id: user.Id,
Event: "sso-logout",
SessionIds: sessionIds,
AccessTokenHashes: accessTokenHashes,
Nonce: nonce,
Timestamp: timestamp,
Signature: signature,
}
notificationData, err := json.Marshal(notificationObj)
if err != nil {
return fmt.Errorf("failed to marshal user data: %w", err)
}
content := string(userData)
content := string(notificationData)
// Send notifications to all notification providers in the signup application
for _, providerItem := range application.Providers {
@@ -102,3 +179,18 @@ func SendSsoLogoutNotifications(user *User) error {
return nil
}
// VerifySsoLogoutSignature verifies the signature of an SSO logout notification
// This should be called by applications receiving logout notifications
func VerifySsoLogoutSignature(clientSecret string, notification *SsoLogoutNotification) bool {
expectedSignature := generateLogoutSignature(
clientSecret,
notification.Owner,
notification.Name,
notification.Nonce,
notification.Timestamp,
notification.SessionIds,
notification.AccessTokenHashes,
)
return notification.Signature == expectedSignature
}

154
object/notification_test.go Normal file
View File

@@ -0,0 +1,154 @@
// Copyright 2024 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 (
"testing"
)
func TestGenerateLogoutSignature(t *testing.T) {
// Test that the signature generation is deterministic
clientSecret := "test-secret-key"
owner := "test-org"
name := "test-user"
nonce := "test-nonce-123"
timestamp := int64(1699900000)
sessionIds := []string{"session-1", "session-2"}
accessTokenHashes := []string{"hash-1", "hash-2"}
sig1 := generateLogoutSignature(clientSecret, owner, name, nonce, timestamp, sessionIds, accessTokenHashes)
sig2 := generateLogoutSignature(clientSecret, owner, name, nonce, timestamp, sessionIds, accessTokenHashes)
if sig1 != sig2 {
t.Errorf("Signature should be deterministic, got %s and %s", sig1, sig2)
}
// Test that different inputs produce different signatures
sig3 := generateLogoutSignature(clientSecret, owner, "different-user", nonce, timestamp, sessionIds, accessTokenHashes)
if sig1 == sig3 {
t.Error("Different inputs should produce different signatures")
}
// Test with different client secret
sig4 := generateLogoutSignature("different-secret", owner, name, nonce, timestamp, sessionIds, accessTokenHashes)
if sig1 == sig4 {
t.Error("Different client secrets should produce different signatures")
}
}
func TestVerifySsoLogoutSignature(t *testing.T) {
clientSecret := "test-secret-key"
owner := "test-org"
name := "test-user"
nonce := "test-nonce-123"
timestamp := int64(1699900000)
sessionIds := []string{"session-1", "session-2"}
accessTokenHashes := []string{"hash-1", "hash-2"}
// Generate a valid signature
signature := generateLogoutSignature(clientSecret, owner, name, nonce, timestamp, sessionIds, accessTokenHashes)
// Create a notification with the valid signature
notification := &SsoLogoutNotification{
Owner: owner,
Name: name,
Nonce: nonce,
Timestamp: timestamp,
SessionIds: sessionIds,
AccessTokenHashes: accessTokenHashes,
Signature: signature,
}
// Verify with correct secret
if !VerifySsoLogoutSignature(clientSecret, notification) {
t.Error("Valid signature should be verified successfully")
}
// Verify with wrong secret
if VerifySsoLogoutSignature("wrong-secret", notification) {
t.Error("Invalid signature should not be verified")
}
// Verify with tampered data
tamperedNotification := &SsoLogoutNotification{
Owner: owner,
Name: "tampered-user", // Changed
Nonce: nonce,
Timestamp: timestamp,
SessionIds: sessionIds,
AccessTokenHashes: accessTokenHashes,
Signature: signature, // Same signature
}
if VerifySsoLogoutSignature(clientSecret, tamperedNotification) {
t.Error("Tampered notification should not be verified")
}
}
func TestSsoLogoutNotificationStructure(t *testing.T) {
notification := SsoLogoutNotification{
Owner: "test-org",
Name: "test-user",
DisplayName: "Test User",
Email: "test@example.com",
Phone: "+1234567890",
Id: "user-123",
Event: "sso-logout",
SessionIds: []string{"session-1", "session-2"},
AccessTokenHashes: []string{"hash-1", "hash-2"},
Nonce: "nonce-123",
Timestamp: 1699900000,
Signature: "sig-123",
}
// Verify all fields are set correctly
if notification.Owner != "test-org" {
t.Errorf("Owner mismatch, got %s", notification.Owner)
}
if notification.Name != "test-user" {
t.Errorf("Name mismatch, got %s", notification.Name)
}
if notification.Event != "sso-logout" {
t.Errorf("Event mismatch, got %s", notification.Event)
}
if len(notification.SessionIds) != 2 {
t.Errorf("SessionIds count mismatch, got %d", len(notification.SessionIds))
}
if len(notification.AccessTokenHashes) != 2 {
t.Errorf("AccessTokenHashes count mismatch, got %d", len(notification.AccessTokenHashes))
}
}
func TestGenerateLogoutSignatureWithEmptyArrays(t *testing.T) {
clientSecret := "test-secret-key"
owner := "test-org"
name := "test-user"
nonce := "test-nonce-123"
timestamp := int64(1699900000)
// Test with empty session IDs and token hashes
sig1 := generateLogoutSignature(clientSecret, owner, name, nonce, timestamp, []string{}, []string{})
sig2 := generateLogoutSignature(clientSecret, owner, name, nonce, timestamp, nil, nil)
// Empty slice and nil should produce the same signature
if sig1 != sig2 {
t.Errorf("Empty slice and nil should produce the same signature, got %s and %s", sig1, sig2)
}
// Should be different from non-empty arrays
sig3 := generateLogoutSignature(clientSecret, owner, name, nonce, timestamp, []string{"session-1"}, []string{"hash-1"})
if sig1 == sig3 {
t.Error("Empty arrays should produce different signature from non-empty arrays")
}
}

View File

@@ -23,7 +23,7 @@ import (
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
"gopkg.in/square/go-jose.v2"
"github.com/go-jose/go-jose/v4"
)
type OidcDiscovery struct {
@@ -140,7 +140,7 @@ func GetOidcDiscovery(host string, applicationName string) OidcDiscovery {
IntrospectionEndpoint: fmt.Sprintf("%s/api/login/oauth/introspect", originBackend),
ResponseTypesSupported: []string{"code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token", "none"},
ResponseModesSupported: []string{"query", "fragment", "form_post"},
GrantTypesSupported: []string{"password", "authorization_code"},
GrantTypesSupported: []string{"authorization_code", "implicit", "password", "client_credentials", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code"},
SubjectTypesSupported: []string{"public"},
IdTokenSigningAlgValuesSupported: []string{"RS256", "RS512", "ES256", "ES384", "ES512"},
ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"},

View File

@@ -92,7 +92,7 @@ func PlaceOrder(productId string, user *User, pricingName string, planName strin
return order, nil
}
func PayOrder(providerName, host, paymentEnv string, order *Order) (payment *Payment, attachInfo map[string]interface{}, err error) {
func PayOrder(providerName, host, paymentEnv string, order *Order, lang string) (payment *Payment, attachInfo map[string]interface{}, err error) {
if order.State != "Created" {
return nil, nil, fmt.Errorf("cannot pay for order: %s, current state is %s", order.GetId(), order.State)
}
@@ -210,7 +210,6 @@ func PayOrder(providerName, host, paymentEnv string, order *Order) (payment *Pay
ProductName: product.Name,
ProductDisplayName: product.DisplayName,
Detail: product.Detail,
Tag: product.Tag,
Currency: order.Currency,
Price: order.Price,
IsRecharge: product.IsRecharge,
@@ -223,58 +222,8 @@ func PayOrder(providerName, host, paymentEnv string, order *Order) (payment *Pay
OutOrderId: payResp.OrderId,
}
transaction := &Transaction{
Owner: payment.Owner,
Name: payment.Name,
CreatedTime: util.GetCurrentTime(),
DisplayName: payment.DisplayName,
Application: owner,
Domain: "",
Amount: payment.Price,
Currency: order.Currency,
Payment: payment.Name,
State: pp.PaymentStateCreated,
}
if product.IsRecharge {
transaction.Category = "Recharge"
transaction.Type = ""
transaction.Subtype = ""
transaction.Provider = ""
transaction.Tag = "User"
transaction.User = payment.User
transaction.State = pp.PaymentStatePaid
} else {
transaction.Category = ""
transaction.Type = provider.Category
transaction.Subtype = provider.Type
transaction.Provider = provider.Name
transaction.Tag = product.Tag
transaction.User = payment.User
}
if provider.Type == "Dummy" {
if provider.Type == "Dummy" || provider.Type == "Balance" {
payment.State = pp.PaymentStatePaid
currency := payment.Currency
if currency == "" {
currency = "USD"
}
err = UpdateUserBalance(user.Owner, user.Name, payment.Price, currency, "en")
if err != nil {
return nil, nil, err
}
} else if provider.Type == "Balance" {
convertedPrice := ConvertCurrency(order.Price, order.Currency, user.BalanceCurrency)
if convertedPrice > user.Balance {
return nil, nil, fmt.Errorf("insufficient user balance")
}
transaction.Amount = -transaction.Amount
err = UpdateUserBalance(user.Owner, user.Name, -convertedPrice, user.BalanceCurrency, "en")
if err != nil {
return nil, nil, err
}
payment.State = pp.PaymentStatePaid
transaction.State = pp.PaymentStatePaid
}
affected, err := AddPayment(payment)
@@ -286,14 +235,56 @@ func PayOrder(providerName, host, paymentEnv string, order *Order) (payment *Pay
return nil, nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
}
if product.IsRecharge || provider.Type == "Balance" {
affected, _, err = AddTransaction(transaction, "en", false)
if provider.Type == "Balance" {
transaction := &Transaction{
Owner: payment.Owner,
CreatedTime: util.GetCurrentTime(),
Application: user.SignupApplication,
Amount: -payment.Price,
Currency: order.Currency,
Payment: payment.Name,
Category: TransactionCategoryPurchase,
Type: provider.Category,
Subtype: provider.Type,
Provider: provider.Name,
Tag: "User",
User: payment.User,
State: string(pp.PaymentStatePaid),
}
affected, err = AddInternalPaymentTransaction(transaction, lang)
if err != nil {
return nil, nil, err
}
if !affected {
return nil, nil, fmt.Errorf("failed to add transaction: %s", util.StructToJson(transaction))
}
if product.IsRecharge {
rechargeTransaction := &Transaction{
Owner: payment.Owner,
CreatedTime: util.GetCurrentTime(),
Application: user.SignupApplication,
Amount: payment.Price,
Currency: order.Currency,
Payment: payment.Name,
Category: TransactionCategoryRecharge,
Type: provider.Category,
Subtype: provider.Type,
Provider: provider.Name,
Tag: "User",
User: payment.User,
State: string(pp.PaymentStatePaid),
}
affected, err = AddInternalPaymentTransaction(rechargeTransaction, lang)
if err != nil {
return nil, nil, err
}
if !affected {
return nil, nil, fmt.Errorf("failed to add recharge transaction: %s", util.StructToJson(rechargeTransaction))
}
}
}
order.Payment = payment.Name

Some files were not shown because too many files have changed in this diff Show More