forked from casdoor/casdoor
Compare commits
2 Commits
v2.381.2
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2885b54d1 | ||
|
|
588015f0bc |
52
.github/workflows/build.yml
vendored
52
.github/workflows/build.yml
vendored
@@ -1,8 +1,5 @@
|
||||
name: Build
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.25.8"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -10,6 +7,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
go-tests:
|
||||
name: Running Go tests
|
||||
runs-on: ubuntu-latest
|
||||
@@ -26,7 +24,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version: '1.23'
|
||||
cache-dependency-path: ./go.mod
|
||||
- name: Tests
|
||||
run: |
|
||||
@@ -36,13 +34,13 @@ jobs:
|
||||
frontend:
|
||||
name: Front-end
|
||||
runs-on: ubuntu-latest
|
||||
needs: [go-tests]
|
||||
needs: [ go-tests ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: "yarn"
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: ./web/yarn.lock
|
||||
- run: yarn install && CI=false yarn run build
|
||||
working-directory: ./web
|
||||
@@ -56,12 +54,12 @@ jobs:
|
||||
backend:
|
||||
name: Back-end
|
||||
runs-on: ubuntu-latest
|
||||
needs: [go-tests]
|
||||
needs: [ go-tests ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version: '1.23'
|
||||
cache-dependency-path: ./go.mod
|
||||
- run: go version
|
||||
- name: Build
|
||||
@@ -72,28 +70,27 @@ jobs:
|
||||
linter:
|
||||
name: Go-Linter
|
||||
runs-on: ubuntu-latest
|
||||
needs: [go-tests]
|
||||
needs: [ go-tests ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version: '1.23'
|
||||
cache: false
|
||||
|
||||
- name: Sync vendor tree
|
||||
run: go mod vendor
|
||||
# gen a dummy config file
|
||||
- run: touch dummy.yml
|
||||
|
||||
# CI and local `make lint` both use the repo's gofumpt-only golangci-lint config.
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v9.2.0
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v2.11.4
|
||||
args: --config .golangci.yml ./...
|
||||
version: latest
|
||||
args: --disable-all -c dummy.yml -E=gofumpt --max-same-issues=0 --timeout 5m --modules-download-mode=mod
|
||||
|
||||
e2e:
|
||||
name: e2e-test
|
||||
runs-on: ubuntu-latest
|
||||
needs: [go-tests]
|
||||
needs: [ go-tests ]
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
@@ -107,7 +104,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version: '1.23'
|
||||
cache-dependency-path: ./go.mod
|
||||
- name: start backend
|
||||
run: nohup go run ./main.go > /tmp/backend.log 2>&1 &
|
||||
@@ -132,7 +129,7 @@ jobs:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
cache: "yarn"
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: ./web/yarn.lock
|
||||
- run: yarn install
|
||||
working-directory: ./web
|
||||
@@ -140,7 +137,7 @@ jobs:
|
||||
with:
|
||||
browser: chrome
|
||||
start: yarn start
|
||||
wait-on: "http://localhost:7001"
|
||||
wait-on: 'http://localhost:7001'
|
||||
wait-on-timeout: 210
|
||||
working-directory: ./web
|
||||
|
||||
@@ -162,7 +159,7 @@ jobs:
|
||||
contents: write
|
||||
issues: write
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
|
||||
needs: [frontend, backend, linter, e2e]
|
||||
needs: [ frontend, backend, linter, e2e ]
|
||||
outputs:
|
||||
new-release-published: ${{ steps.semantic.outputs.new_release_published }}
|
||||
new-release-version: ${{ steps.semantic.outputs.new_release_version }}
|
||||
@@ -183,18 +180,13 @@ jobs:
|
||||
contents: write
|
||||
issues: write
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && needs.tag-release.outputs.new-release-published == 'true'
|
||||
needs: [tag-release]
|
||||
needs: [ tag-release ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache-dependency-path: ./go.mod
|
||||
|
||||
- name: Free disk space
|
||||
uses: jlumbroso/free-disk-space@v1.3.1
|
||||
with:
|
||||
@@ -221,7 +213,7 @@ jobs:
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: "~> v2"
|
||||
version: '~> v2'
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -233,7 +225,7 @@ jobs:
|
||||
contents: write
|
||||
issues: write
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && needs.tag-release.outputs.new-release-published == 'true'
|
||||
needs: [tag-release]
|
||||
needs: [ tag-release ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -303,7 +295,7 @@ jobs:
|
||||
if: steps.should_push.outputs.push=='true'
|
||||
with:
|
||||
repository: casdoor/casdoor-helm
|
||||
ref: "master"
|
||||
ref: 'master'
|
||||
token: ${{ secrets.GH_BOT_TOKEN }}
|
||||
|
||||
- name: Update Helm Chart
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,7 +20,6 @@ bin/
|
||||
.idea/
|
||||
*.iml
|
||||
.vscode/settings.json
|
||||
.claude
|
||||
|
||||
tmp/
|
||||
tmpFiles/
|
||||
|
||||
@@ -1,26 +1,42 @@
|
||||
version: "2"
|
||||
run:
|
||||
relative-path-mode: gomod
|
||||
modules-download-mode: vendor
|
||||
linters:
|
||||
default: none
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- gofumpt
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
- deadcode
|
||||
- dupl
|
||||
- errcheck
|
||||
- goconst
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- lll
|
||||
- misspell
|
||||
- nakedret
|
||||
- prealloc
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
- revive
|
||||
- exportloopref
|
||||
run:
|
||||
deadline: 5m
|
||||
skip-dirs:
|
||||
- api
|
||||
# skip-files:
|
||||
# - ".*_test\\.go$"
|
||||
modules-download-mode: mod
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
line-length: 150
|
||||
# tab width in spaces. Default to 1.
|
||||
tab-width: 1
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM node:20.20.1 AS FRONT
|
||||
FROM --platform=$BUILDPLATFORM node:18.19.0 AS FRONT
|
||||
WORKDIR /web
|
||||
|
||||
# Copy only dependency files first for better caching
|
||||
@@ -9,7 +9,7 @@ RUN yarn install --frozen-lockfile --network-timeout 1000000
|
||||
COPY ./web .
|
||||
RUN NODE_OPTIONS="--max-old-space-size=4096" yarn run build
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25.8 AS BACK
|
||||
FROM --platform=$BUILDPLATFORM golang:1.23.12 AS BACK
|
||||
WORKDIR /go/src/casdoor
|
||||
|
||||
# Copy only go.mod and go.sum first for dependency caching
|
||||
|
||||
8
Makefile
8
Makefile
@@ -90,12 +90,12 @@ deps: ## Run dependencies for local development
|
||||
docker compose up -d db
|
||||
|
||||
lint-install: ## Install golangci-lint
|
||||
@# Keep the local golangci-lint version aligned with CI. Both local and CI lint run the gofumpt-only ruleset from .golangci.yml.
|
||||
GOTOOLCHAIN=go1.25.8 go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4
|
||||
@# The following installs a specific version of golangci-lint, which is appropriate for a CI server to avoid different results from build to build
|
||||
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.40.1
|
||||
|
||||
lint: vendor ## Run golangci-lint
|
||||
lint: ## Run golangci-lint
|
||||
@echo "---lint---"
|
||||
golangci-lint run ./...
|
||||
golangci-lint run --modules-download-mode=vendor ./...
|
||||
|
||||
##@ Deployment
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<h1 align="center" style="border-bottom: none;">📦⚡️ Casdoor</h1>
|
||||
<h3 align="center">An open-source AI-first Identity and Access Management (IAM) /AI MCP gateway and auth server with web UI supporting MCP, A2A, OAuth 2.1, OIDC, SAML, CAS, LDAP, SCIM, WebAuthn, TOTP, MFA, Face ID, Google Workspace, Azure AD</h3>
|
||||
<h3 align="center">An open-source UI-first Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML, CAS, LDAP, SCIM, WebAuthn, TOTP, MFA and RADIUS</h3>
|
||||
<p align="center">
|
||||
<a href="#badge">
|
||||
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
|
||||
|
||||
@@ -59,7 +59,6 @@ p, *, *, GET, /api/get-qrcode, *, *
|
||||
p, *, *, GET, /api/get-webhook-event, *, *
|
||||
p, *, *, GET, /api/get-captcha-status, *, *
|
||||
p, *, *, *, /api/login/oauth, *, *
|
||||
p, *, *, POST, /api/oauth/register, *, *
|
||||
p, *, *, GET, /api/get-application, *, *
|
||||
p, *, *, GET, /api/get-organization-applications, *, *
|
||||
p, *, *, GET, /api/get-user, *, *
|
||||
@@ -68,7 +67,6 @@ p, *, *, POST, /api/upload-users, *, *
|
||||
p, *, *, GET, /api/get-resources, *, *
|
||||
p, *, *, GET, /api/get-records, *, *
|
||||
p, *, *, GET, /api/get-product, *, *
|
||||
p, *, *, GET, /api/get-products, *, *
|
||||
p, *, *, GET, /api/get-order, *, *
|
||||
p, *, *, GET, /api/get-orders, *, *
|
||||
p, *, *, GET, /api/get-user-orders, *, *
|
||||
@@ -85,7 +83,6 @@ p, *, *, POST, /api/send-verification-code, *, *
|
||||
p, *, *, GET, /api/get-captcha, *, *
|
||||
p, *, *, POST, /api/verify-captcha, *, *
|
||||
p, *, *, POST, /api/verify-code, *, *
|
||||
p, *, *, POST, /api/v1/traces, *, *
|
||||
p, *, *, POST, /api/reset-email-or-phone, *, *
|
||||
p, *, *, POST, /api/upload-resource, *, *
|
||||
p, *, *, GET, /.well-known/openid-configuration, *, *
|
||||
@@ -119,7 +116,6 @@ p, *, *, GET, /api/run-casbin-command, *, *
|
||||
p, *, *, POST, /api/refresh-engines, *, *
|
||||
p, *, *, GET, /api/get-invitation-info, *, *
|
||||
p, *, *, GET, /api/faceid-signin-begin, *, *
|
||||
p, *, *, GET, /api/kerberos-login, *, *
|
||||
`
|
||||
|
||||
sa := stringadapter.NewAdapter(ruleText)
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
|
||||
"github.com/casbin/lego/v4/acme"
|
||||
"github.com/casbin/lego/v4/certcrypto"
|
||||
"github.com/casbin/lego/v4/lego"
|
||||
"github.com/casbin/lego/v4/registration"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
Key crypto.PrivateKey
|
||||
}
|
||||
|
||||
/** Implementation of the registration.User interface **/
|
||||
|
||||
// GetEmail returns the email address for the account.
|
||||
func (a *Account) GetEmail() string {
|
||||
return a.Email
|
||||
}
|
||||
|
||||
// GetPrivateKey returns the private RSA account key.
|
||||
func (a *Account) GetPrivateKey() crypto.PrivateKey {
|
||||
return a.Key
|
||||
}
|
||||
|
||||
// GetRegistration returns the server registration.
|
||||
func (a *Account) GetRegistration() *registration.Resource {
|
||||
return a.Registration
|
||||
}
|
||||
|
||||
func getLegoClientAndAccount(email string, privateKey string, devMode bool) (*lego.Client, *Account, error) {
|
||||
key, err := decodeEccKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
account := &Account{
|
||||
Email: email,
|
||||
Key: key,
|
||||
}
|
||||
|
||||
config := lego.NewConfig(account)
|
||||
if devMode {
|
||||
config.CADirURL = lego.LEDirectoryStaging
|
||||
} else {
|
||||
config.CADirURL = lego.LEDirectoryProduction
|
||||
}
|
||||
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
config.HTTPClient = proxy.ProxyHttpClient
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client, account, err
|
||||
}
|
||||
|
||||
// GetAcmeClient Incoming an email ,a privatekey and a Boolean value that controls the opening of the test environment
|
||||
// When this function is started for the first time, it will initialize the account-related configuration,
|
||||
// After initializing the configuration, It will try to obtain an account based on the private key,
|
||||
// if it fails, it will create an account based on the private key.
|
||||
// This account will be used during the running of the program
|
||||
func GetAcmeClient(email string, privateKey string, devMode bool) (*lego.Client, error) {
|
||||
// Create a user. New accounts need an email and private key to start.
|
||||
client, account, err := getLegoClientAndAccount(email, privateKey, devMode)
|
||||
|
||||
// try to obtain an account based on the private key
|
||||
account.Registration, err = client.Registration.ResolveAccountByKey()
|
||||
if err != nil {
|
||||
acmeError, ok := err.(*acme.ProblemDetails)
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if acmeError.Type != "urn:ietf:params:acme:error:accountDoesNotExist" {
|
||||
return nil, acmeError
|
||||
}
|
||||
|
||||
// Failed to get account, so create an account based on the private key.
|
||||
account.Registration, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetClient(t *testing.T) {
|
||||
err := web.LoadAppConfig("ini", "../conf/app.conf")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
proxy.InitHttpClient()
|
||||
|
||||
eccKey := util.ReadStringFromPath("acme_account.key")
|
||||
println(eccKey)
|
||||
|
||||
client, err := GetAcmeClient("acme2@casbin.org", eccKey, false)
|
||||
assert.Nil(t, err)
|
||||
pem, key, err := ObtainCertificateAli(client, "casbin.com", accessKeyId, accessKeySecret)
|
||||
assert.Nil(t, err)
|
||||
println(pem)
|
||||
println()
|
||||
println(key)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package certificate
|
||||
|
||||
var (
|
||||
accessKeyId = ""
|
||||
accessKeySecret = ""
|
||||
)
|
||||
@@ -1,151 +0,0 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casbin/lego/v4/certificate"
|
||||
"github.com/casbin/lego/v4/challenge/dns01"
|
||||
"github.com/casbin/lego/v4/cmd"
|
||||
"github.com/casbin/lego/v4/lego"
|
||||
"github.com/casbin/lego/v4/providers/dns/alidns"
|
||||
"github.com/casbin/lego/v4/providers/dns/godaddy"
|
||||
)
|
||||
|
||||
type AliConf struct {
|
||||
Domains []string // The domain names for which you want to apply for a certificate
|
||||
AccessKey string // Aliyun account's AccessKey, if this is not empty, Secret is required.
|
||||
Secret string
|
||||
RAMRole string // Use Ramrole to control aliyun account
|
||||
SecurityToken string // Optional
|
||||
Path string // The path to store cert file
|
||||
Timeout int // Maximum waiting time for certificate application, in minutes
|
||||
}
|
||||
|
||||
type GodaddyConf struct {
|
||||
Domains []string // The domain names for which you want to apply for a certificate
|
||||
APIKey string // GoDaddy account's API Key
|
||||
APISecret string
|
||||
Path string // The path to store cert file
|
||||
Timeout int // Maximum waiting time for certificate application, in minutes
|
||||
}
|
||||
|
||||
// getCert Verify domain ownership, then obtain a certificate, and finally store it locally.
|
||||
// Need to pass in an AliConf struct, some parameters are required, other parameters can be left blank
|
||||
func getAliCert(client *lego.Client, conf AliConf) (string, string, error) {
|
||||
if conf.Timeout <= 0 {
|
||||
conf.Timeout = 3
|
||||
}
|
||||
|
||||
config := alidns.NewDefaultConfig()
|
||||
config.PropagationTimeout = time.Duration(conf.Timeout) * time.Minute
|
||||
config.APIKey = conf.AccessKey
|
||||
config.SecretKey = conf.Secret
|
||||
config.RAMRole = conf.RAMRole
|
||||
config.SecurityToken = conf.SecurityToken
|
||||
|
||||
dnsProvider, err := alidns.NewDNSProvider(config)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Choose a local DNS service provider to increase the authentication speed
|
||||
servers := []string{"223.5.5.5:53"}
|
||||
err = client.Challenge.SetDNS01Provider(dnsProvider, dns01.CondOption(len(servers) > 0, dns01.AddRecursiveNameservers(dns01.ParseNameservers(servers))), dns01.DisableCompletePropagationRequirement())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Obtain the certificate
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: conf.Domains,
|
||||
Bundle: true,
|
||||
}
|
||||
cert, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return string(cert.Certificate), string(cert.PrivateKey), nil
|
||||
}
|
||||
|
||||
func getGoDaddyCert(client *lego.Client, conf GodaddyConf) (string, string, error) {
|
||||
if conf.Timeout <= 0 {
|
||||
conf.Timeout = 3
|
||||
}
|
||||
|
||||
config := godaddy.NewDefaultConfig()
|
||||
config.PropagationTimeout = time.Duration(conf.Timeout) * time.Minute
|
||||
config.PollingInterval = time.Duration(conf.Timeout) * time.Minute / 9
|
||||
config.APIKey = conf.APIKey
|
||||
config.APISecret = conf.APISecret
|
||||
|
||||
dnsProvider, err := godaddy.NewDNSProvider(config)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Choose a local DNS service provider to increase the authentication speed
|
||||
servers := []string{"223.5.5.5:53"}
|
||||
err = client.Challenge.SetDNS01Provider(dnsProvider, dns01.CondOption(len(servers) > 0, dns01.AddRecursiveNameservers(dns01.ParseNameservers(servers))), dns01.DisableCompletePropagationRequirement())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Obtain the certificate
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: conf.Domains,
|
||||
Bundle: true,
|
||||
}
|
||||
cert, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return string(cert.Certificate), string(cert.PrivateKey), nil
|
||||
}
|
||||
|
||||
func ObtainCertificateAli(client *lego.Client, domain string, accessKey string, accessSecret string) (string, string, error) {
|
||||
conf := AliConf{
|
||||
Domains: []string{fmt.Sprintf("*.%s", domain), domain},
|
||||
AccessKey: accessKey,
|
||||
Secret: accessSecret,
|
||||
RAMRole: "",
|
||||
SecurityToken: "",
|
||||
Path: "",
|
||||
Timeout: 3,
|
||||
}
|
||||
return getAliCert(client, conf)
|
||||
}
|
||||
|
||||
func ObtainCertificateGoDaddy(client *lego.Client, domain string, accessKey string, accessSecret string) (string, string, error) {
|
||||
conf := GodaddyConf{
|
||||
Domains: []string{fmt.Sprintf("*.%s", domain), domain},
|
||||
APIKey: accessKey,
|
||||
APISecret: accessSecret,
|
||||
Path: "",
|
||||
Timeout: 3,
|
||||
}
|
||||
return getGoDaddyCert(client, conf)
|
||||
}
|
||||
|
||||
func SaveCert(path, filename string, cert *certificate.Resource) {
|
||||
// Store the certificate file locally
|
||||
certsStorage := cmd.NewCertificatesStorageLib(path, filename, true)
|
||||
certsStorage.CreateRootFolder()
|
||||
certsStorage.SaveResource(cert)
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// generateEccKey generates a public and private key pair.(NIST P-256)
|
||||
func generateEccKey() (*ecdsa.PrivateKey, error) {
|
||||
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
}
|
||||
|
||||
// encodeEccKey Return the input private key object as string type private key
|
||||
func encodeEccKey(privateKey *ecdsa.PrivateKey) (string, error) {
|
||||
x509Encoded, err := x509.MarshalECPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: x509Encoded})
|
||||
return string(pemEncoded), nil
|
||||
}
|
||||
|
||||
// decodeEccKey Return the entered private key string as a private key object that can be used
|
||||
func decodeEccKey(pemEncoded string) (*ecdsa.PrivateKey, error) {
|
||||
block, _ := pem.Decode([]byte(pemEncoded))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("invalid PEM-encoded EC private key")
|
||||
}
|
||||
x509Encoded := block.Bytes
|
||||
privateKey, err := x509.ParseECPrivateKey(x509Encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGenerateEccKey(t *testing.T) {
|
||||
eccKey, err := generateEccKey()
|
||||
assert.Nil(t, err)
|
||||
eccKeyStr, err := encodeEccKey(eccKey)
|
||||
assert.Nil(t, err)
|
||||
println(eccKeyStr)
|
||||
util.WriteStringToPath(eccKeyStr, "acme_account.key")
|
||||
}
|
||||
@@ -21,12 +21,6 @@ originFrontend =
|
||||
staticBaseUrl = "https://cdn.casbin.org"
|
||||
isDemoMode = false
|
||||
batchSize = 100
|
||||
showGithubCorner = false
|
||||
forceLanguage = ""
|
||||
defaultLanguage = "en"
|
||||
aiAssistantUrl = "https://ai.casbin.com"
|
||||
defaultApplication = "app-built-in"
|
||||
maxItemsForFlatMenu = 7
|
||||
enableErrorMask = false
|
||||
enableGzip = true
|
||||
inactiveTimeoutMinutes =
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -25,9 +24,6 @@ import (
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
//go:embed waf.conf
|
||||
var WafConf string
|
||||
|
||||
func init() {
|
||||
// this array contains the beego configuration items that may be modified via env
|
||||
presetConfigItems := []string{"httpport", "appname"}
|
||||
|
||||
246
conf/waf.conf
246
conf/waf.conf
@@ -1,246 +0,0 @@
|
||||
# -- Rule engine initialization ----------------------------------------------
|
||||
|
||||
# Enable Coraza, attaching it to every transaction. Use detection
|
||||
# only to start with, because that minimises the chances of post-installation
|
||||
# disruption.
|
||||
#
|
||||
SecRuleEngine DetectionOnly
|
||||
|
||||
|
||||
# -- Request body handling ---------------------------------------------------
|
||||
|
||||
# Allow Coraza to access request bodies. If you don't, Coraza
|
||||
# won't be able to see any POST parameters, which opens a large security
|
||||
# hole for attackers to exploit.
|
||||
#
|
||||
SecRequestBodyAccess On
|
||||
|
||||
# Enable XML request body parser.
|
||||
# Initiate XML Processor in case of xml content-type
|
||||
#
|
||||
SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \
|
||||
"id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
|
||||
|
||||
# Enable JSON request body parser.
|
||||
# Initiate JSON Processor in case of JSON content-type; change accordingly
|
||||
# if your application does not use 'application/json'
|
||||
#
|
||||
SecRule REQUEST_HEADERS:Content-Type "^application/json" \
|
||||
"id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
|
||||
|
||||
# Sample rule to enable JSON request body parser for more subtypes.
|
||||
# Uncomment or adapt this rule if you want to engage the JSON
|
||||
# Processor for "+json" subtypes
|
||||
#
|
||||
#SecRule REQUEST_HEADERS:Content-Type "^application/[a-z0-9.-]+[+]json" \
|
||||
# "id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
|
||||
|
||||
# Maximum request body size we will accept for buffering. If you support
|
||||
# file uploads then the value given on the first line has to be as large
|
||||
# as the largest file you are willing to accept. The second value refers
|
||||
# to the size of data, with files excluded. You want to keep that value as
|
||||
# low as practical.
|
||||
#
|
||||
SecRequestBodyLimit 13107200
|
||||
|
||||
SecRequestBodyInMemoryLimit 131072
|
||||
|
||||
# SecRequestBodyNoFilesLimit is currently not supported by Coraza
|
||||
# SecRequestBodyNoFilesLimit 131072
|
||||
|
||||
# What to do if the request body size is above our configured limit.
|
||||
# Keep in mind that this setting will automatically be set to ProcessPartial
|
||||
# when SecRuleEngine is set to DetectionOnly mode in order to minimize
|
||||
# disruptions when initially deploying Coraza.
|
||||
#
|
||||
SecRequestBodyLimitAction Reject
|
||||
|
||||
# Verify that we've correctly processed the request body.
|
||||
# As a rule of thumb, when failing to process a request body
|
||||
# you should reject the request (when deployed in blocking mode)
|
||||
# or log a high-severity alert (when deployed in detection-only mode).
|
||||
#
|
||||
SecRule REQBODY_ERROR "!@eq 0" \
|
||||
"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
|
||||
|
||||
# By default be strict with what we accept in the multipart/form-data
|
||||
# request body. If the rule below proves to be too strict for your
|
||||
# environment consider changing it to detection-only. You are encouraged
|
||||
# _not_ to remove it altogether.
|
||||
#
|
||||
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
|
||||
"id:'200003',phase:2,t:none,log,deny,status:400, \
|
||||
msg:'Multipart request body failed strict validation: \
|
||||
PE %{REQBODY_PROCESSOR_ERROR}, \
|
||||
BQ %{MULTIPART_BOUNDARY_QUOTED}, \
|
||||
BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
|
||||
DB %{MULTIPART_DATA_BEFORE}, \
|
||||
DA %{MULTIPART_DATA_AFTER}, \
|
||||
HF %{MULTIPART_HEADER_FOLDING}, \
|
||||
LF %{MULTIPART_LF_LINE}, \
|
||||
SM %{MULTIPART_MISSING_SEMICOLON}, \
|
||||
IQ %{MULTIPART_INVALID_QUOTING}, \
|
||||
IP %{MULTIPART_INVALID_PART}, \
|
||||
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
|
||||
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
|
||||
|
||||
# Did we see anything that might be a boundary?
|
||||
#
|
||||
# Here is a short description about the Coraza Multipart parser: the
|
||||
# parser returns with value 0, if all "boundary-like" line matches with
|
||||
# the boundary string which given in MIME header. In any other cases it returns
|
||||
# with different value, eg. 1 or 2.
|
||||
#
|
||||
# The RFC 1341 descript the multipart content-type and its syntax must contains
|
||||
# only three mandatory lines (above the content):
|
||||
# * Content-Type: multipart/mixed; boundary=BOUNDARY_STRING
|
||||
# * --BOUNDARY_STRING
|
||||
# * --BOUNDARY_STRING--
|
||||
#
|
||||
# First line indicates, that this is a multipart content, second shows that
|
||||
# here starts a part of the multipart content, third shows the end of content.
|
||||
#
|
||||
# If there are any other lines, which starts with "--", then it should be
|
||||
# another boundary id - or not.
|
||||
#
|
||||
# After 3.0.3, there are two kinds of types of boundary errors: strict and permissive.
|
||||
#
|
||||
# If multipart content contains the three necessary lines with correct order, but
|
||||
# there are one or more lines with "--", then parser returns with value 2 (non-zero).
|
||||
#
|
||||
# If some of the necessary lines (usually the start or end) misses, or the order
|
||||
# is wrong, then parser returns with value 1 (also a non-zero).
|
||||
#
|
||||
# You can choose, which one is what you need. The example below contains the
|
||||
# 'strict' mode, which means if there are any lines with start of "--", then
|
||||
# Coraza blocked the content. But the next, commented example contains
|
||||
# the 'permissive' mode, then you check only if the necessary lines exists in
|
||||
# correct order. Whit this, you can enable to upload PEM files (eg "----BEGIN.."),
|
||||
# or other text files, which contains eg. HTTP headers.
|
||||
#
|
||||
# The difference is only the operator - in strict mode (first) the content blocked
|
||||
# in case of any non-zero value. In permissive mode (second, commented) the
|
||||
# content blocked only if the value is explicit 1. If it 0 or 2, the content will
|
||||
# allowed.
|
||||
#
|
||||
|
||||
#
|
||||
# See #1747 and #1924 for further information on the possible values for
|
||||
# MULTIPART_UNMATCHED_BOUNDARY.
|
||||
#
|
||||
SecRule MULTIPART_UNMATCHED_BOUNDARY "@eq 1" \
|
||||
"id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
|
||||
|
||||
# Some internal errors will set flags in TX and we will need to look for these.
|
||||
# All of these are prefixed with "MSC_". The following flags currently exist:
|
||||
#
|
||||
# COR_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded.
|
||||
#
|
||||
SecRule TX:/^COR_/ "!@streq 0" \
|
||||
"id:'200005',phase:2,t:none,deny,msg:'Coraza internal error flagged: %{MATCHED_VAR_NAME}'"
|
||||
|
||||
|
||||
# -- Response body handling --------------------------------------------------
|
||||
|
||||
# Allow Coraza to access response bodies.
|
||||
# You should have this directive enabled in order to identify errors
|
||||
# and data leakage issues.
|
||||
#
|
||||
# Do keep in mind that enabling this directive does increases both
|
||||
# memory consumption and response latency.
|
||||
#
|
||||
SecResponseBodyAccess On
|
||||
|
||||
# Which response MIME types do you want to inspect? You should adjust the
|
||||
# configuration below to catch documents but avoid static files
|
||||
# (e.g., images and archives).
|
||||
#
|
||||
SecResponseBodyMimeType text/plain text/html text/xml
|
||||
|
||||
# Buffer response bodies of up to 512 KB in length.
|
||||
SecResponseBodyLimit 524288
|
||||
|
||||
# What happens when we encounter a response body larger than the configured
|
||||
# limit? By default, we process what we have and let the rest through.
|
||||
# That's somewhat less secure, but does not break any legitimate pages.
|
||||
#
|
||||
SecResponseBodyLimitAction ProcessPartial
|
||||
|
||||
|
||||
# -- Filesystem configuration ------------------------------------------------
|
||||
|
||||
# The location where Coraza will keep its persistent data. This default setting
|
||||
# is chosen due to all systems have /tmp available however, it
|
||||
# too should be updated to a place that other users can't access.
|
||||
#
|
||||
SecDataDir /tmp/
|
||||
|
||||
|
||||
# -- File uploads handling configuration -------------------------------------
|
||||
|
||||
# The location where Coraza stores intercepted uploaded files. This
|
||||
# location must be private to Coraza. You don't want other users on
|
||||
# the server to access the files, do you?
|
||||
#
|
||||
#SecUploadDir /opt/coraza/var/upload/
|
||||
|
||||
# By default, only keep the files that were determined to be unusual
|
||||
# in some way (by an external inspection script). For this to work you
|
||||
# will also need at least one file inspection rule.
|
||||
#
|
||||
#SecUploadKeepFiles RelevantOnly
|
||||
|
||||
# Uploaded files are by default created with permissions that do not allow
|
||||
# any other user to access them. You may need to relax that if you want to
|
||||
# interface Coraza to an external program (e.g., an anti-virus).
|
||||
#
|
||||
#SecUploadFileMode 0600
|
||||
|
||||
|
||||
# -- Debug log configuration -------------------------------------------------
|
||||
|
||||
# Default debug log path
|
||||
# Debug levels:
|
||||
# 0: No logging (least verbose)
|
||||
# 1: Error
|
||||
# 2: Warn
|
||||
# 3: Info
|
||||
# 4-8: Debug
|
||||
# 9: Trace (most verbose)
|
||||
# Most logging has not been implemented because it will be replaced with
|
||||
# advanced rule profiling options
|
||||
#SecDebugLog /opt/coraza/var/log/debug.log
|
||||
#SecDebugLogLevel 3
|
||||
|
||||
|
||||
# -- Audit log configuration -------------------------------------------------
|
||||
|
||||
# Log the transactions that are marked by a rule, as well as those that
|
||||
# trigger a server error (determined by a 5xx or 4xx, excluding 404,
|
||||
# level response status codes).
|
||||
#
|
||||
SecAuditEngine RelevantOnly
|
||||
SecAuditLogRelevantStatus "^(?:(5|4)(0|1)[0-9])$"
|
||||
|
||||
# Log everything we know about a transaction.
|
||||
SecAuditLogParts ABIJDEFHZ
|
||||
|
||||
# Use a single file for logging. This is much easier to look at, but
|
||||
# assumes that you will use the audit log only occasionally.
|
||||
#
|
||||
SecAuditLogType Serial
|
||||
|
||||
|
||||
# -- Miscellaneous -----------------------------------------------------------
|
||||
|
||||
# Use the most commonly used application/x-www-form-urlencoded parameter
|
||||
# separator. There's probably only one application somewhere that uses
|
||||
# something else so don't expect to change this value.
|
||||
#
|
||||
SecArgumentSeparator &
|
||||
|
||||
# Settle on version 0 (zero) cookies, as that is what most applications
|
||||
# use. Using an incorrect cookie version may open your installation to
|
||||
# evasion attacks (against the rules that examine named cookies).
|
||||
#
|
||||
SecCookieFormat 0
|
||||
@@ -1,49 +0,0 @@
|
||||
// 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 conf
|
||||
|
||||
type WebConfig struct {
|
||||
ShowGithubCorner bool `json:"showGithubCorner"`
|
||||
ForceLanguage string `json:"forceLanguage"`
|
||||
DefaultLanguage string `json:"defaultLanguage"`
|
||||
IsDemoMode bool `json:"isDemoMode"`
|
||||
StaticBaseUrl string `json:"staticBaseUrl"`
|
||||
AiAssistantUrl string `json:"aiAssistantUrl"`
|
||||
DefaultApplication string `json:"defaultApplication"`
|
||||
MaxItemsForFlatMenu int64 `json:"maxItemsForFlatMenu"`
|
||||
}
|
||||
|
||||
func GetWebConfig() *WebConfig {
|
||||
config := &WebConfig{}
|
||||
|
||||
config.ShowGithubCorner = GetConfigBool("showGithubCorner")
|
||||
config.ForceLanguage = GetLanguage(GetConfigString("forceLanguage"))
|
||||
config.DefaultLanguage = GetLanguage(GetConfigString("defaultLanguage"))
|
||||
config.IsDemoMode = IsDemoMode()
|
||||
config.StaticBaseUrl = GetConfigString("staticBaseUrl")
|
||||
config.AiAssistantUrl = GetConfigString("aiAssistantUrl")
|
||||
config.DefaultApplication = GetConfigString("defaultApplication")
|
||||
if config.DefaultApplication == "" {
|
||||
config.DefaultApplication = "app-built-in"
|
||||
}
|
||||
|
||||
maxItemsForFlatMenu, err := GetConfigInt64("maxItemsForFlatMenu")
|
||||
if err != nil {
|
||||
maxItemsForFlatMenu = 7
|
||||
}
|
||||
config.MaxItemsForFlatMenu = maxItemsForFlatMenu
|
||||
|
||||
return config
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/casdoor/casdoor/form"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
@@ -313,40 +312,6 @@ func (c *ApiController) Signup() {
|
||||
userId := user.GetId()
|
||||
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
|
||||
|
||||
// Check if this is an OAuth flow and automatically generate code
|
||||
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")
|
||||
codeChallenge := c.Ctx.Input.Query("code_challenge")
|
||||
|
||||
// If OAuth parameters are present, generate OAuth code and return it
|
||||
if clientId != "" && responseType == ResponseTypeCode {
|
||||
consentRequired, err := object.CheckConsentRequired(user, application, scope)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if consentRequired {
|
||||
c.ResponseOk(map[string]bool{"required": true})
|
||||
return
|
||||
}
|
||||
|
||||
code, err := object.GetOAuthCode(userId, clientId, "", "password", responseType, redirectUri, scope, state, nonce, codeChallenge, "", c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
resp := codeToResponse(code)
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(userId)
|
||||
}
|
||||
|
||||
@@ -376,11 +341,18 @@ func (c *ApiController) Logout() {
|
||||
|
||||
c.ClearUserSession()
|
||||
c.ClearTokenSession()
|
||||
|
||||
if err := c.deleteUserSession(user); err != nil {
|
||||
owner, username, err := util.GetOwnerAndNameFromIdWithError(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID(context.Background()))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
|
||||
|
||||
application := c.GetSessionApplication()
|
||||
if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" {
|
||||
@@ -410,7 +382,7 @@ func (c *ApiController) Logout() {
|
||||
return
|
||||
}
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), token.Application))
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist")), token.Application)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -420,13 +392,21 @@ func (c *ApiController) Logout() {
|
||||
|
||||
c.ClearUserSession()
|
||||
c.ClearTokenSession()
|
||||
|
||||
// TODO https://github.com/casdoor/casdoor/pull/1494#discussion_r1095675265
|
||||
if err := c.deleteUserSession(user); err != nil {
|
||||
owner, username, err := util.GetOwnerAndNameFromIdWithError(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
_, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID(context.Background()))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
|
||||
|
||||
if redirectUri == "" {
|
||||
c.ResponseOk()
|
||||
return
|
||||
@@ -559,11 +539,6 @@ func (c *ApiController) SsoLogout() {
|
||||
// @router /get-account [get]
|
||||
func (c *ApiController) GetAccount() {
|
||||
var err error
|
||||
err = util.AppendWebConfigCookie(c.Ctx)
|
||||
if err != nil {
|
||||
logs.Error("AppendWebConfigCookie failed in GetAccount, error: %s", err)
|
||||
}
|
||||
|
||||
user, ok := c.RequireSignedInUser()
|
||||
if !ok {
|
||||
return
|
||||
@@ -690,51 +665,6 @@ func (c *ApiController) GetCaptcha() {
|
||||
applicationId := c.Ctx.Input.Query("applicationId")
|
||||
isCurrentProvider := c.Ctx.Input.Query("isCurrentProvider")
|
||||
|
||||
// When isCurrentProvider == "true", the frontend passes a provider ID instead of an application ID.
|
||||
// In that case, skip application lookup and rule evaluation, and just return the provider config.
|
||||
shouldSkipCaptcha := false
|
||||
|
||||
if isCurrentProvider != "true" {
|
||||
application, err := object.GetApplication(applicationId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), applicationId))
|
||||
return
|
||||
}
|
||||
|
||||
// Check the CAPTCHA rule to determine if CAPTCHA should be shown
|
||||
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
|
||||
|
||||
// For Internet-Only rule, we can determine on the backend if CAPTCHA should be shown
|
||||
// For other rules (Dynamic, Always), we need to return the CAPTCHA config
|
||||
for _, providerItem := range application.Providers {
|
||||
if providerItem.Provider == nil || providerItem.Provider.Category != "Captcha" {
|
||||
continue
|
||||
}
|
||||
|
||||
// For "None" rule, skip CAPTCHA
|
||||
if providerItem.Rule == "None" || providerItem.Rule == "" {
|
||||
shouldSkipCaptcha = true
|
||||
} else if providerItem.Rule == "Internet-Only" {
|
||||
// For Internet-Only rule, check if the client is from intranet
|
||||
if !util.IsInternetIp(clientIp) {
|
||||
// Client is from intranet, skip CAPTCHA
|
||||
shouldSkipCaptcha = true
|
||||
}
|
||||
}
|
||||
|
||||
break // Only check the first CAPTCHA provider
|
||||
}
|
||||
|
||||
if shouldSkipCaptcha {
|
||||
c.ResponseOk(Captcha{Type: "none"})
|
||||
return
|
||||
}
|
||||
}
|
||||
captchaProvider, err := object.GetCaptchaProviderByApplication(applicationId, isCurrentProvider, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -768,24 +698,3 @@ func (c *ApiController) GetCaptcha() {
|
||||
|
||||
c.ResponseOk(Captcha{Type: "none"})
|
||||
}
|
||||
|
||||
func (c *ApiController) deleteUserSession(user string) error {
|
||||
owner, username, err := util.GetOwnerAndNameFromIdWithError(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Casdoor session ID derived from owner, username, and application
|
||||
sessionId := util.GetSessionId(owner, username, object.CasdoorApplication)
|
||||
|
||||
// Explicitly get the Beego session ID from the context
|
||||
beegoSessionId := c.Ctx.Input.CruSession.SessionID(context.Background())
|
||||
|
||||
_, err = object.DeleteSessionId(sessionId, beegoSessionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/beego/beego/v2/server/web/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetAgents
|
||||
// @Title GetAgents
|
||||
// @Tag Agent API
|
||||
// @Description get agents
|
||||
// @Param owner query string true "The owner of agents"
|
||||
// @Success 200 {array} object.Agent The Response object
|
||||
// @router /get-agents [get]
|
||||
func (c *ApiController) GetAgents() {
|
||||
owner := c.Ctx.Input.Query("owner")
|
||||
if owner == "admin" {
|
||||
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 == "" {
|
||||
agents, err := object.GetAgents(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.ResponseOk(agents)
|
||||
return
|
||||
}
|
||||
|
||||
limitInt := util.ParseInt(limit)
|
||||
count, err := object.GetAgentCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limitInt, count)
|
||||
agents, err := object.GetPaginationAgents(owner, paginator.Offset(), limitInt, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(agents, paginator.Nums())
|
||||
}
|
||||
|
||||
// GetAgent
|
||||
// @Title GetAgent
|
||||
// @Tag Agent API
|
||||
// @Description get agent
|
||||
// @Param id query string true "The id ( owner/name ) of the agent"
|
||||
// @Success 200 {object} object.Agent The Response object
|
||||
// @router /get-agent [get]
|
||||
func (c *ApiController) GetAgent() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
agent, err := object.GetAgent(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(agent)
|
||||
}
|
||||
|
||||
// UpdateAgent
|
||||
// @Title UpdateAgent
|
||||
// @Tag Agent API
|
||||
// @Description update agent
|
||||
// @Param id query string true "The id ( owner/name ) of the agent"
|
||||
// @Param body body object.Agent true "The details of the agent"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-agent [post]
|
||||
func (c *ApiController) UpdateAgent() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
var agent object.Agent
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &agent)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateAgent(id, &agent))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddAgent
|
||||
// @Title AddAgent
|
||||
// @Tag Agent API
|
||||
// @Description add agent
|
||||
// @Param body body object.Agent true "The details of the agent"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-agent [post]
|
||||
func (c *ApiController) AddAgent() {
|
||||
var agent object.Agent
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &agent)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddAgent(&agent))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteAgent
|
||||
// @Title DeleteAgent
|
||||
// @Tag Agent API
|
||||
// @Description delete agent
|
||||
// @Param body body object.Agent true "The details of the agent"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-agent [post]
|
||||
func (c *ApiController) DeleteAgent() {
|
||||
var agent object.Agent
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &agent)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteAgent(&agent))
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -161,26 +161,12 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
nonce := c.Ctx.Input.Query("nonce")
|
||||
challengeMethod := c.Ctx.Input.Query("code_challenge_method")
|
||||
codeChallenge := c.Ctx.Input.Query("code_challenge")
|
||||
resource := c.Ctx.Input.Query("resource")
|
||||
|
||||
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
|
||||
c.ResponseError(c.T("auth:Challenge method should be S256"))
|
||||
return
|
||||
}
|
||||
|
||||
consentRequired, err := object.CheckConsentRequired(user, application, scope)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if consentRequired {
|
||||
resp = &Response{Status: "ok", Data: map[string]bool{"required": true}}
|
||||
resp.Data3 = user.NeedUpdatePassword
|
||||
return
|
||||
}
|
||||
|
||||
code, err := object.GetOAuthCode(userId, clientId, form.Provider, form.SigninMethod, responseType, redirectUri, scope, state, nonce, codeChallenge, resource, c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||
code, err := object.GetOAuthCode(userId, clientId, form.Provider, form.SigninMethod, responseType, redirectUri, scope, state, nonce, codeChallenge, c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
@@ -198,15 +184,10 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
} else {
|
||||
scope := c.Ctx.Input.Query("scope")
|
||||
nonce := c.Ctx.Input.Query("nonce")
|
||||
expandedScope, valid := object.IsScopeValidAndExpand(scope, application)
|
||||
if !valid {
|
||||
resp = &Response{Status: "error", Msg: "error: invalid_scope", Data: ""}
|
||||
} else {
|
||||
token, _ := object.GetTokenByUser(application, user, expandedScope, nonce, c.Ctx.Request.Host)
|
||||
resp = tokenToResponse(token)
|
||||
token, _ := object.GetTokenByUser(application, user, scope, nonce, c.Ctx.Request.Host)
|
||||
resp = tokenToResponse(token)
|
||||
|
||||
resp.Data3 = user.NeedUpdatePassword
|
||||
}
|
||||
resp.Data3 = user.NeedUpdatePassword
|
||||
}
|
||||
} else if form.Type == ResponseTypeDevice {
|
||||
authCache, ok := object.DeviceAuthMap.LoadAndDelete(form.UserCode)
|
||||
@@ -456,55 +437,6 @@ func checkMfaEnable(c *ApiController, user *object.User, organization *object.Or
|
||||
return false
|
||||
}
|
||||
|
||||
func getExistUserByBindingRule(providerItem *object.ProviderItem, application *object.Application, userInfo *idp.UserInfo) (user *object.User, err error) {
|
||||
if providerItem.BindingRule == nil {
|
||||
providerItem.BindingRule = &[]string{"Email", "Phone", "Name"}
|
||||
}
|
||||
if len(*providerItem.BindingRule) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
for _, rule := range *providerItem.BindingRule {
|
||||
// Find existing user with Email
|
||||
if rule == "Email" {
|
||||
user, err = object.GetUserByField(application.Organization, "email", userInfo.Email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user != nil {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Find existing user with phone number
|
||||
if rule == "Phone" {
|
||||
user, err = object.GetUserByField(application.Organization, "phone", userInfo.Phone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user != nil {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find existing user by username (case-insensitive)
|
||||
// This allows OAuth providers (e.g., Wecom) to automatically associate with
|
||||
// existing users when usernames match, particularly useful for enterprise
|
||||
// scenarios where signup is disabled and users already exist in Casdoor
|
||||
if rule == "Name" {
|
||||
user, err = object.GetUserByFields(application.Organization, userInfo.Username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user != nil {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// Login ...
|
||||
// @Title Login
|
||||
// @Tag Login API
|
||||
@@ -806,11 +738,7 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
||||
// OAuth
|
||||
idpInfo, err := object.FromProviderToIdpInfo(c.Ctx, provider)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
idpInfo := object.FromProviderToIdpInfo(c.Ctx, provider)
|
||||
idpInfo.CodeVerifier = authForm.CodeVerifier
|
||||
var idProvider idp.IdProvider
|
||||
idProvider, err = idp.GetIdProvider(idpInfo, authForm.RedirectUri)
|
||||
@@ -856,7 +784,7 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
if !reg.MatchString(userInfo.Email) {
|
||||
c.ResponseError(c.T("check:Email is invalid"))
|
||||
c.ResponseError(fmt.Sprintf(c.T("check:Email is invalid")))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -896,10 +824,36 @@ func (c *ApiController) Login() {
|
||||
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||
} else if provider.Category == "OAuth" || provider.Category == "Web3" || provider.Category == "SAML" {
|
||||
// Sign up via OAuth
|
||||
user, err = getExistUserByBindingRule(providerItem, application, userInfo)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
if application.EnableLinkWithEmail {
|
||||
if userInfo.Email != "" {
|
||||
// Find existing user with Email
|
||||
user, err = object.GetUserByField(application.Organization, "email", userInfo.Email)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if user == nil && userInfo.Phone != "" {
|
||||
// Find existing user with phone number
|
||||
user, err = object.GetUserByField(application.Organization, "phone", userInfo.Phone)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find existing user by username (case-insensitive)
|
||||
// This allows OAuth providers (e.g., Wecom) to automatically associate with
|
||||
// existing users when usernames match, particularly useful for enterprise
|
||||
// scenarios where signup is disabled and users already exist in Casdoor
|
||||
if user == nil && userInfo.Username != "" {
|
||||
user, err = object.GetUserByFields(application.Organization, userInfo.Username)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
@@ -913,16 +867,10 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
|
||||
// Check and validate invitation code
|
||||
invitation, msg := object.CheckInvitationCode(application, organization, &authForm, c.GetAcceptLanguage())
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
if application.IsSignupItemRequired("Invitation code") {
|
||||
c.ResponseError(c.T("check:Invitation code cannot be blank"))
|
||||
return
|
||||
}
|
||||
invitationName := ""
|
||||
if invitation != nil {
|
||||
invitationName = invitation.Name
|
||||
}
|
||||
|
||||
// Handle UseEmailAsUsername for OAuth and Web3
|
||||
if organization.UseEmailAsUsername && userInfo.Email != "" {
|
||||
@@ -989,19 +937,12 @@ func (c *ApiController) Login() {
|
||||
IsDeleted: false,
|
||||
SignupApplication: application.Name,
|
||||
Properties: properties,
|
||||
Invitation: invitationName,
|
||||
InvitationCode: authForm.InvitationCode,
|
||||
RegisterType: "Application Signup",
|
||||
RegisterSource: fmt.Sprintf("%s/%s", application.Organization, application.Name),
|
||||
}
|
||||
|
||||
// Set group from invitation code if available, otherwise use provider's signup group or application's default group
|
||||
if invitation != nil && invitation.SignupGroup != "" {
|
||||
user.Groups = []string{invitation.SignupGroup}
|
||||
} else if providerItem.SignupGroup != "" {
|
||||
if providerItem.SignupGroup != "" {
|
||||
user.Groups = []string{providerItem.SignupGroup}
|
||||
} else if application.DefaultGroup != "" {
|
||||
user.Groups = []string{application.DefaultGroup}
|
||||
}
|
||||
|
||||
var affected bool
|
||||
@@ -1015,16 +956,6 @@ func (c *ApiController) Login() {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to create user, user information is invalid: %s"), util.StructToJson(user)))
|
||||
return
|
||||
}
|
||||
|
||||
// Increment invitation usage count
|
||||
if invitation != nil {
|
||||
invitation.UsedCount += 1
|
||||
_, err = object.UpdateInvitation(invitation.GetId(), invitation, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sync info from 3rd-party if possible
|
||||
@@ -1414,7 +1345,7 @@ func (c *ApiController) Callback() {
|
||||
code := c.GetString("code")
|
||||
state := c.GetString("state")
|
||||
|
||||
frontendCallbackUrl := fmt.Sprintf("/callback?code=%s&state=%s", url.QueryEscape(code), url.QueryEscape(state))
|
||||
frontendCallbackUrl := fmt.Sprintf("/callback?code=%s&state=%s", code, state)
|
||||
c.Ctx.Redirect(http.StatusFound, frontendCallbackUrl)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
|
||||
"github.com/beego/beego/v2/core/logs"
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
"github.com/casdoor/casdoor/mcpself"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
@@ -292,14 +291,3 @@ func (c *ApiController) Finish() {
|
||||
}
|
||||
c.Controller.Finish()
|
||||
}
|
||||
|
||||
func (c *ApiController) McpResponseError(id interface{}, code int, message string, data interface{}) {
|
||||
resp := mcpself.BuildMcpResponse(id, nil, &mcpself.McpError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Data: data,
|
||||
})
|
||||
c.Ctx.Output.Header("Content-Type", "application/json")
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@@ -303,13 +303,6 @@ func (c *ApiController) BatchEnforce() {
|
||||
c.ResponseOk(res, keyRes)
|
||||
}
|
||||
|
||||
// GetAllObjects
|
||||
// @Title GetAllObjects
|
||||
// @Tag Enforcer API
|
||||
// @Description Get all objects for a user (Casbin API)
|
||||
// @Param userId query string false "user id like built-in/admin"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /get-all-objects [get]
|
||||
func (c *ApiController) GetAllObjects() {
|
||||
userId := c.Ctx.Input.Query("userId")
|
||||
if userId == "" {
|
||||
@@ -329,13 +322,6 @@ func (c *ApiController) GetAllObjects() {
|
||||
c.ResponseOk(objects)
|
||||
}
|
||||
|
||||
// GetAllActions
|
||||
// @Title GetAllActions
|
||||
// @Tag Enforcer API
|
||||
// @Description Get all actions for a user (Casbin API)
|
||||
// @Param userId query string false "user id like built-in/admin"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /get-all-actions [get]
|
||||
func (c *ApiController) GetAllActions() {
|
||||
userId := c.Ctx.Input.Query("userId")
|
||||
if userId == "" {
|
||||
@@ -355,13 +341,6 @@ func (c *ApiController) GetAllActions() {
|
||||
c.ResponseOk(actions)
|
||||
}
|
||||
|
||||
// GetAllRoles
|
||||
// @Title GetAllRoles
|
||||
// @Tag Enforcer API
|
||||
// @Description Get all roles for a user (Casbin API)
|
||||
// @Param userId query string false "user id like built-in/admin"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /get-all-roles [get]
|
||||
func (c *ApiController) GetAllRoles() {
|
||||
userId := c.Ctx.Input.Query("userId")
|
||||
if userId == "" {
|
||||
|
||||
@@ -26,8 +26,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
)
|
||||
|
||||
type CLIVersionInfo struct {
|
||||
@@ -166,11 +164,6 @@ func processArgsToTempFiles(args []string) ([]string, []string, error) {
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /run-casbin-command [get]
|
||||
func (c *ApiController) RunCasbinCommand() {
|
||||
if !conf.IsDemoMode() && !c.IsAdmin() {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateIdentifier(c); err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
||||
@@ -183,40 +183,3 @@ func (c *ApiController) DeleteCert() {
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteCert(&cert))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateCertDomainExpire
|
||||
// @Title UpdateCertDomainExpire
|
||||
// @Tag Cert API
|
||||
// @Description update cert domain expire time
|
||||
// @Param id query string true "The ID of the cert"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-cert-domain-expire [post]
|
||||
func (c *ApiController) UpdateCertDomainExpire() {
|
||||
if _, ok := c.RequireSignedIn(); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := c.Ctx.Input.Query("id")
|
||||
cert, err := object.GetCert(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
domainExpireTime, err := object.GetDomainExpireTime(cert.Name)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if domainExpireTime == "" {
|
||||
c.ResponseError("Failed to determine domain expiration time for domain " + cert.Name +
|
||||
". Please verify that the domain is valid, publicly resolvable, and has a retrievable expiration date, " +
|
||||
"or update the domain expiration time manually.")
|
||||
return
|
||||
}
|
||||
cert.DomainExpireTime = domainExpireTime
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateCert(id, cert))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
@@ -447,8 +446,8 @@ func downloadCLI() error {
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /refresh-engines [post]
|
||||
func (c *ApiController) RefreshEngines() {
|
||||
if !conf.IsDemoMode() && !c.IsAdmin() {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
if !web.AppConfig.DefaultBool("isDemoMode", false) {
|
||||
c.ResponseError("refresh engines is only available in demo mode")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
// RevokeConsent revokes a consent record
|
||||
// @Title RevokeConsent
|
||||
// @Tag Consent API
|
||||
// @Description revoke a consent record
|
||||
// @Param body body object.ConsentRecord true "The consent object"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /revoke-consent [post]
|
||||
func (c *ApiController) RevokeConsent() {
|
||||
userId := c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError(c.T("general:Please login first"))
|
||||
return
|
||||
}
|
||||
|
||||
var consent object.ConsentRecord
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &consent)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Validate that consent.Application is not empty
|
||||
if consent.Application == "" {
|
||||
c.ResponseError(c.T("general:Application cannot be empty"))
|
||||
return
|
||||
}
|
||||
|
||||
// Validate that GrantedScopes is not empty when scope-specific revoke is requested
|
||||
if len(consent.GrantedScopes) == 0 {
|
||||
c.ResponseError(c.T("general:Granted scopes cannot be empty"))
|
||||
return
|
||||
}
|
||||
|
||||
userObj, err := object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if userObj == nil {
|
||||
c.ResponseError(c.T("general:The user doesn't exist"))
|
||||
return
|
||||
}
|
||||
|
||||
newScopes := []object.ConsentRecord{}
|
||||
for _, record := range userObj.ApplicationScopes {
|
||||
if record.Application != consent.Application {
|
||||
// skip other applications
|
||||
newScopes = append(newScopes, record)
|
||||
continue
|
||||
}
|
||||
// revoke specified scopes
|
||||
revokeSet := make(map[string]bool)
|
||||
for _, s := range consent.GrantedScopes {
|
||||
revokeSet[s] = true
|
||||
}
|
||||
remaining := []string{}
|
||||
for _, s := range record.GrantedScopes {
|
||||
if !revokeSet[s] {
|
||||
remaining = append(remaining, s)
|
||||
}
|
||||
}
|
||||
if len(remaining) > 0 {
|
||||
// still have remaining scopes, keep the record and update
|
||||
record.GrantedScopes = remaining
|
||||
newScopes = append(newScopes, record)
|
||||
}
|
||||
// otherwise the application authorization is revoked, delete the whole record
|
||||
}
|
||||
userObj.ApplicationScopes = newScopes
|
||||
success, err := object.UpdateUser(userObj.GetId(), userObj, nil, false)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.ResponseOk(success)
|
||||
}
|
||||
|
||||
// GrantConsent grants consent for an OAuth application and returns authorization code
|
||||
// @Title GrantConsent
|
||||
// @Tag Consent API
|
||||
// @Description grant consent for an OAuth application and get authorization code
|
||||
// @Param body body object.ConsentRecord true "The consent object with OAuth parameters"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /grant-consent [post]
|
||||
func (c *ApiController) GrantConsent() {
|
||||
userId := c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError(c.T("general:Please login first"))
|
||||
return
|
||||
}
|
||||
|
||||
var request struct {
|
||||
Application string `json:"application"`
|
||||
Scopes []string `json:"grantedScopes"`
|
||||
ClientId string `json:"clientId"`
|
||||
Provider string `json:"provider"`
|
||||
SigninMethod string `json:"signinMethod"`
|
||||
ResponseType string `json:"responseType"`
|
||||
RedirectUri string `json:"redirectUri"`
|
||||
Scope string `json:"scope"`
|
||||
State string `json:"state"`
|
||||
Nonce string `json:"nonce"`
|
||||
Challenge string `json:"challenge"`
|
||||
Resource string `json:"resource"`
|
||||
}
|
||||
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Validate application by clientId
|
||||
application, err := object.GetApplicationByClientId(request.ClientId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if application == nil {
|
||||
c.ResponseError(c.T("general:Invalid client_id"))
|
||||
return
|
||||
}
|
||||
|
||||
// Verify that request.Application matches the application's actual ID
|
||||
if request.Application != application.GetId() {
|
||||
c.ResponseError(c.T("general:Invalid application"))
|
||||
return
|
||||
}
|
||||
|
||||
// Update user's ApplicationScopes
|
||||
userObj, err := object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if userObj == nil {
|
||||
c.ResponseError(c.T("general:User not found"))
|
||||
return
|
||||
}
|
||||
|
||||
appId := application.GetId()
|
||||
found := false
|
||||
// Insert new scope into existing applicationScopes
|
||||
for i, record := range userObj.ApplicationScopes {
|
||||
if record.Application == appId {
|
||||
existing := make(map[string]bool)
|
||||
for _, s := range userObj.ApplicationScopes[i].GrantedScopes {
|
||||
existing[s] = true
|
||||
}
|
||||
for _, s := range request.Scopes {
|
||||
if !existing[s] {
|
||||
userObj.ApplicationScopes[i].GrantedScopes = append(userObj.ApplicationScopes[i].GrantedScopes, s)
|
||||
existing[s] = true
|
||||
}
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// create a new applicationScopes if not found
|
||||
if !found {
|
||||
uniqueScopes := []string{}
|
||||
existing := make(map[string]bool)
|
||||
for _, s := range request.Scopes {
|
||||
if !existing[s] {
|
||||
uniqueScopes = append(uniqueScopes, s)
|
||||
existing[s] = true
|
||||
}
|
||||
}
|
||||
userObj.ApplicationScopes = append(userObj.ApplicationScopes, object.ConsentRecord{
|
||||
Application: appId,
|
||||
GrantedScopes: uniqueScopes,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = object.UpdateUser(userObj.GetId(), userObj, []string{"application_scopes"}, false)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Now get the OAuth code
|
||||
code, err := object.GetOAuthCode(
|
||||
userId,
|
||||
request.ClientId,
|
||||
request.Provider,
|
||||
request.SigninMethod,
|
||||
request.ResponseType,
|
||||
request.RedirectUri,
|
||||
request.Scope,
|
||||
request.State,
|
||||
request.Nonce,
|
||||
request.Challenge,
|
||||
request.Resource,
|
||||
c.Ctx.Request.Host,
|
||||
c.GetAcceptLanguage(),
|
||||
)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(code.Code)
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/beego/beego/v2/server/web/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetEntries
|
||||
// @Title GetEntries
|
||||
// @Tag Entry API
|
||||
// @Description get entries
|
||||
// @Param owner query string true "The owner of entries"
|
||||
// @Success 200 {array} object.Entry The Response object
|
||||
// @router /get-entries [get]
|
||||
func (c *ApiController) GetEntries() {
|
||||
owner := c.Ctx.Input.Query("owner")
|
||||
if owner == "admin" {
|
||||
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 == "" {
|
||||
entries, err := object.GetEntries(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.ResponseOk(entries)
|
||||
return
|
||||
}
|
||||
|
||||
limitInt := util.ParseInt(limit)
|
||||
count, err := object.GetEntryCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limitInt, count)
|
||||
entries, err := object.GetPaginationEntries(owner, paginator.Offset(), limitInt, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(entries, paginator.Nums())
|
||||
}
|
||||
|
||||
// GetEntry
|
||||
// @Title GetEntry
|
||||
// @Tag Entry API
|
||||
// @Description get entry
|
||||
// @Param id query string true "The id ( owner/name ) of the entry"
|
||||
// @Success 200 {object} object.Entry The Response object
|
||||
// @router /get-entry [get]
|
||||
func (c *ApiController) GetEntry() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
entry, err := object.GetEntry(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(entry)
|
||||
}
|
||||
|
||||
// UpdateEntry
|
||||
// @Title UpdateEntry
|
||||
// @Tag Entry API
|
||||
// @Description update entry
|
||||
// @Param id query string true "The id ( owner/name ) of the entry"
|
||||
// @Param body body object.Entry true "The details of the entry"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-entry [post]
|
||||
func (c *ApiController) UpdateEntry() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
var entry object.Entry
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &entry)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateEntry(id, &entry))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddEntry
|
||||
// @Title AddEntry
|
||||
// @Tag Entry API
|
||||
// @Description add entry
|
||||
// @Param body body object.Entry true "The details of the entry"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-entry [post]
|
||||
func (c *ApiController) AddEntry() {
|
||||
var entry object.Entry
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &entry)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddEntry(&entry))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteEntry
|
||||
// @Title DeleteEntry
|
||||
// @Tag Entry API
|
||||
// @Description delete entry
|
||||
// @Param body body object.Entry true "The details of the entry"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-entry [post]
|
||||
func (c *ApiController) DeleteEntry() {
|
||||
var entry object.Entry
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &entry)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteEntry(&entry))
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// AddOtlpEntry
|
||||
// @Title AddTrace
|
||||
// @Tag OTLP API
|
||||
// @Description receive otlp trace protobuf
|
||||
// @Success 200 {object} string
|
||||
// @router /api/v1/traces [post]
|
||||
func (c *ApiController) AddTrace() {
|
||||
if !strings.HasPrefix(c.Ctx.Input.Header("Content-Type"), "application/x-protobuf") {
|
||||
c.Ctx.Output.SetStatus(415)
|
||||
c.Ctx.Output.Body([]byte("unsupported content type"))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.Ctx.Output.SetStatus(400)
|
||||
c.Ctx.Output.Body([]byte("read body failed"))
|
||||
return
|
||||
}
|
||||
|
||||
var req coltracepb.ExportTraceServiceRequest
|
||||
|
||||
if err := proto.Unmarshal(body, &req); err != nil {
|
||||
c.Ctx.Output.SetStatus(400)
|
||||
c.Ctx.Output.Body([]byte(fmt.Sprintf("bad protobuf: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
opts := protojson.MarshalOptions{
|
||||
Multiline: true,
|
||||
Indent: " ",
|
||||
}
|
||||
|
||||
message, err := opts.Marshal(&req)
|
||||
if err != nil {
|
||||
c.Ctx.Output.SetStatus(500)
|
||||
c.Ctx.Output.Body([]byte(fmt.Sprintf("marshal trace failed: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
entry := object.NewTraceEntry(message)
|
||||
|
||||
if _, err := object.AddEntry(entry); err != nil {
|
||||
c.Ctx.Output.SetStatus(500)
|
||||
c.Ctx.Output.Body([]byte(fmt.Sprintf("save trace failed: %v", err)))
|
||||
return
|
||||
}
|
||||
|
||||
resp := &coltracepb.ExportTraceServiceResponse{}
|
||||
respBytes, _ := proto.Marshal(resp)
|
||||
|
||||
c.Ctx.Output.Header("Content-Type", "application/x-protobuf")
|
||||
c.Ctx.Output.SetStatus(200)
|
||||
c.Ctx.Output.Body(respBytes)
|
||||
}
|
||||
@@ -103,7 +103,7 @@ func (c *ApiController) GetInvitationCodeInfo() {
|
||||
return
|
||||
}
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), applicationId))
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The application: %s does not exist"), applicationId))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ func (c *ApiController) SendInvitation() {
|
||||
return
|
||||
}
|
||||
if organization == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The organization: %s does not exist"), invitation.Owner))
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The organization: %s does not exist"), invitation.Owner))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/form"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// KerberosLogin
|
||||
// @Title KerberosLogin
|
||||
// @Tag Login API
|
||||
// @Description Kerberos/SPNEGO login via Integrated Windows Authentication
|
||||
// @Param application query string true "application name"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /kerberos-login [get]
|
||||
func (c *ApiController) KerberosLogin() {
|
||||
applicationName := c.Ctx.Input.Query("application")
|
||||
if applicationName == "" {
|
||||
c.ResponseError(c.T("general:Missing parameter") + ": application")
|
||||
return
|
||||
}
|
||||
|
||||
application, err := object.GetApplication(fmt.Sprintf("admin/%s", applicationName))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), applicationName))
|
||||
return
|
||||
}
|
||||
|
||||
organization, err := object.GetOrganization(util.GetId("admin", application.Organization))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if organization == nil {
|
||||
c.ResponseError(fmt.Sprintf("The organization: %s does not exist", application.Organization))
|
||||
return
|
||||
}
|
||||
|
||||
if organization.KerberosRealm == "" || organization.KerberosKeytab == "" {
|
||||
c.ResponseError("Kerberos is not configured for this organization")
|
||||
return
|
||||
}
|
||||
|
||||
authHeader := c.Ctx.Input.Header("Authorization")
|
||||
if authHeader == "" || !strings.HasPrefix(authHeader, "Negotiate ") {
|
||||
c.Ctx.Output.Header("WWW-Authenticate", "Negotiate")
|
||||
c.Ctx.Output.SetStatus(401)
|
||||
c.Ctx.Output.Body([]byte("Kerberos authentication required"))
|
||||
return
|
||||
}
|
||||
|
||||
spnegoToken := strings.TrimPrefix(authHeader, "Negotiate ")
|
||||
|
||||
kerberosUsername, err := object.ValidateKerberosToken(organization, spnegoToken)
|
||||
if err != nil {
|
||||
c.Ctx.Output.Header("WWW-Authenticate", "Negotiate")
|
||||
c.ResponseError(fmt.Sprintf("Kerberos authentication failed: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
user, err := object.GetUserByKerberosName(organization.Name, kerberosUsername)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), kerberosUsername))
|
||||
return
|
||||
}
|
||||
|
||||
application.OrganizationObj = organization
|
||||
|
||||
authForm := &form.AuthForm{
|
||||
Type: "code",
|
||||
Application: applicationName,
|
||||
Organization: organization.Name,
|
||||
}
|
||||
|
||||
resp := c.HandleLoggedIn(application, user, authForm)
|
||||
if resp != nil {
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
}
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/beego/beego/v2/core/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetKeys
|
||||
// @Title GetKeys
|
||||
// @Tag Key API
|
||||
// @Description get keys
|
||||
// @Param owner query string true "The owner of keys"
|
||||
// @Success 200 {array} object.Key The Response object
|
||||
// @router /get-keys [get]
|
||||
func (c *ApiController) GetKeys() {
|
||||
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 == "" {
|
||||
keys, err := object.GetKeys(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
maskedKeys, err := object.GetMaskedKeys(keys, true, nil)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedKeys)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetKeyCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
|
||||
keys, err := object.GetPaginationKeys(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
maskedKeys, err := object.GetMaskedKeys(keys, true, nil)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedKeys, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// GetGlobalKeys
|
||||
// @Title GetGlobalKeys
|
||||
// @Tag Key API
|
||||
// @Description get global keys
|
||||
// @Success 200 {array} object.Key The Response object
|
||||
// @router /get-global-keys [get]
|
||||
func (c *ApiController) GetGlobalKeys() {
|
||||
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 == "" {
|
||||
keys, err := object.GetGlobalKeys()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
maskedKeys, err := object.GetMaskedKeys(keys, true, nil)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedKeys)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetGlobalKeyCount(field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
|
||||
keys, err := object.GetPaginationGlobalKeys(paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
maskedKeys, err := object.GetMaskedKeys(keys, true, nil)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedKeys, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// GetKey
|
||||
// @Title GetKey
|
||||
// @Tag Key API
|
||||
// @Description get key
|
||||
// @Param id query string true "The id ( owner/name ) of the key"
|
||||
// @Success 200 {object} object.Key The Response object
|
||||
// @router /get-key [get]
|
||||
func (c *ApiController) GetKey() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
key, err := object.GetKey(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(key)
|
||||
}
|
||||
|
||||
// UpdateKey
|
||||
// @Title UpdateKey
|
||||
// @Tag Key API
|
||||
// @Description update key
|
||||
// @Param id query string true "The id ( owner/name ) of the key"
|
||||
// @Param body body object.Key true "The details of the key"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-key [post]
|
||||
func (c *ApiController) UpdateKey() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
oldKey, err := object.GetKey(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if oldKey == nil {
|
||||
c.Data["json"] = wrapActionResponse(false)
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
var key object.Key
|
||||
err = json.Unmarshal(c.Ctx.Input.RequestBody, &key)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !c.IsGlobalAdmin() && oldKey.Owner != key.Owner {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateKey(id, &key))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddKey
|
||||
// @Title AddKey
|
||||
// @Tag Key API
|
||||
// @Description add key
|
||||
// @Param body body object.Key true "The details of the key"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-key [post]
|
||||
func (c *ApiController) AddKey() {
|
||||
var key object.Key
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &key)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddKey(&key))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteKey
|
||||
// @Title DeleteKey
|
||||
// @Tag Key API
|
||||
// @Description delete key
|
||||
// @Param body body object.Key true "The details of the key"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-key [post]
|
||||
func (c *ApiController) DeleteKey() {
|
||||
var key object.Key
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &key)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteKey(&key))
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
||||
"github.com/casdoor/casdoor/mcpself"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// ProxyServer
|
||||
// @Title ProxyServer
|
||||
// @Tag Server API
|
||||
// @Description proxy request to the upstream MCP server by Server URL
|
||||
// @Param owner path string true "The owner name of the server"
|
||||
// @Param name path string true "The name of the server"
|
||||
// @Success 200 {object} mcp.McpResponse The Response object
|
||||
// @router /server/:owner/:name [post]
|
||||
func (c *ApiController) ProxyServer() {
|
||||
owner := c.Ctx.Input.Param(":owner")
|
||||
name := c.Ctx.Input.Param(":name")
|
||||
|
||||
var mcpReq *mcpself.McpRequest
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &mcpReq)
|
||||
if err != nil {
|
||||
c.McpResponseError(1, -32700, "Parse error", err.Error())
|
||||
return
|
||||
}
|
||||
if util.IsStringsEmpty(owner, name) {
|
||||
c.McpResponseError(1, -32600, "invalid server identifier", nil)
|
||||
return
|
||||
}
|
||||
|
||||
server, err := object.GetServer(util.GetId(owner, name))
|
||||
if err != nil {
|
||||
c.McpResponseError(mcpReq.ID, -32600, "server not found", err.Error())
|
||||
return
|
||||
}
|
||||
if server == nil {
|
||||
c.McpResponseError(mcpReq.ID, -32600, "server not found", nil)
|
||||
return
|
||||
}
|
||||
if server.Url == "" {
|
||||
c.McpResponseError(mcpReq.ID, -32600, "server URL is empty", nil)
|
||||
return
|
||||
}
|
||||
|
||||
targetUrl, err := url.Parse(server.Url)
|
||||
if err != nil || !targetUrl.IsAbs() || targetUrl.Host == "" {
|
||||
c.McpResponseError(mcpReq.ID, -32600, "server URL is invalid", nil)
|
||||
return
|
||||
}
|
||||
if targetUrl.Scheme != "http" && targetUrl.Scheme != "https" {
|
||||
c.McpResponseError(mcpReq.ID, -32600, "server URL scheme is invalid", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if mcpReq.Method == "tools/call" {
|
||||
var params mcpself.McpCallToolParams
|
||||
err = json.Unmarshal(mcpReq.Params, ¶ms)
|
||||
if err != nil {
|
||||
c.McpResponseError(mcpReq.ID, -32600, "Invalid request", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, tool := range server.Tools {
|
||||
if tool.Name == params.Name && !tool.IsAllowed {
|
||||
c.McpResponseError(mcpReq.ID, -32600, "tool is forbidden", nil)
|
||||
return
|
||||
} else if tool.Name == params.Name {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(targetUrl)
|
||||
proxy.ErrorHandler = func(writer http.ResponseWriter, request *http.Request, proxyErr error) {
|
||||
c.Ctx.Output.SetStatus(http.StatusBadGateway)
|
||||
c.McpResponseError(mcpReq.ID, -32603, "failed to proxy server request: %s", proxyErr.Error())
|
||||
}
|
||||
proxy.Director = func(request *http.Request) {
|
||||
request.URL.Scheme = targetUrl.Scheme
|
||||
request.URL.Host = targetUrl.Host
|
||||
request.Host = targetUrl.Host
|
||||
request.URL.Path = targetUrl.Path
|
||||
request.URL.RawPath = ""
|
||||
request.URL.RawQuery = targetUrl.RawQuery
|
||||
|
||||
if server.Token != "" {
|
||||
request.Header.Set("Authorization", "Bearer "+server.Token)
|
||||
}
|
||||
}
|
||||
|
||||
proxy.ServeHTTP(c.Ctx.ResponseWriter, c.Ctx.Request)
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
// DynamicClientRegister
|
||||
// @Title DynamicClientRegister
|
||||
// @Tag OAuth API
|
||||
// @Description Register a new OAuth 2.0 client dynamically (RFC 7591)
|
||||
// @Param organization query string false "The organization name (defaults to built-in)"
|
||||
// @Param body body object.DynamicClientRegistrationRequest true "Client registration request"
|
||||
// @Success 201 {object} object.DynamicClientRegistrationResponse
|
||||
// @Failure 400 {object} object.DcrError
|
||||
// @router /api/oauth/register [post]
|
||||
func (c *ApiController) DynamicClientRegister() {
|
||||
var req object.DynamicClientRegistrationRequest
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
|
||||
if err != nil {
|
||||
c.Ctx.Output.Status = http.StatusBadRequest
|
||||
c.Data["json"] = object.DcrError{
|
||||
Error: "invalid_client_metadata",
|
||||
ErrorDescription: "invalid request body: " + err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// Get organization from query parameter or default to built-in
|
||||
organization := c.Ctx.Input.Query("organization")
|
||||
if organization == "" {
|
||||
organization = "built-in"
|
||||
}
|
||||
|
||||
// Register the client
|
||||
response, dcrErr, err := object.RegisterDynamicClient(&req, organization)
|
||||
if err != nil {
|
||||
c.Ctx.Output.Status = http.StatusInternalServerError
|
||||
c.Data["json"] = object.DcrError{
|
||||
Error: "server_error",
|
||||
ErrorDescription: err.Error(),
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
if dcrErr != nil {
|
||||
c.Ctx.Output.Status = http.StatusBadRequest
|
||||
c.Data["json"] = dcrErr
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// Return 201 Created
|
||||
c.Ctx.Output.Status = http.StatusCreated
|
||||
c.Data["json"] = response
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -137,29 +137,3 @@ func (c *RootController) GetWebFingerByApplication() {
|
||||
c.Ctx.Output.ContentType("application/jrd+json")
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetOAuthServerMetadata
|
||||
// @Title GetOAuthServerMetadata
|
||||
// @Tag OAuth API
|
||||
// @Description Get OAuth 2.0 Authorization Server Metadata (RFC 8414)
|
||||
// @Success 200 {object} object.OidcDiscovery
|
||||
// @router /.well-known/oauth-authorization-server [get]
|
||||
func (c *RootController) GetOAuthServerMetadata() {
|
||||
host := c.Ctx.Request.Host
|
||||
c.Data["json"] = object.GetOidcDiscovery(host, "")
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetOAuthServerMetadataByApplication
|
||||
// @Title GetOAuthServerMetadataByApplication
|
||||
// @Tag OAuth API
|
||||
// @Description Get OAuth 2.0 Authorization Server Metadata for specific application (RFC 8414)
|
||||
// @Param application path string true "application name"
|
||||
// @Success 200 {object} object.OidcDiscovery
|
||||
// @router /.well-known/:application/oauth-authorization-server [get]
|
||||
func (c *RootController) GetOAuthServerMetadataByApplication() {
|
||||
application := c.Ctx.Input.Param(":application")
|
||||
host := c.Ctx.Request.Host
|
||||
c.Data["json"] = object.GetOidcDiscovery(host, application)
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -17,6 +17,8 @@ package controllers
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
|
||||
|
||||
"github.com/beego/beego/v2/core/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
@@ -57,7 +59,7 @@ func (c *ApiController) GetRecords() {
|
||||
if c.IsGlobalAdmin() && organizationName != "" {
|
||||
organization = organizationName
|
||||
}
|
||||
filterRecord := &object.Record{Organization: organization}
|
||||
filterRecord := &casvisorsdk.Record{Organization: organization}
|
||||
count, err := object.GetRecordCount(field, value, filterRecord)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -90,7 +92,7 @@ func (c *ApiController) GetRecordsByFilter() {
|
||||
|
||||
body := string(c.Ctx.Input.RequestBody)
|
||||
|
||||
record := &object.Record{}
|
||||
record := &casvisorsdk.Record{}
|
||||
err := util.JsonToStruct(body, record)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -114,7 +116,7 @@ func (c *ApiController) GetRecordsByFilter() {
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-record [post]
|
||||
func (c *ApiController) AddRecord() {
|
||||
var record object.Record
|
||||
var record casvisorsdk.Record
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &record)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/v2/server/web/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/hsluoyz/modsecurity-go/seclang/parser"
|
||||
)
|
||||
|
||||
// GetRules
|
||||
// @Title GetRules
|
||||
// @Tag Rule API
|
||||
// @Description get rules
|
||||
// @Param owner query string true "The owner of rules"
|
||||
// @Success 200 {array} object.Rule The Response object
|
||||
// @router /get-rules [get]
|
||||
func (c *ApiController) GetRules() {
|
||||
owner := c.Ctx.Input.Query("owner")
|
||||
if owner == "admin" {
|
||||
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 == "" {
|
||||
rules, err := object.GetRules(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(rules)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetRuleCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
rules, err := object.GetPaginationRules(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(rules, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// GetRule
|
||||
// @Title GetRule
|
||||
// @Tag Rule API
|
||||
// @Description get rule
|
||||
// @Param id query string true "The id ( owner/name ) of the rule"
|
||||
// @Success 200 {object} object.Rule The Response object
|
||||
// @router /get-rule [get]
|
||||
func (c *ApiController) GetRule() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
rule, err := object.GetRule(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(rule)
|
||||
}
|
||||
|
||||
// AddRule
|
||||
// @Title AddRule
|
||||
// @Tag Rule API
|
||||
// @Description add rule
|
||||
// @Param body body object.Rule true "The details of the rule"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-rule [post]
|
||||
func (c *ApiController) AddRule() {
|
||||
currentTime := util.GetCurrentTime()
|
||||
rule := object.Rule{
|
||||
CreatedTime: currentTime,
|
||||
UpdatedTime: currentTime,
|
||||
}
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &rule)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
err = checkExpressions(rule.Expressions, rule.Type)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.Data["json"] = wrapActionResponse(object.AddRule(&rule))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateRule
|
||||
// @Title UpdateRule
|
||||
// @Tag Rule API
|
||||
// @Description update rule
|
||||
// @Param id query string true "The id ( owner/name ) of the rule"
|
||||
// @Param body body object.Rule true "The details of the rule"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-rule [post]
|
||||
func (c *ApiController) UpdateRule() {
|
||||
var rule object.Rule
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &rule)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = checkExpressions(rule.Expressions, rule.Type)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
id := c.Ctx.Input.Query("id")
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateRule(id, &rule))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteRule
|
||||
// @Title DeleteRule
|
||||
// @Tag Rule API
|
||||
// @Description delete rule
|
||||
// @Param body body object.Rule true "The details of the rule"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-rule [post]
|
||||
func (c *ApiController) DeleteRule() {
|
||||
var rule object.Rule
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &rule)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteRule(&rule))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func checkExpressions(expressions []*object.Expression, ruleType string) error {
|
||||
values := make([]string, len(expressions))
|
||||
for i, expression := range expressions {
|
||||
values[i] = expression.Value
|
||||
}
|
||||
switch ruleType {
|
||||
case "WAF":
|
||||
return checkWafRule(values)
|
||||
case "IP":
|
||||
return checkIpRule(values)
|
||||
case "IP Rate Limiting":
|
||||
return checkIpRateRule(expressions)
|
||||
case "Compound":
|
||||
return checkCompoundRules(values)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkWafRule(rules []string) error {
|
||||
for _, rule := range rules {
|
||||
scanner := parser.NewSecLangScannerFromString(rule)
|
||||
_, err := scanner.AllDirective()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkIpRule(ipLists []string) error {
|
||||
for _, ipList := range ipLists {
|
||||
for _, ip := range strings.Split(ipList, ",") {
|
||||
_, _, err := net.ParseCIDR(ip)
|
||||
if net.ParseIP(ip) == nil && err != nil {
|
||||
return errors.New("Invalid IP address: " + ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkIpRateRule(expressions []*object.Expression) error {
|
||||
if len(expressions) != 1 {
|
||||
return errors.New("IP Rate Limiting rule must have exactly one expression")
|
||||
}
|
||||
expression := expressions[0]
|
||||
_, err := util.ParseIntWithError(expression.Operator)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = util.ParseIntWithError(expression.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkCompoundRules(rules []string) error {
|
||||
_, err := object.GetRulesByRuleIds(rules)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/beego/beego/v2/server/web/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetServers
|
||||
// @Title GetServers
|
||||
// @Tag Server API
|
||||
// @Description get servers
|
||||
// @Param owner query string true "The owner of servers"
|
||||
// @Success 200 {array} object.Server The Response object
|
||||
// @router /get-servers [get]
|
||||
func (c *ApiController) GetServers() {
|
||||
owner := c.Ctx.Input.Query("owner")
|
||||
if owner == "admin" {
|
||||
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 == "" {
|
||||
servers, err := object.GetServers(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.ResponseOk(servers)
|
||||
return
|
||||
}
|
||||
|
||||
limitInt := util.ParseInt(limit)
|
||||
count, err := object.GetServerCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limitInt, count)
|
||||
servers, err := object.GetPaginationServers(owner, paginator.Offset(), limitInt, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(servers, paginator.Nums())
|
||||
}
|
||||
|
||||
// GetServer
|
||||
// @Title GetServer
|
||||
// @Tag Server API
|
||||
// @Description get server
|
||||
// @Param id query string true "The id ( owner/name ) of the server"
|
||||
// @Success 200 {object} object.Server The Response object
|
||||
// @router /get-server [get]
|
||||
func (c *ApiController) GetServer() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
server, err := object.GetServer(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(server)
|
||||
}
|
||||
|
||||
// UpdateServer
|
||||
// @Title UpdateServer
|
||||
// @Tag Server API
|
||||
// @Description update server
|
||||
// @Param id query string true "The id ( owner/name ) of the server"
|
||||
// @Param body body object.Server true "The details of the server"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-server [post]
|
||||
func (c *ApiController) UpdateServer() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
var server object.Server
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &server)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateServer(id, &server))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddServer
|
||||
// @Title AddServer
|
||||
// @Tag Server API
|
||||
// @Description add server
|
||||
// @Param body body object.Server true "The details of the server"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-server [post]
|
||||
func (c *ApiController) AddServer() {
|
||||
var server object.Server
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &server)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddServer(&server))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteServer
|
||||
// @Title DeleteServer
|
||||
// @Tag Server API
|
||||
// @Description delete server
|
||||
// @Param body body object.Server true "The details of the server"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-server [post]
|
||||
func (c *ApiController) DeleteServer() {
|
||||
var server object.Server
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &server)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteServer(&server))
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const onlineServerListUrl = "https://remotemcplist.com/api/servers.json"
|
||||
|
||||
// GetOnlineServers
|
||||
// @Title GetOnlineServers
|
||||
// @Tag Server API
|
||||
// @Description get online MCP server list
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /get-online-servers [get]
|
||||
func (c *ApiController) GetOnlineServers() {
|
||||
httpClient := &http.Client{Timeout: 10 * time.Second}
|
||||
resp, err := httpClient.Get(onlineServerListUrl)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
|
||||
c.ResponseError(fmt.Sprintf("failed to get online server list, status code: %d", resp.StatusCode))
|
||||
return
|
||||
}
|
||||
|
||||
var onlineServers interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&onlineServers)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(onlineServers)
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/beego/beego/v2/server/web/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetGlobalSites
|
||||
// @Title GetGlobalSites
|
||||
// @Tag Site API
|
||||
// @Description get global sites
|
||||
// @Success 200 {array} object.Site The Response object
|
||||
// @router /get-global-sites [get]
|
||||
func (c *ApiController) GetGlobalSites() {
|
||||
sites, err := object.GetGlobalSites()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedSites(sites, util.GetHostname()))
|
||||
}
|
||||
|
||||
// GetSites
|
||||
// @Title GetSites
|
||||
// @Tag Site API
|
||||
// @Description get sites
|
||||
// @Param owner query string true "The owner of sites"
|
||||
// @Success 200 {array} object.Site The Response object
|
||||
// @router /get-sites [get]
|
||||
func (c *ApiController) GetSites() {
|
||||
owner := c.Ctx.Input.Query("owner")
|
||||
if owner == "admin" {
|
||||
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 == "" {
|
||||
sites, err := object.GetSites(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.ResponseOk(object.GetMaskedSites(sites, util.GetHostname()))
|
||||
return
|
||||
}
|
||||
|
||||
limitInt := util.ParseInt(limit)
|
||||
count, err := object.GetSiteCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limitInt, count)
|
||||
sites, err := object.GetPaginationSites(owner, paginator.Offset(), limitInt, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedSites(sites, util.GetHostname()), paginator.Nums())
|
||||
}
|
||||
|
||||
// GetSite
|
||||
// @Title GetSite
|
||||
// @Tag Site API
|
||||
// @Description get site
|
||||
// @Param id query string true "The id ( owner/name ) of the site"
|
||||
// @Success 200 {object} object.Site The Response object
|
||||
// @router /get-site [get]
|
||||
func (c *ApiController) GetSite() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
site, err := object.GetSite(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedSite(site, util.GetHostname()))
|
||||
}
|
||||
|
||||
// UpdateSite
|
||||
// @Title UpdateSite
|
||||
// @Tag Site API
|
||||
// @Description update site
|
||||
// @Param id query string true "The id ( owner/name ) of the site"
|
||||
// @Param body body object.Site true "The details of the site"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-site [post]
|
||||
func (c *ApiController) UpdateSite() {
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
var site object.Site
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &site)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateSite(id, &site))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddSite
|
||||
// @Title AddSite
|
||||
// @Tag Site API
|
||||
// @Description add site
|
||||
// @Param body body object.Site true "The details of the site"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-site [post]
|
||||
func (c *ApiController) AddSite() {
|
||||
var site object.Site
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &site)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddSite(&site))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteSite
|
||||
// @Title DeleteSite
|
||||
// @Tag Site API
|
||||
// @Description delete site
|
||||
// @Param body body object.Site true "The details of the site"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-site [post]
|
||||
func (c *ApiController) DeleteSite() {
|
||||
var site object.Site
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &site)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteSite(&site))
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -16,7 +16,6 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/beego/beego/v2/core/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
@@ -151,26 +150,6 @@ func (c *ApiController) AddSubscription() {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if plan restricts user to one subscription
|
||||
if subscription.Plan != "" {
|
||||
plan, err := object.GetPlan(util.GetId(subscription.Owner, subscription.Plan))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if plan != nil && plan.IsExclusive {
|
||||
hasSubscription, err := object.HasActiveSubscriptionForPlan(subscription.Owner, subscription.User, subscription.Plan)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if hasSubscription {
|
||||
c.ResponseError(fmt.Sprintf("User already has an active subscription for plan: %s", subscription.Plan))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddSubscription(&subscription))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
@@ -162,9 +162,6 @@ func (c *ApiController) DeleteToken() {
|
||||
func (c *ApiController) GetOAuthToken() {
|
||||
clientId := c.Ctx.Input.Query("client_id")
|
||||
clientSecret := c.Ctx.Input.Query("client_secret")
|
||||
assertion := c.Ctx.Input.Query("assertion")
|
||||
clientAssertion := c.Ctx.Input.Query("client_assertion")
|
||||
clientAssertionType := c.Ctx.Input.Query("client_assertion_type")
|
||||
grantType := c.Ctx.Input.Query("grant_type")
|
||||
code := c.Ctx.Input.Query("code")
|
||||
verifier := c.Ctx.Input.Query("code_verifier")
|
||||
@@ -176,10 +173,6 @@ func (c *ApiController) GetOAuthToken() {
|
||||
avatar := c.Ctx.Input.Query("avatar")
|
||||
refreshToken := c.Ctx.Input.Query("refresh_token")
|
||||
deviceCode := c.Ctx.Input.Query("device_code")
|
||||
subjectToken := c.Ctx.Input.Query("subject_token")
|
||||
subjectTokenType := c.Ctx.Input.Query("subject_token_type")
|
||||
audience := c.Ctx.Input.Query("audience")
|
||||
resource := c.Ctx.Input.Query("resource")
|
||||
|
||||
if clientId == "" && clientSecret == "" {
|
||||
clientId, clientSecret, _ = c.Ctx.Request.BasicAuth()
|
||||
@@ -196,12 +189,6 @@ func (c *ApiController) GetOAuthToken() {
|
||||
if clientSecret == "" {
|
||||
clientSecret = tokenRequest.ClientSecret
|
||||
}
|
||||
if clientAssertion == "" {
|
||||
clientAssertion = tokenRequest.ClientAssertion
|
||||
}
|
||||
if clientAssertionType == "" {
|
||||
clientAssertionType = tokenRequest.ClientAssertionType
|
||||
}
|
||||
if grantType == "" {
|
||||
grantType = tokenRequest.GrantType
|
||||
}
|
||||
@@ -232,25 +219,9 @@ func (c *ApiController) GetOAuthToken() {
|
||||
if refreshToken == "" {
|
||||
refreshToken = tokenRequest.RefreshToken
|
||||
}
|
||||
if subjectToken == "" {
|
||||
subjectToken = tokenRequest.SubjectToken
|
||||
}
|
||||
if subjectTokenType == "" {
|
||||
subjectTokenType = tokenRequest.SubjectTokenType
|
||||
}
|
||||
if audience == "" {
|
||||
audience = tokenRequest.Audience
|
||||
}
|
||||
if resource == "" {
|
||||
resource = tokenRequest.Resource
|
||||
}
|
||||
if assertion == "" {
|
||||
assertion = tokenRequest.Assertion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
host := c.Ctx.Request.Host
|
||||
if deviceCode != "" {
|
||||
deviceAuthCache, ok := object.DeviceAuthMap.Load(deviceCode)
|
||||
if !ok {
|
||||
@@ -291,7 +262,8 @@ func (c *ApiController) GetOAuthToken() {
|
||||
username = deviceAuthCacheCast.UserName
|
||||
}
|
||||
|
||||
token, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, nonce, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage(), subjectToken, subjectTokenType, assertion, clientAssertion, clientAssertionType, audience, resource)
|
||||
host := c.Ctx.Request.Host
|
||||
token, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, nonce, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -335,12 +307,7 @@ func (c *ApiController) RefreshToken() {
|
||||
}
|
||||
}
|
||||
|
||||
ok, application, clientId, _, err := c.ValidateOAuth(true)
|
||||
if err != nil || !ok {
|
||||
return
|
||||
}
|
||||
|
||||
refreshToken2, err := object.RefreshToken(application, grantType, refreshToken, scope, clientId, clientSecret, host)
|
||||
refreshToken2, err := object.RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -351,79 +318,14 @@ func (c *ApiController) RefreshToken() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) ResponseTokenError(errorMsg string, errorDescription string) {
|
||||
func (c *ApiController) ResponseTokenError(errorMsg string) {
|
||||
c.Data["json"] = &object.TokenError{
|
||||
Error: errorMsg,
|
||||
ErrorDescription: errorDescription,
|
||||
Error: errorMsg,
|
||||
}
|
||||
c.SetTokenErrorHttpStatus()
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) ValidateOAuth(ignoreValidSecret bool) (ok bool, application *object.Application, clientId, clientSecret string, err error) {
|
||||
reqClientId := c.Ctx.Input.Query("client_id")
|
||||
reqClientSecret := c.Ctx.Input.Query("client_secret")
|
||||
clientAssertion := c.Ctx.Input.Query("client_assertion")
|
||||
clientAssertionType := c.Ctx.Input.Query("client_assertion_type")
|
||||
|
||||
if reqClientId == "" && clientAssertionType == "" {
|
||||
var tokenRequest TokenRequest
|
||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &tokenRequest); err == nil {
|
||||
reqClientId = tokenRequest.ClientId
|
||||
reqClientSecret = tokenRequest.ClientSecret
|
||||
clientAssertion = tokenRequest.ClientAssertion
|
||||
clientAssertionType = tokenRequest.ClientAssertionType
|
||||
}
|
||||
}
|
||||
|
||||
if clientAssertionType == "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" {
|
||||
ok, application, err = object.ValidateClientAssertion(clientAssertion, c.Ctx.Request.Host)
|
||||
if err != nil {
|
||||
c.ResponseTokenError(object.InvalidClient, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !ok || application == nil {
|
||||
c.ResponseTokenError(object.InvalidClient, "client_assertion is invalid")
|
||||
return
|
||||
}
|
||||
|
||||
clientSecret = application.ClientSecret
|
||||
clientId = application.ClientId
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
if reqClientId == "" && reqClientSecret == "" {
|
||||
clientId, clientSecret, ok = c.Ctx.Request.BasicAuth()
|
||||
if !ok {
|
||||
clientId = c.Ctx.Input.Query("client_id")
|
||||
clientSecret = c.Ctx.Input.Query("client_secret")
|
||||
if clientId == "" || clientSecret == "" {
|
||||
c.ResponseTokenError(object.InvalidRequest, "")
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clientId = reqClientId
|
||||
clientSecret = reqClientSecret
|
||||
}
|
||||
|
||||
application, err = object.GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
c.ResponseTokenError(object.InvalidClient, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil || (application.ClientSecret != clientSecret && !ignoreValidSecret) {
|
||||
c.ResponseTokenError(object.InvalidClient, c.T("token:Invalid application or wrong clientSecret"))
|
||||
return
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// IntrospectToken
|
||||
// @Title IntrospectToken
|
||||
// @Tag Login API
|
||||
@@ -431,7 +333,7 @@ func (c *ApiController) ValidateOAuth(ignoreValidSecret bool) (ok bool, applicat
|
||||
// parameter representing an OAuth 2.0 token and returns a JSON document
|
||||
// representing the meta information surrounding the
|
||||
// token, including whether this token is currently active.
|
||||
// This endpoint support Basic Authorization and authorization defined in RFC 7523.
|
||||
// This endpoint only support Basic Authorization.
|
||||
//
|
||||
// @Param token formData string true "access_token's value or refresh_token's value"
|
||||
// @Param token_type_hint formData string true "the token type access_token or refresh_token"
|
||||
@@ -441,9 +343,24 @@ func (c *ApiController) ValidateOAuth(ignoreValidSecret bool) (ok bool, applicat
|
||||
// @router /login/oauth/introspect [post]
|
||||
func (c *ApiController) IntrospectToken() {
|
||||
tokenValue := c.Ctx.Input.Query("token")
|
||||
clientId, clientSecret, ok := c.Ctx.Request.BasicAuth()
|
||||
if !ok {
|
||||
clientId = c.Ctx.Input.Query("client_id")
|
||||
clientSecret = c.Ctx.Input.Query("client_secret")
|
||||
if clientId == "" || clientSecret == "" {
|
||||
c.ResponseTokenError(object.InvalidRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ok, application, clientId, _, err := c.ValidateOAuth(false)
|
||||
if err != nil || !ok {
|
||||
application, err := object.GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
c.ResponseTokenError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil || application.ClientSecret != clientSecret {
|
||||
c.ResponseTokenError(c.T("token:Invalid application or wrong clientSecret"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -457,7 +374,7 @@ func (c *ApiController) IntrospectToken() {
|
||||
if tokenTypeHint != "" {
|
||||
token, err = object.GetTokenByTokenValue(tokenValue, tokenTypeHint)
|
||||
if err != nil {
|
||||
c.ResponseTokenError(object.InvalidRequest, err.Error())
|
||||
c.ResponseTokenError(err.Error())
|
||||
return
|
||||
}
|
||||
if token == nil || token.ExpiresIn <= 0 {
|
||||
@@ -534,7 +451,7 @@ func (c *ApiController) IntrospectToken() {
|
||||
if tokenTypeHint == "" {
|
||||
token, err = object.GetTokenByTokenValue(tokenValue, introspectionResponse.TokenType)
|
||||
if err != nil {
|
||||
c.ResponseTokenError(object.InvalidRequest, err.Error())
|
||||
c.ResponseTokenError(err.Error())
|
||||
return
|
||||
}
|
||||
if token == nil || token.ExpiresIn <= 0 {
|
||||
@@ -546,7 +463,7 @@ func (c *ApiController) IntrospectToken() {
|
||||
if token != nil {
|
||||
application, err = object.GetApplication(fmt.Sprintf("%s/%s", token.Owner, token.Application))
|
||||
if err != nil {
|
||||
c.ResponseTokenError(object.InvalidClient, err.Error())
|
||||
c.ResponseTokenError(err.Error())
|
||||
return
|
||||
}
|
||||
if application == nil {
|
||||
|
||||
@@ -15,23 +15,16 @@
|
||||
package controllers
|
||||
|
||||
type TokenRequest struct {
|
||||
Assertion string `json:"assertion"`
|
||||
ClientId string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
ClientAssertion string `json:"client_assertion"`
|
||||
ClientAssertionType string `json:"client_assertion_type"`
|
||||
GrantType string `json:"grant_type"`
|
||||
Code string `json:"code"`
|
||||
Verifier string `json:"code_verifier"`
|
||||
Scope string `json:"scope"`
|
||||
Nonce string `json:"nonce"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Tag string `json:"tag"`
|
||||
Avatar string `json:"avatar"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
SubjectToken string `json:"subject_token"`
|
||||
SubjectTokenType string `json:"subject_token_type"`
|
||||
Audience string `json:"audience"`
|
||||
Resource string `json:"resource"` // RFC 8707 Resource Indicator
|
||||
ClientId string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
GrantType string `json:"grant_type"`
|
||||
Code string `json:"code"`
|
||||
Verifier string `json:"code_verifier"`
|
||||
Scope string `json:"scope"`
|
||||
Nonce string `json:"nonce"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Tag string `json:"tag"`
|
||||
Avatar string `json:"avatar"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
@@ -730,6 +730,29 @@ func (c *ApiController) GetUserCount() {
|
||||
c.ResponseOk(count)
|
||||
}
|
||||
|
||||
// AddUserKeys
|
||||
// @Title AddUserKeys
|
||||
// @router /add-user-keys [post]
|
||||
// @Tag User API
|
||||
// @Success 200 {object} object.Userinfo The Response object
|
||||
func (c *ApiController) AddUserKeys() {
|
||||
var user object.User
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
isAdmin := c.IsAdmin()
|
||||
affected, err := object.AddUserKeys(&user, isAdmin)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(affected)
|
||||
}
|
||||
|
||||
func (c *ApiController) RemoveUserFromGroup() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
@@ -919,7 +942,7 @@ func (c *ApiController) VerifyIdentification() {
|
||||
}
|
||||
|
||||
if provider == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The provider: %s does not exist"), providerName))
|
||||
c.ResponseError(fmt.Sprintf(c.T("provider:The provider: %s does not exist"), providerName))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -231,7 +230,7 @@ func (c *ApiController) GetProviderFromContext(category string) (*object.Provide
|
||||
|
||||
userId, ok := c.RequireSignedIn()
|
||||
if !ok {
|
||||
return nil, errors.New(c.T("general:Please login first"))
|
||||
return nil, fmt.Errorf(c.T("general:Please login first"))
|
||||
}
|
||||
|
||||
application, err := object.GetApplicationByUserId(userId)
|
||||
|
||||
@@ -151,31 +151,40 @@ func (c *ApiController) SendVerificationCode() {
|
||||
return
|
||||
}
|
||||
|
||||
application, err := object.GetApplication(vform.ApplicationId)
|
||||
provider, err := object.GetCaptchaProviderByApplication(vform.ApplicationId, "false", c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), vform.ApplicationId))
|
||||
return
|
||||
}
|
||||
|
||||
// Check if "Forgot password?" signin item is visible when using forget verification
|
||||
if vform.Method == ForgetVerification {
|
||||
isForgotPasswordEnabled := false
|
||||
for _, item := range application.SigninItems {
|
||||
if item.Name == "Forgot password?" {
|
||||
isForgotPasswordEnabled = item.Visible
|
||||
break
|
||||
}
|
||||
}
|
||||
// Block access if the signin item is not found or is explicitly hidden
|
||||
if !isForgotPasswordEnabled {
|
||||
c.ResponseError(c.T("verification:The forgot password feature is disabled"))
|
||||
if provider != nil {
|
||||
if vform.CaptchaType != provider.Type {
|
||||
c.ResponseError(c.T("verification:Turing test failed."))
|
||||
return
|
||||
}
|
||||
|
||||
if provider.Type != "Default" {
|
||||
vform.ClientSecret = provider.ClientSecret
|
||||
}
|
||||
|
||||
if vform.CaptchaType != "none" {
|
||||
if captchaProvider := captcha.GetCaptchaProvider(vform.CaptchaType); captchaProvider == nil {
|
||||
c.ResponseError(c.T("general:don't support captchaProvider: ") + vform.CaptchaType)
|
||||
return
|
||||
} else if isHuman, err := captchaProvider.VerifyCaptcha(vform.CaptchaToken, provider.ClientId, vform.ClientSecret, provider.ClientId2); err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
} else if !isHuman {
|
||||
c.ResponseError(c.T("verification:Turing test failed."))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
application, err := object.GetApplication(vform.ApplicationId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
organization, err := object.GetOrganization(util.GetId(application.Owner, application.Organization))
|
||||
@@ -189,7 +198,6 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
|
||||
var user *object.User
|
||||
// Try to resolve user for CAPTCHA rule checking
|
||||
// checkUser != "", means method is ForgetVerification
|
||||
if vform.CheckUser != "" {
|
||||
owner := application.Organization
|
||||
@@ -207,86 +215,18 @@ func (c *ApiController) SendVerificationCode() {
|
||||
c.ResponseError(c.T("check:The user is forbidden to sign in, please contact the administrator"))
|
||||
return
|
||||
}
|
||||
} else if mfaUserSession := c.getMfaUserSession(); mfaUserSession != "" {
|
||||
// mfaUserSession != "", means method is MfaAuthVerification
|
||||
}
|
||||
|
||||
// mfaUserSession != "", means method is MfaAuthVerification
|
||||
if mfaUserSession := c.getMfaUserSession(); mfaUserSession != "" {
|
||||
user, err = object.GetUser(mfaUserSession)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
} else if vform.Method == ResetVerification {
|
||||
// For reset verification, get the current logged-in user
|
||||
user = c.getCurrentUser()
|
||||
} else if vform.Method == LoginVerification {
|
||||
// For login verification, try to find user by email/phone for CAPTCHA check
|
||||
// This is a preliminary lookup; the actual validation happens later in the switch statement
|
||||
if vform.Type == object.VerifyTypeEmail && util.IsEmailValid(vform.Dest) {
|
||||
user, err = object.GetUserByEmail(organization.Name, vform.Dest)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
} else if vform.Type == object.VerifyTypePhone {
|
||||
// Prefer resolving the user directly by phone, consistent with the later login switch,
|
||||
// so that Dynamic CAPTCHA is not skipped due to missing/invalid country code.
|
||||
user, err = object.GetUserByPhone(organization.Name, vform.Dest)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine username for CAPTCHA check
|
||||
username := ""
|
||||
if user != nil {
|
||||
username = user.Name
|
||||
} else if vform.CheckUser != "" {
|
||||
username = vform.CheckUser
|
||||
}
|
||||
|
||||
// Check if CAPTCHA should be enabled based on the rule (Dynamic/Always/Internet-Only)
|
||||
enableCaptcha, err := object.CheckToEnableCaptcha(application, organization.Name, username, clientIp)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Only verify CAPTCHA if it should be enabled
|
||||
if enableCaptcha {
|
||||
captchaProvider, err := object.GetCaptchaProviderByApplication(vform.ApplicationId, "false", c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if captchaProvider != nil {
|
||||
if vform.CaptchaType != captchaProvider.Type {
|
||||
c.ResponseError(c.T("verification:Turing test failed."))
|
||||
return
|
||||
}
|
||||
|
||||
if captchaProvider.Type != "Default" {
|
||||
vform.ClientSecret = captchaProvider.ClientSecret
|
||||
}
|
||||
|
||||
if vform.CaptchaType != "none" {
|
||||
if captchaService := captcha.GetCaptchaProvider(vform.CaptchaType); captchaService == nil {
|
||||
c.ResponseError(c.T("general:don't support captchaProvider: ") + vform.CaptchaType)
|
||||
return
|
||||
} else if isHuman, err := captchaService.VerifyCaptcha(vform.CaptchaToken, captchaProvider.ClientId, vform.ClientSecret, captchaProvider.ClientId2); err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
} else if !isHuman {
|
||||
c.ResponseError(c.T("verification:Turing test failed."))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendResp := errors.New("invalid dest type")
|
||||
var provider *object.Provider
|
||||
|
||||
switch vform.Type {
|
||||
case object.VerifyTypeEmail:
|
||||
@@ -440,8 +380,6 @@ func (c *ApiController) ResetEmailOrPhone() {
|
||||
return
|
||||
}
|
||||
|
||||
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
|
||||
|
||||
destType := c.Ctx.Request.Form.Get("type")
|
||||
dest := c.Ctx.Request.Form.Get("dest")
|
||||
code := c.Ctx.Request.Form.Get("code")
|
||||
@@ -496,9 +434,13 @@ func (c *ApiController) ResetEmailOrPhone() {
|
||||
}
|
||||
}
|
||||
|
||||
err = object.CheckVerifyCodeWithLimitAndIp(user, clientIp, checkDest, code, c.GetAcceptLanguage())
|
||||
result, err := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
c.ResponseError(c.T(err.Error()))
|
||||
return
|
||||
}
|
||||
if result.Code != object.VerificationSuccess {
|
||||
c.ResponseError(result.Msg)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -596,12 +538,15 @@ func (c *ApiController) VerifyCode() {
|
||||
}
|
||||
|
||||
if !passed {
|
||||
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
|
||||
err = object.CheckVerifyCodeWithLimitAndIp(user, clientIp, checkDest, authForm.Code, c.GetAcceptLanguage())
|
||||
result, err := object.CheckVerificationCode(checkDest, authForm.Code, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if result.Code != object.VerificationSuccess {
|
||||
c.ResponseError(result.Msg)
|
||||
return
|
||||
}
|
||||
|
||||
err = object.DisableVerificationCode(checkDest)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/beego/beego/v2/core/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
const defaultWebhookEventListLimit = 100
|
||||
|
||||
func (c *ApiController) getScopedWebhookEventQuery() (string, string, bool) {
|
||||
organization, ok := c.RequireAdmin()
|
||||
if !ok {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
owner := ""
|
||||
if c.IsGlobalAdmin() {
|
||||
owner = c.Ctx.Input.Query("owner")
|
||||
|
||||
requestedOrganization := c.Ctx.Input.Query("organization")
|
||||
if requestedOrganization != "" {
|
||||
organization = requestedOrganization
|
||||
}
|
||||
}
|
||||
|
||||
return owner, organization, true
|
||||
}
|
||||
|
||||
func (c *ApiController) checkWebhookEventAccess(event *object.WebhookEvent, organization string) bool {
|
||||
if event == nil || c.IsGlobalAdmin() {
|
||||
return true
|
||||
}
|
||||
|
||||
if event.Organization != organization {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// GetWebhookEvents
|
||||
// @Title GetWebhookEvents
|
||||
// @Tag Webhook Event API
|
||||
// @Description get webhook events with filtering
|
||||
// @Param owner query string false "The owner of webhook events"
|
||||
// @Param organization query string false "The organization"
|
||||
// @Param webhookName query string false "The webhook name"
|
||||
// @Param status query string false "Event status (pending, success, failed, retrying)"
|
||||
// @Success 200 {array} object.WebhookEvent The Response object
|
||||
// @router /get-webhook-events [get]
|
||||
func (c *ApiController) GetWebhookEvents() {
|
||||
owner, organization, ok := c.getScopedWebhookEventQuery()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
webhookName := c.Ctx.Input.Query("webhookName")
|
||||
status := c.Ctx.Input.Query("status")
|
||||
limit := c.Ctx.Input.Query("pageSize")
|
||||
page := c.Ctx.Input.Query("p")
|
||||
sortField := c.Ctx.Input.Query("sortField")
|
||||
sortOrder := c.Ctx.Input.Query("sortOrder")
|
||||
|
||||
if limit != "" && page != "" {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetWebhookEventCount(owner, organization, webhookName, object.WebhookEventStatus(status))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.NewPaginator(c.Ctx.Request, limit, count)
|
||||
events, err := object.GetWebhookEvents(owner, organization, webhookName, object.WebhookEventStatus(status), paginator.Offset(), limit, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(events, paginator.Nums())
|
||||
} else {
|
||||
events, err := object.GetWebhookEvents(owner, organization, webhookName, object.WebhookEventStatus(status), 0, defaultWebhookEventListLimit, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(events)
|
||||
}
|
||||
}
|
||||
|
||||
// GetWebhookEvent
|
||||
// @Title GetWebhookEvent
|
||||
// @Tag Webhook Event API
|
||||
// @Description get webhook event
|
||||
// @Param id query string true "The id ( owner/name ) of the webhook event"
|
||||
// @Success 200 {object} object.WebhookEvent The Response object
|
||||
// @router /get-webhook-event-detail [get]
|
||||
func (c *ApiController) GetWebhookEvent() {
|
||||
organization, ok := c.RequireAdmin()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
event, err := object.GetWebhookEvent(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !c.checkWebhookEventAccess(event, organization) {
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(event)
|
||||
}
|
||||
|
||||
// ReplayWebhookEvent
|
||||
// @Title ReplayWebhookEvent
|
||||
// @Tag Webhook Event API
|
||||
// @Description replay a webhook event
|
||||
// @Param id query string true "The id ( owner/name ) of the webhook event"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /replay-webhook-event [post]
|
||||
func (c *ApiController) ReplayWebhookEvent() {
|
||||
organization, ok := c.RequireAdmin()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := c.Ctx.Input.Query("id")
|
||||
|
||||
event, err := object.GetWebhookEvent(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !c.checkWebhookEventAccess(event, organization) {
|
||||
return
|
||||
}
|
||||
|
||||
err = object.ReplayWebhookEvent(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk("Webhook event replay triggered")
|
||||
}
|
||||
|
||||
// DeleteWebhookEvent
|
||||
// @Title DeleteWebhookEvent
|
||||
// @Tag Webhook Event API
|
||||
// @Description delete webhook event
|
||||
// @Param body body object.WebhookEvent true "The details of the webhook event"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-webhook-event [post]
|
||||
func (c *ApiController) DeleteWebhookEvent() {
|
||||
organization, ok := c.RequireAdmin()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var event object.WebhookEvent
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &event)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
storedEvent, err := object.GetWebhookEvent(event.GetId())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !c.checkWebhookEventAccess(storedEvent, organization) {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteWebhookEvent(&event))
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
// GetOauthProtectedResourceMetadata
|
||||
// @Title GetOauthProtectedResourceMetadata
|
||||
// @Tag OAuth 2.0 API
|
||||
// @Description Get OAuth 2.0 Protected Resource Metadata (RFC 9728)
|
||||
// @Success 200 {object} object.OauthProtectedResourceMetadata
|
||||
// @router /.well-known/oauth-protected-resource [get]
|
||||
func (c *RootController) GetOauthProtectedResourceMetadata() {
|
||||
host := c.Ctx.Request.Host
|
||||
c.Data["json"] = object.GetOauthProtectedResourceMetadata(host)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetOauthProtectedResourceMetadataByApplication
|
||||
// @Title GetOauthProtectedResourceMetadataByApplication
|
||||
// @Tag OAuth 2.0 API
|
||||
// @Description Get OAuth 2.0 Protected Resource Metadata for specific application (RFC 9728)
|
||||
// @Param application path string true "application name"
|
||||
// @Success 200 {object} object.OauthProtectedResourceMetadata
|
||||
// @router /.well-known/:application/oauth-protected-resource [get]
|
||||
func (c *RootController) GetOauthProtectedResourceMetadataByApplication() {
|
||||
application := c.Ctx.Input.Param(":application")
|
||||
host := c.Ctx.Request.Host
|
||||
c.Data["json"] = object.GetOauthProtectedResourceMetadataByApplication(host, application)
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -18,17 +18,14 @@ type EmailProvider interface {
|
||||
Send(fromAddress string, fromName string, toAddress []string, subject string, content string) error
|
||||
}
|
||||
|
||||
func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, sslMode string, endpoint string, method string, httpHeaders map[string]string, bodyMapping map[string]string, contentType string, enableProxy bool) EmailProvider {
|
||||
switch typ {
|
||||
case "Azure ACS":
|
||||
func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool, endpoint string, method string, httpHeaders map[string]string, bodyMapping map[string]string, contentType string, enableProxy bool) EmailProvider {
|
||||
if typ == "Azure ACS" {
|
||||
return NewAzureACSEmailProvider(clientSecret, host)
|
||||
case "Custom HTTP Email":
|
||||
} else if typ == "Custom HTTP Email" {
|
||||
return NewHttpEmailProvider(endpoint, method, httpHeaders, bodyMapping, contentType)
|
||||
case "SendGrid":
|
||||
} else if typ == "SendGrid" {
|
||||
return NewSendgridEmailProvider(clientSecret, host, endpoint)
|
||||
case "Resend":
|
||||
return NewResendEmailProvider(clientSecret)
|
||||
default:
|
||||
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, sslMode, enableProxy)
|
||||
} else {
|
||||
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl, enableProxy)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// 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 email
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/resend/resend-go/v3"
|
||||
)
|
||||
|
||||
type ResendEmailProvider struct {
|
||||
Client *resend.Client
|
||||
}
|
||||
|
||||
func NewResendEmailProvider(apiKey string) *ResendEmailProvider {
|
||||
client := resend.NewClient(apiKey)
|
||||
client.UserAgent += " Casdoor"
|
||||
return &ResendEmailProvider{Client: client}
|
||||
}
|
||||
|
||||
func (s *ResendEmailProvider) Send(fromAddress string, fromName string, toAddresses []string, subject string, content string) error {
|
||||
from := fromAddress
|
||||
if fromName != "" {
|
||||
from = fmt.Sprintf("%s <%s>", fromName, fromAddress)
|
||||
}
|
||||
params := &resend.SendEmailRequest{
|
||||
From: from,
|
||||
To: toAddresses,
|
||||
Subject: subject,
|
||||
Html: content,
|
||||
}
|
||||
if _, err := s.Client.Emails.Send(params); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -25,20 +25,13 @@ type SmtpEmailProvider struct {
|
||||
Dialer *gomail.Dialer
|
||||
}
|
||||
|
||||
func NewSmtpEmailProvider(userName string, password string, host string, port int, typ string, sslMode string, enableProxy bool) *SmtpEmailProvider {
|
||||
func NewSmtpEmailProvider(userName string, password string, host string, port int, typ string, disableSsl bool, enableProxy bool) *SmtpEmailProvider {
|
||||
dialer := gomail.NewDialer(host, port, userName, password)
|
||||
if typ == "SUBMAIL" {
|
||||
dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
|
||||
// Handle SSL mode: "Auto" (or empty) means don't override gomail's default behavior
|
||||
// "Enable" means force SSL on, "Disable" means force SSL off
|
||||
if sslMode == "Enable" {
|
||||
dialer.SSL = true
|
||||
} else if sslMode == "Disable" {
|
||||
dialer.SSL = false
|
||||
}
|
||||
// If sslMode is "Auto" or empty, don't set dialer.SSL - let gomail decide based on port
|
||||
dialer.SSL = !disableSsl
|
||||
|
||||
if enableProxy {
|
||||
socks5Proxy := conf.GetConfigString("socks5Proxy")
|
||||
|
||||
121
go.mod
121
go.mod
@@ -1,8 +1,6 @@
|
||||
module github.com/casdoor/casdoor
|
||||
|
||||
go 1.25.0
|
||||
|
||||
toolchain go1.25.8
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/Masterminds/squirrel v1.5.3
|
||||
@@ -16,29 +14,26 @@ require (
|
||||
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/aliyun/alibaba-cloud-sdk-go v1.63.107
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible
|
||||
github.com/aliyun/credentials-go v1.3.10
|
||||
github.com/aws/aws-sdk-go v1.45.5
|
||||
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/casbin/lego/v4 v4.5.4
|
||||
github.com/casdoor/casdoor-go-sdk v0.50.0
|
||||
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/notify2 v1.6.0
|
||||
github.com/casdoor/oss v1.8.0
|
||||
github.com/casdoor/xorm-adapter/v3 v3.1.0
|
||||
github.com/corazawaf/coraza/v3 v3.3.3
|
||||
github.com/casvisor/casvisor-go-sdk v1.4.0
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/denisenkom/go-mssqldb v0.9.0
|
||||
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.16.3
|
||||
github.com/go-jose/go-jose/v4 v4.1.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
|
||||
@@ -46,27 +41,20 @@ require (
|
||||
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.3.0
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hsluoyz/modsecurity-go v0.0.7
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/lestrrat-go/jwx v1.2.29
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/likexian/whois v1.15.1
|
||||
github.com/likexian/whois-parser v1.24.9
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
||||
github.com/markbates/goth v1.82.0
|
||||
github.com/microsoft/go-mssqldb v1.9.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/modelcontextprotocol/go-sdk v1.4.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.19.0
|
||||
github.com/prometheus/client_model v0.6.2
|
||||
github.com/prometheus/client_model v0.6.0
|
||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
||||
github.com/resend/resend-go/v3 v3.1.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/russellhaering/gosaml2 v0.9.0
|
||||
github.com/russellhaering/goxmldsig v1.2.0
|
||||
@@ -80,12 +68,10 @@ 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
|
||||
go.opentelemetry.io/proto/otlp v1.7.1
|
||||
golang.org/x/crypto v0.47.0
|
||||
golang.org/x/net v0.49.0
|
||||
golang.org/x/oauth2 v0.34.0
|
||||
golang.org/x/text v0.33.0
|
||||
golang.org/x/time v0.8.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.22.1
|
||||
@@ -93,11 +79,11 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.25.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.9.0 // 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
|
||||
@@ -107,7 +93,7 @@ require (
|
||||
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.31.0 // 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
|
||||
@@ -125,8 +111,10 @@ require (
|
||||
github.com/alibabacloud-go/tea-oss-utils v1.1.0 // indirect
|
||||
github.com/alibabacloud-go/tea-utils v1.3.6 // indirect
|
||||
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.545 // 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
|
||||
@@ -134,16 +122,16 @@ require (
|
||||
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/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.6.1 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
|
||||
github.com/corazawaf/libinjection-go v0.2.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // 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.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // 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
|
||||
@@ -153,8 +141,8 @@ require (
|
||||
github.com/drswork/go-twitter v0.0.0-20221107160839-dea1b6ed53d7 // indirect
|
||||
github.com/ebitengine/purego v0.9.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 // 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
|
||||
@@ -162,7 +150,7 @@ require (
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // 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.3 // 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
|
||||
@@ -173,30 +161,21 @@ require (
|
||||
github.com/go-webauthn/x v0.1.9 // 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-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // 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-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/go-tpm v0.9.0 // indirect
|
||||
github.com/google/jsonschema-go v0.4.2 // 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/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
|
||||
github.com/jcmturner/gofork v1.7.6 // indirect
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
@@ -208,51 +187,41 @@ require (
|
||||
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/likexian/gokit v0.25.13 // 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/magefile/mage v1.15.1-0.20241126214340-bdc92f694516 // 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/miekg/dns v1.1.57 // 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
|
||||
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20240411101913-e07a1f0e8eb4 // indirect
|
||||
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.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.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // 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/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2 // 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.33.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/scim2/filter-parser/v2 v2.2.0 // indirect
|
||||
github.com/segmentio/asm v1.1.3 // indirect
|
||||
github.com/segmentio/encoding v0.5.3 // indirect
|
||||
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // 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.1 // indirect
|
||||
github.com/slack-go/slack v0.15.0 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.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
|
||||
@@ -268,43 +237,40 @@ require (
|
||||
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/urfave/cli v1.22.5 // indirect
|
||||
github.com/utahta/go-linenotify v0.5.0 // indirect
|
||||
github.com/valllabh/ocsf-schema-golang v1.0.3 // 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/yosida95/uritemplate/v3 v3.0.2 // 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/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.40.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect
|
||||
go.opentelemetry.io/otel v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.40.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-20241215155358-4a5509556b9e // indirect
|
||||
golang.org/x/image v0.18.0 // indirect
|
||||
golang.org/x/mod v0.32.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/tools v0.41.0 // indirect
|
||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
|
||||
golang.org/x/mod v0.25.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-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // 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/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
@@ -316,5 +282,4 @@ require (
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
rsc.io/binaryregexp v0.2.0 // indirect
|
||||
)
|
||||
|
||||
255
go.sum
255
go.sum
@@ -1,5 +1,5 @@
|
||||
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
|
||||
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
|
||||
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
@@ -186,8 +186,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ
|
||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY=
|
||||
cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck=
|
||||
cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w=
|
||||
@@ -627,16 +627,6 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGq
|
||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
|
||||
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww=
|
||||
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
|
||||
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
@@ -652,14 +642,12 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0 h1:jJKWl98inONJAr/IZrdFQUWcwUO95DLY1XMD1ZIut+g=
|
||||
@@ -778,9 +766,8 @@ github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCE
|
||||
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
||||
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
|
||||
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 h1:qagvUyrgOnBIlVRQWOyCZGVKUIYbMBdGdJ104vBpRFU=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.545 h1:0LfzeUr4quwrrrTHn1kfLA0FBdsChCMs8eK2EzOwXVQ=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.545/go.mod h1:Api2AkmMgGaSUAhmk76oaFObkoeCPc/bKAqcyplPODs=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible h1:9gWa46nstkJ9miBReJcN8Gq34cBFbzSpQZVVT9N09TM=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
|
||||
@@ -851,8 +838,6 @@ github.com/casbin/casbin/v2 v2.28.3/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRt
|
||||
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
||||
github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3jM=
|
||||
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
|
||||
github.com/casbin/lego/v4 v4.5.4 h1:WdVEj1A5KmKZheNuFNLF/5+UUkpXLt9mEOrLX3E81Vo=
|
||||
github.com/casbin/lego/v4 v4.5.4/go.mod h1:JjTyJgN5pyrDPcg3+aAM1NtFQIXl8zDgsoSS1TnVpJ8=
|
||||
github.com/casdoor/casdoor-go-sdk v0.50.0 h1:bUYbz/MzJuWfLKJbJM0+U0YpYewAur+THp5TKnufWZM=
|
||||
github.com/casdoor/casdoor-go-sdk v0.50.0/go.mod h1:cMnkCQJgMYpgAlgEx8reSt1AVaDIQLcJ1zk5pzBaz+4=
|
||||
github.com/casdoor/go-sms-sender v0.25.0 h1:eF4cOCSbjVg7+0uLlJQnna/FQ0BWW+Fp/x4cXhzQu1Y=
|
||||
@@ -867,12 +852,15 @@ github.com/casdoor/oss v1.8.0 h1:uuyKhDIp7ydOtV4lpqhAY23Ban2Ln8La8+QT36CwylM=
|
||||
github.com/casdoor/oss v1.8.0/go.mod h1:uaqO7KBI2lnZcnB8rF7O6C2bN7llIbfC5Ql8ex1yR1U=
|
||||
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
|
||||
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
|
||||
github.com/casvisor/casvisor-go-sdk v1.4.0 h1:hbZEGGJ1cwdHFAxeXrMoNw6yha6Oyg2F0qQhBNCN/dg=
|
||||
github.com/casvisor/casvisor-go-sdk v1.4.0/go.mod h1:frnNtH5GA0wxzAQLyZxxfL0RSsSub9GQPi2Ybe86ocE=
|
||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -905,20 +893,12 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
|
||||
github.com/corazawaf/coraza-coreruleset v0.0.0-20240226094324-415b1017abdc h1:OlJhrgI3I+FLUCTI3JJW8MoqyM78WbqJjecqMnqG+wc=
|
||||
github.com/corazawaf/coraza-coreruleset v0.0.0-20240226094324-415b1017abdc/go.mod h1:7rsocqNDkTCira5T0M7buoKR2ehh7YZiPkzxRuAgvVU=
|
||||
github.com/corazawaf/coraza/v3 v3.3.3 h1:kqjStHAgWqwP5dh7n0vhTOF0a3t+VikNS/EaMiG0Fhk=
|
||||
github.com/corazawaf/coraza/v3 v3.3.3/go.mod h1:xSaXWOhFMSbrV8qOOfBKAyw3aOqfwaSaOy5BgSF8XlA=
|
||||
github.com/corazawaf/libinjection-go v0.2.2 h1:Chzodvb6+NXh6wew5/yhD0Ggioif9ACrQGR4qjTCs1g=
|
||||
github.com/corazawaf/libinjection-go v0.2.2/go.mod h1:OP4TM7xdJ2skyXqNX1AN1wN5nNZEmJNuWbNPOItn7aw=
|
||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI=
|
||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cschomburg/go-pushbullet v0.0.0-20171206132031-67759df45fbb h1:7X9nrm+LNWdxzQOiCjy0G51rNUxbH35IDHCjAMvogyM=
|
||||
github.com/cschomburg/go-pushbullet v0.0.0-20171206132031-67759df45fbb/go.mod h1:RfQ9wji3fjcSEsQ+uFCtIh3+BXgcZum8Kt3JxvzYzlk=
|
||||
@@ -928,9 +908,8 @@ github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs
|
||||
github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
|
||||
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
|
||||
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
@@ -938,6 +917,7 @@ github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPc
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk=
|
||||
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dghubble/oauth1 v0.7.3 h1:EkEM/zMDMp3zOsX2DC/ZQ2vnEX3ELK0/l9kb+vs4ptE=
|
||||
github.com/dghubble/oauth1 v0.7.3/go.mod h1:oxTe+az9NSMIucDPDCCtzJGsPhciJV33xocHfcR2sVY=
|
||||
@@ -980,18 +960,14 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34=
|
||||
github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||
github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE=
|
||||
github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
@@ -1003,8 +979,6 @@ github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzP
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
|
||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
|
||||
github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
|
||||
github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo=
|
||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
@@ -1038,8 +1012,8 @@ github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lo
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI=
|
||||
github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
|
||||
@@ -1056,8 +1030,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-mysql-org/go-mysql v1.7.0 h1:qE5FTRb3ZeTQmlk3pjE+/m2ravGxxRDrVDTyDe9tvqI=
|
||||
@@ -1114,13 +1088,10 @@ github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
@@ -1192,8 +1163,6 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17
|
||||
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
|
||||
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
|
||||
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@@ -1267,9 +1236,7 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
|
||||
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
@@ -1280,8 +1247,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
|
||||
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -1303,8 +1268,6 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
@@ -1314,8 +1277,6 @@ github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg
|
||||
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hsluoyz/modsecurity-go v0.0.7 h1:W5ChaDrm4kM/UhHxoD2zyxQ+6s5kSj6cVftDFgdFzBM=
|
||||
github.com/hsluoyz/modsecurity-go v0.0.7/go.mod h1:hi81ySzwvlQFd5pip9c3uwXHDAW9ayxwLbt8ufxRkdY=
|
||||
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
@@ -1325,21 +1286,11 @@ github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9
|
||||
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jcchavezs/mergefs v0.1.0 h1:7oteO7Ocl/fnfFMkoVLJxTveCjrsd//UB0j89xmnpec=
|
||||
github.com/jcchavezs/mergefs v0.1.0/go.mod h1:eRLTrsA+vFwQZ48hj8p8gki/5v9C2bFtHH5Mnn4bcGk=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
|
||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
|
||||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
|
||||
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
|
||||
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko=
|
||||
github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
|
||||
@@ -1359,7 +1310,6 @@ github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -1402,8 +1352,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
|
||||
@@ -1428,12 +1376,6 @@ github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/likexian/gokit v0.25.13 h1:p2Uw3+6fGG53CwdU2Dz0T6bOycdb2+bAFAa3ymwWVkM=
|
||||
github.com/likexian/gokit v0.25.13/go.mod h1:qQhEWFBEfqLCO3/vOEo2EDKd+EycekVtUK4tex+l2H4=
|
||||
github.com/likexian/whois v1.15.1 h1:6vTMI8n9s1eJdmcO4R9h1x99aQWIZZX1CD3am68gApU=
|
||||
github.com/likexian/whois v1.15.1/go.mod h1:/nxmQ6YXvLz+qTxC/QFtEJNAt0zLuRxJrKiWpBJX8X0=
|
||||
github.com/likexian/whois-parser v1.24.9 h1:BT6fzO3lj3F07yzVv0YXoaj+K4Ush0/cF+Yp6tvJJgk=
|
||||
github.com/likexian/whois-parser v1.24.9/go.mod h1:b6STMHHDaSKbd4PzGrP50wWE5NzeBUETa/hT9gI0G9I=
|
||||
github.com/line/line-bot-sdk-go v7.8.0+incompatible h1:Uf9/OxV0zCVfqyvwZPH8CrdiHXXmMRa/L91G3btQblQ=
|
||||
github.com/line/line-bot-sdk-go v7.8.0+incompatible/go.mod h1:0RjLjJEAU/3GIcHkC3av6O4jInAbt25nnZVmOFUgDBg=
|
||||
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:IZycmTpoUtQK3PD60UYBwjaCUHUP7cML494ao9/O8+Q=
|
||||
@@ -1445,8 +1387,6 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2
|
||||
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
|
||||
github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
|
||||
github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
|
||||
github.com/magefile/mage v1.15.1-0.20241126214340-bdc92f694516 h1:aAO0L0ulox6m/CLRYvJff+jWXYYCKGpEm3os7dM/Z+M=
|
||||
github.com/magefile/mage v1.15.1-0.20241126214340-bdc92f694516/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
||||
github.com/markbates/going v1.0.0 h1:DQw0ZP7NbNlFGcKbcE/IVSOAFzScxRtLpd0rLMzLhq0=
|
||||
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
|
||||
github.com/markbates/goth v1.82.0 h1:8j/c34AjBSTNzO7zTsOyP5IYCQCMBTRBHAbBt/PI0bQ=
|
||||
@@ -1476,13 +1416,9 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4
|
||||
github.com/mattn/go-sqlite3 v1.14.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU=
|
||||
github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microsoft/go-mssqldb v1.9.0 h1:5Vq+u2f4LDujJNeZn62Z4kBDEC9MjLv0ukRzOuEuvdA=
|
||||
github.com/microsoft/go-mssqldb v1.9.0/go.mod h1:GBbW9ASTiDC+mpgWDGKdm3FnFLTUsLYN3iFL90lQ+PA=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||
github.com/mileusna/viber v1.0.1 h1:gWB6/lKoWYVxkH0Jb8jRnGIRZ/9DEM7RBZRJHRfdYWs=
|
||||
github.com/mileusna/viber v1.0.1/go.mod h1:Pxu/iPMnYjnHgu+bEp3SiKWHWmlf/kDp/yOX8XUdYrQ=
|
||||
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
|
||||
@@ -1500,8 +1436,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modelcontextprotocol/go-sdk v1.4.0 h1:u0kr8lbJc1oBcawK7Df+/ajNMpIDFE41OEPxdeTLOn8=
|
||||
github.com/modelcontextprotocol/go-sdk v1.4.0/go.mod h1:Nxc2n+n/GdCebUaqCOhTetptS17SXXNu9IfNTaLDi1E=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -1547,8 +1481,6 @@ github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM=
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20240411101913-e07a1f0e8eb4 h1:1Kw2vDBXmjop+LclnzCb/fFy+sgb3gYARwfmoUcQe6o=
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20240411101913-e07a1f0e8eb4/go.mod h1:EHPiTAKtiFmrMldLUNswFwfZ2eJIYBHktdaUTZxYWRw=
|
||||
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
|
||||
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||
github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||
@@ -1566,8 +1498,6 @@ github.com/pingcap/tidb/parser v0.0.0-20221126021158-6b02a5d8ba7d h1:1DyyRrgYeNj
|
||||
github.com/pingcap/tidb/parser v0.0.0-20221126021158-6b02a5d8ba7d/go.mod h1:ElJiub4lRy6UZDb+0JHDkGEdr6aOli+ykhyej7VCLoI=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1-0.20161029093637-248dadf4e906/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -1579,9 +1509,8 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/polarsource/polar-go v0.12.0 h1:um+6ftOPUMg2TQq9Kv/6fKGBOAl7dOc2YiDdx4Bb0y8=
|
||||
github.com/polarsource/polar-go v0.12.0/go.mod h1:FB11Q4m2n3wIk6l/POOkz0MVOUx1o0Yt4Y97MnQfe0c=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
@@ -1602,8 +1531,8 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
|
||||
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
@@ -1625,16 +1554,12 @@ github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdk
|
||||
github.com/qiniu/go-sdk/v7 v7.12.1 h1:FZG5dhs2MZBV/mHVhmHnsgsQ+j1gSE0RqIoA2WwEDwY=
|
||||
github.com/qiniu/go-sdk/v7 v7.12.1/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFsDY0BLE+w=
|
||||
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2 h1:dq90+d51/hQRaHEqRAsQ1rE/pC1GUS4sc2rCbbFsAIY=
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/redis/go-redis/v9 v9.5.5 h1:51VEyMF8eOO+NUHFm8fpg+IOc1xFuFOhxs3R+kPu1FM=
|
||||
github.com/redis/go-redis/v9 v9.5.5/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/resend/resend-go/v3 v3.1.0 h1:bJpU5gYCDcczLdhCo37oy9mOmdtSVlOzM6IfWX9zhMw=
|
||||
github.com/resend/resend-go/v3 v3.1.0/go.mod h1:iI7VA0NoGjWvsNii5iNC5Dy0llsI3HncXPejhniYzwE=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
@@ -1652,7 +1577,6 @@ github.com/russellhaering/gosaml2 v0.9.0 h1:CNMnH42z/GirrKjdmNrSS6bAAs47F9bPdl4P
|
||||
github.com/russellhaering/gosaml2 v0.9.0/go.mod h1:byViER/1YPUa0Puj9ROZblpoq2jsE7h/CJmitzX0geU=
|
||||
github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg=
|
||||
github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
|
||||
@@ -1662,10 +1586,6 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh
|
||||
github.com/scim2/filter-parser/v2 v2.2.0 h1:QGadEcsmypxg8gYChRSM2j1edLyE/2j72j+hdmI4BJM=
|
||||
github.com/scim2/filter-parser/v2 v2.2.0/go.mod h1:jWnkDToqX/Y0ugz0P5VvpVEUKcWcyHHj+X+je9ce5JA=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc=
|
||||
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
|
||||
github.com/segmentio/encoding v0.5.3 h1:OjMgICtcSFuNvQCdwqMCv9Tg7lEOXGwm1J5RPQccx6w=
|
||||
github.com/segmentio/encoding v0.5.3/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
|
||||
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
|
||||
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
||||
github.com/sendgrid/sendgrid-go v3.16.0+incompatible h1:i8eE6IMkiCy7vusSdacHHSBUpXyTcTXy/Rl9N9aZ/Qw=
|
||||
@@ -1678,7 +1598,6 @@ github.com/shirou/gopsutil/v4 v4.25.9 h1:JImNpf6gCVhKgZhtaAHJ0serfFGtlfIlSC08eaK
|
||||
github.com/shirou/gopsutil/v4 v4.25.9/go.mod h1:gxIxoC+7nQRwUl/xNhutXlD8lq+jxTgpIkEf3rADHL8=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
|
||||
@@ -1699,9 +1618,7 @@ github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQ
|
||||
github.com/slack-go/slack v0.15.0 h1:LE2lj2y9vqqiOf+qIIy0GvEoxgF1N5yLGZffmEZykt0=
|
||||
github.com/slack-go/slack v0.15.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
|
||||
@@ -1710,8 +1627,6 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||
github.com/spyzhov/ajson v0.8.0 h1:sFXyMbi4Y/BKjrsfkUZHSjA2JM1184enheSjjoT/zCc=
|
||||
github.com/spyzhov/ajson v0.8.0/go.mod h1:63V+CGM6f1Bu/p4nLIN8885ojBdt88TbLoSFzyqMuVA=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
@@ -1720,7 +1635,6 @@ github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5J
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
@@ -1764,8 +1678,6 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/timtadh/data-structures v0.5.3/go.mod h1:9R4XODhJ8JdWFEI8P/HJKqxuJctfBQw6fDibMQny2oU=
|
||||
github.com/timtadh/lexmachine v0.2.2/go.mod h1:GBJvD5OAfRn/gnp92zb9KTgHLB7akKyxmVivoYCcjQI=
|
||||
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||
@@ -1782,13 +1694,9 @@ github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVK
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
github.com/ucloud/ucloud-sdk-go v0.22.5 h1:GIltVwMDUqQj4iPL/emsZAMhEYWjLTwZqpOxdkdDrM8=
|
||||
github.com/ucloud/ucloud-sdk-go v0.22.5/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw=
|
||||
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/utahta/go-linenotify v0.5.0 h1:E1tJaB/XhqRY/iz203FD0MaHm10DjQPOq5/Mem2A3Gs=
|
||||
github.com/utahta/go-linenotify v0.5.0/go.mod h1:KsvBXil2wx+ByaCR0e+IZKTbp4pDesc7yjzRigLf6pE=
|
||||
github.com/valllabh/ocsf-schema-golang v1.0.3 h1:eR8k/3jP/OOqB8LRCtdJ4U+vlgd/gk5y3KMXoodrsrw=
|
||||
github.com/valllabh/ocsf-schema-golang v1.0.3/go.mod h1:sZ3as9xqm1SSK5feFWIR2CuGeGRhsM7TR1MbpBctzPk=
|
||||
github.com/volcengine/volc-sdk-golang v1.0.117 h1:ykFVSwsVq9qvIoWP9jeP+VKNAUjrblAdsZl46yVWiH8=
|
||||
github.com/volcengine/volc-sdk-golang v1.0.117/go.mod h1:ojXSFvj404o2UKnZR9k9LUUWIUU+9XtlRlzk2+UFc/M=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
@@ -1804,8 +1712,6 @@ github.com/xorm-io/core v0.7.4 h1:qIznlqqmYNEb03ewzRXCrNkbbxpkgc/44nVF8yoFV7Y=
|
||||
github.com/xorm-io/core v0.7.4/go.mod h1:GueyhafDnkB0KK0fXX/dEhr/P1EAGW0GLmoNDUEE1Mo=
|
||||
github.com/xorm-io/xorm v1.1.6 h1:s4fDpUXJx8Zr/PBovXNaadn+v1P3h/U3iV4OxAkWS8s=
|
||||
github.com/xorm-io/xorm v1.1.6/go.mod h1:7nsSUdmgLIcqHSSaKOzbVQiZtzIzbpGf1GGSYp6DD70=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -1834,31 +1740,27 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.40.0 h1:Awaf8gmW99tZTOWqkLCOl6aw1/rxAWVlHsHIZ3fT2sA=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.40.0/go.mod h1:99OY9ZCqyLkzJLTh5XhECpLRSxcZl+ZDKBEO+jMBFR4=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 h1:XmiuHzgJt067+a6kwyAzkhXooYVv3/TOw9cM2VfJgUM=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0/go.mod h1:KDgtbWKTQs4bM+VPUr6WlL9m/WXcmkCcBlIzqxPGzmI=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 h1:qtFISDHKolvIxzSs0gIaiPUPR0Cucb0F2coHC7ZLdps=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0/go.mod h1:Y+Pop1Q6hCOnETWTW4NROK/q1hv50hM7yDaUTjG8lp8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
@@ -1907,7 +1809,6 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
@@ -1922,8 +1823,8 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1954,9 +1855,8 @@ golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+o
|
||||
golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 h1:TcHcE0vrmgzNH1v3ppjcMGbhG5+9fMuvOmUYwNEF4q4=
|
||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -1989,8 +1889,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20171115151908-9dfe39835686/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -2078,8 +1978,8 @@ golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -2111,8 +2011,8 @@ golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw
|
||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
|
||||
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20171101214715-fd80eb99c8f6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -2135,8 +2035,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -2262,8 +2162,8 @@ golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -2287,8 +2187,8 @@ golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -2313,8 +2213,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -2397,8 +2297,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -2411,8 +2311,6 @@ gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJ
|
||||
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
|
||||
gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0=
|
||||
gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
||||
gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
|
||||
@@ -2629,15 +2527,15 @@ google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
|
||||
google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -2679,9 +2577,11 @@ google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
|
||||
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3 h1:hUfOButuEtpc0UvYiaYRbNwxVYr0mQQOWq6X8beJ9Gc=
|
||||
google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3/go.mod h1:jzYlkSMbKypzuu6xoAEijsNVo9ZeDF1u/zCfFgsx7jg=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -2700,8 +2600,8 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
|
||||
google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
@@ -2714,17 +2614,14 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
@@ -2740,7 +2637,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -2831,7 +2727,6 @@ modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfp
|
||||
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
|
||||
modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
|
||||
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
|
||||
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
// 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 i18n
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// DuplicateInfo represents information about a duplicate key
|
||||
type DuplicateInfo struct {
|
||||
Key string
|
||||
OldPrefix string
|
||||
NewPrefix string
|
||||
OldPrefixKey string // e.g., "general:Submitter"
|
||||
NewPrefixKey string // e.g., "permission:Submitter"
|
||||
}
|
||||
|
||||
// findDuplicateKeysInJSON finds duplicate keys across the entire JSON file
|
||||
// Returns a list of duplicate information showing old and new prefix:key pairs
|
||||
// The order is determined by the order keys appear in the JSON file (git history)
|
||||
func findDuplicateKeysInJSON(filePath string) ([]DuplicateInfo, error) {
|
||||
// Read the JSON file
|
||||
fileContent, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read file %s: %w", filePath, err)
|
||||
}
|
||||
|
||||
// Track the first occurrence of each key (prefix where it was first seen)
|
||||
keyFirstPrefix := make(map[string]string)
|
||||
var duplicates []DuplicateInfo
|
||||
|
||||
// To preserve order, we need to parse the JSON with order preservation
|
||||
// We'll use a decoder to read through the top-level object
|
||||
decoder := json.NewDecoder(bytes.NewReader(fileContent))
|
||||
|
||||
// Read the opening brace of the top-level object
|
||||
token, err := decoder.Token()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read token: %w", err)
|
||||
}
|
||||
if delim, ok := token.(json.Delim); !ok || delim != '{' {
|
||||
return nil, fmt.Errorf("expected object start, got %v", token)
|
||||
}
|
||||
|
||||
// Read all namespaces in order
|
||||
for decoder.More() {
|
||||
// Read the namespace (prefix) name
|
||||
token, err := decoder.Token()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read namespace: %w", err)
|
||||
}
|
||||
|
||||
prefix, ok := token.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected string namespace, got %v", token)
|
||||
}
|
||||
|
||||
// Read the namespace object as raw message
|
||||
var namespaceData map[string]string
|
||||
if err := decoder.Decode(&namespaceData); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode namespace %s: %w", prefix, err)
|
||||
}
|
||||
|
||||
// Now check each key in this namespace
|
||||
for key := range namespaceData {
|
||||
// Check if this key was already seen in a different prefix
|
||||
if firstPrefix, exists := keyFirstPrefix[key]; exists {
|
||||
// This is a duplicate - the key exists in another prefix
|
||||
duplicates = append(duplicates, DuplicateInfo{
|
||||
Key: key,
|
||||
OldPrefix: firstPrefix,
|
||||
NewPrefix: prefix,
|
||||
OldPrefixKey: fmt.Sprintf("%s:%s", firstPrefix, key),
|
||||
NewPrefixKey: fmt.Sprintf("%s:%s", prefix, key),
|
||||
})
|
||||
} else {
|
||||
// First time seeing this key, record the prefix
|
||||
keyFirstPrefix[key] = prefix
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return duplicates, nil
|
||||
}
|
||||
|
||||
// TestDeduplicateFrontendI18n checks for duplicate i18n keys in the frontend en.json file
|
||||
func TestDeduplicateFrontendI18n(t *testing.T) {
|
||||
filePath := "../web/src/locales/en/data.json"
|
||||
|
||||
// Find duplicate keys
|
||||
duplicates, err := findDuplicateKeysInJSON(filePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to check for duplicates in frontend i18n file: %v", err)
|
||||
}
|
||||
|
||||
// Print all duplicates and fail the test if any are found
|
||||
if len(duplicates) > 0 {
|
||||
t.Errorf("Found duplicate i18n keys in frontend file (%s):", filePath)
|
||||
for _, dup := range duplicates {
|
||||
t.Errorf(" i18next.t(\"%s\") duplicates with i18next.t(\"%s\")", dup.NewPrefixKey, dup.OldPrefixKey)
|
||||
}
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeduplicateBackendI18n checks for duplicate i18n keys in the backend en.json file
|
||||
func TestDeduplicateBackendI18n(t *testing.T) {
|
||||
filePath := "../i18n/locales/en/data.json"
|
||||
|
||||
// Find duplicate keys
|
||||
duplicates, err := findDuplicateKeysInJSON(filePath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to check for duplicates in backend i18n file: %v", err)
|
||||
}
|
||||
|
||||
// Print all duplicates and fail the test if any are found
|
||||
if len(duplicates) > 0 {
|
||||
t.Errorf("Found duplicate i18n keys in backend file (%s):", filePath)
|
||||
for _, dup := range duplicates {
|
||||
t.Errorf(" i18n.Translate(\"%s\") duplicates with i18n.Translate(\"%s\")", dup.NewPrefixKey, dup.OldPrefixKey)
|
||||
}
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "Konnte den Benutzer nicht hinzufügen",
|
||||
"Get init score failed, error: %w": "Init-Score konnte nicht abgerufen werden, Fehler: %w",
|
||||
"Please sign out first": "Bitte melden Sie sich zuerst ab",
|
||||
"The application does not allow to sign up new account": "Die Anwendung erlaubt es nicht, sich für ein neues Konto anzumelden"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "Die Anmeldemethode: Anmeldung per E-Mail ist für die Anwendung nicht aktiviert",
|
||||
"The login method: login with face is not enabled for the application": "Die Anmeldemethode: Anmeldung per Gesicht ist für die Anwendung nicht aktiviert",
|
||||
"The login method: login with password is not enabled for the application": "Die Anmeldeart \"Anmeldung mit Passwort\" ist für die Anwendung nicht aktiviert",
|
||||
"The order: %s does not exist": "Die Bestellung: %s existiert nicht",
|
||||
"The organization: %s does not exist": "Die Organisation: %s existiert nicht",
|
||||
"The organization: %s has disabled users to signin": "Die Organisation: %s hat die Anmeldung von Benutzern deaktiviert",
|
||||
"The plan: %s does not exist": "Der Plan: %s existiert nicht",
|
||||
@@ -48,7 +48,7 @@
|
||||
"CIDR for IP: %s should not be empty": "CIDR für IP: %s darf nicht leer sein",
|
||||
"Default code does not match the code's matching rules": "Standardcode entspricht nicht den Übereinstimmungsregeln des Codes",
|
||||
"DisplayName cannot be blank": "Anzeigename kann nicht leer sein",
|
||||
"DisplayName is not valid real name": "Der Anzeigename ist kein gültiger echter Name",
|
||||
"DisplayName is not valid real name": "DisplayName ist kein gültiger Vorname",
|
||||
"Email already exists": "E-Mail existiert bereits",
|
||||
"Email cannot be empty": "E-Mail darf nicht leer sein",
|
||||
"Email is invalid": "E-Mail ist ungültig",
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "Gesichtsdaten stimmen nicht überein",
|
||||
"Failed to parse client IP: %s": "Fehler beim Parsen der Client-IP: %s",
|
||||
"FirstName cannot be blank": "Vorname darf nicht leer sein",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Gastbenutzer müssen ihr Konto aktualisieren, indem sie einen Benutzernamen und ein Passwort festlegen, bevor sie sich direkt anmelden können",
|
||||
"Invitation code cannot be blank": "Einladungscode darf nicht leer sein",
|
||||
"Invitation code exhausted": "Einladungscode aufgebraucht",
|
||||
"Invitation code is invalid": "Einladungscode ist ungültig",
|
||||
"Invitation code suspended": "Einladungscode ausgesetzt",
|
||||
"LDAP user name or password incorrect": "Ldap Benutzername oder Passwort falsch",
|
||||
"LastName cannot be blank": "Nachname darf nicht leer sein",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Mehrere Konten mit derselben uid, bitte überprüfen Sie Ihren LDAP-Server",
|
||||
"Organization does not exist": "Organisation existiert nicht",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Gruppen importieren fehlgeschlagen",
|
||||
"Failed to import users": "Fehler beim Importieren von Benutzern",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Unzureichendes Guthaben: neues Guthaben %v wäre unter dem Kreditlimit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Unzureichendes Guthaben: neues Organisationsguthaben %v wäre unter dem Kreditlimit %v",
|
||||
"Missing parameter": "Fehlender Parameter",
|
||||
"Only admin user can specify user": "Nur Administrator kann Benutzer angeben",
|
||||
"Please login first": "Bitte zuerst einloggen",
|
||||
"The LDAP: %s does not exist": "Das LDAP: %s existiert nicht",
|
||||
"The organization: %s should have one application at least": "Die Organisation: %s sollte mindestens eine Anwendung haben",
|
||||
"The syncer: %s does not exist": "Der Synchronizer: %s existiert nicht",
|
||||
"The user: %s doesn't exist": "Der Benutzer %s existiert nicht",
|
||||
"The user: %s is not found": "Der Benutzer: %s wurde nicht gefunden",
|
||||
"User is required for User category transaction": "Benutzer ist für Benutzer-Kategorie-Transaktionen erforderlich",
|
||||
"Wrong userId": "Falsche Benutzer-ID",
|
||||
"don't support captchaProvider: ": "Unterstütze captchaProvider nicht:",
|
||||
"this operation is not allowed in demo mode": "Dieser Vorgang ist im Demo-Modus nicht erlaubt",
|
||||
@@ -145,14 +139,8 @@
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "Die Berechtigung: \"%s\" existiert nicht"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Produktliste darf nicht leer sein"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "ID-Verifizierungsanbieter konnte nicht initialisiert werden",
|
||||
"Invalid application id": "Ungültige Anwendungs-ID",
|
||||
"No ID Verification provider configured": "Kein ID-Verifizierungsanbieter konfiguriert",
|
||||
"Provider is not an ID Verification provider": "Anbieter ist kein ID-Verifizierungsanbieter",
|
||||
"the provider: %s does not exist": "Der Anbieter %s existiert nicht"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "Ungültige E-Mail-Empfänger: %s",
|
||||
"Invalid phone receivers: %s": "Ungültige Telefonempfänger: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "Sitzungs-ID %s ist die aktuelle Sitzung und kann nicht gelöscht werden"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "Der Objektschlüssel %s ist nicht erlaubt",
|
||||
"The provider type: %s is not supported": "Der Anbieter-Typ %s wird nicht unterstützt"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "Fehler"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Ticket nicht gefunden"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s wird von dieser Anwendung nicht unterstützt",
|
||||
"Invalid application or wrong clientSecret": "Ungültige Anwendung oder falsches clientSecret",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Anzeigename darf nicht leer sein",
|
||||
"ID card information and real name are required": "Personalausweisinformationen und vollständiger Name sind erforderlich",
|
||||
"Identity verification failed": "Identitätsprüfung fehlgeschlagen",
|
||||
"MFA email is enabled but email is empty": "MFA-E-Mail ist aktiviert, aber E-Mail ist leer",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-Telefon ist aktiviert, aber Telefonnummer ist leer",
|
||||
"New password cannot contain blank space.": "Das neue Passwort darf keine Leerzeichen enthalten.",
|
||||
"No application found for user": "Keine Anwendung für Benutzer gefunden",
|
||||
"The new password must be different from your current password": "Das neue Passwort muss sich von Ihrem aktuellen Passwort unterscheiden",
|
||||
"User is already verified": "Benutzer ist bereits verifiziert",
|
||||
"the user's owner and name should not be empty": "Eigentümer und Name des Benutzers dürfen nicht leer sein"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Ungültiger Captcha-Anbieter.",
|
||||
"Phone number is invalid in your region %s": "Die Telefonnummer ist in Ihrer Region %s ungültig",
|
||||
"The forgot password feature is disabled": "Die Funktion \"Passwort vergessen\" ist deaktiviert",
|
||||
"The verification code has already been used!": "Der Verifizierungscode wurde bereits verwendet!",
|
||||
"The verification code has not been sent yet!": "Der Verifizierungscode wurde noch nicht gesendet!",
|
||||
"Turing test failed.": "Turing-Test fehlgeschlagen.",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "Failed to add user",
|
||||
"Get init score failed, error: %w": "Get init score failed, error: %w",
|
||||
"Please sign out first": "Please sign out first",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||
"The order: %s does not exist": "The order: %s does not exist",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The plan: %s does not exist": "The plan: %s does not exist",
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"Failed to parse client IP: %s": "Failed to parse client IP: %s",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Guest users must upgrade their account by setting a username and password before they can sign in directly",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
"Invitation code is invalid": "Invitation code is invalid",
|
||||
"Invitation code suspended": "Invitation code suspended",
|
||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||
"LastName cannot be blank": "LastName cannot be blank",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Failed to import groups",
|
||||
"Failed to import users": "Failed to import users",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Insufficient balance: new balance %v would be below credit limit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Insufficient balance: new organization balance %v would be below credit limit %v",
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Only admin user can specify user": "Only admin user can specify user",
|
||||
"Please login first": "Please login first",
|
||||
"The LDAP: %s does not exist": "The LDAP: %s does not exist",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The syncer: %s does not exist": "The syncer: %s does not exist",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"The user: %s is not found": "The user: %s is not found",
|
||||
"User is required for User category transaction": "User is required for User category transaction",
|
||||
"Wrong userId": "Wrong userId",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode",
|
||||
@@ -145,14 +139,8 @@
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "The permission: \"%s\" doesn't exist"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Product list cannot be empty"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Failed to initialize ID Verification provider",
|
||||
"Invalid application id": "Invalid application id",
|
||||
"No ID Verification provider configured": "No ID Verification provider configured",
|
||||
"Provider is not an ID Verification provider": "Provider is not an ID Verification provider",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
|
||||
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
|
||||
"The provider type: %s is not supported": "The provider type: %s is not supported"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "Error"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Ticket not found"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
|
||||
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Display name cannot be empty",
|
||||
"ID card information and real name are required": "ID card information and real name are required",
|
||||
"Identity verification failed": "Identity verification failed",
|
||||
"MFA email is enabled but email is empty": "MFA email is enabled but email is empty",
|
||||
"MFA phone is enabled but phone number is empty": "MFA phone is enabled but phone number is empty",
|
||||
"New password cannot contain blank space.": "New password cannot contain blank space.",
|
||||
"No application found for user": "No application found for user",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"User is already verified": "User is already verified",
|
||||
"the user's owner and name should not be empty": "the user's owner and name should not be empty"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Invalid captcha provider.",
|
||||
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
|
||||
"The forgot password feature is disabled": "The forgot password feature is disabled",
|
||||
"The verification code has already been used!": "The verification code has already been used!",
|
||||
"The verification code has not been sent yet!": "The verification code has not been sent yet!",
|
||||
"Turing test failed.": "Turing test failed.",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "No se pudo agregar el usuario",
|
||||
"Get init score failed, error: %w": "Error al obtener el puntaje de inicio, error: %w",
|
||||
"Please sign out first": "Por favor, cierra sesión primero",
|
||||
"The application does not allow to sign up new account": "La aplicación no permite registrarse con una cuenta nueva"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "El método de inicio de sesión: inicio de sesión con correo electrónico no está habilitado para la aplicación",
|
||||
"The login method: login with face is not enabled for the application": "El método de inicio de sesión: inicio de sesión con reconocimiento facial no está habilitado para la aplicación",
|
||||
"The login method: login with password is not enabled for the application": "El método de inicio de sesión: inicio de sesión con contraseña no está habilitado para la aplicación",
|
||||
"The order: %s does not exist": "El pedido: %s no existe",
|
||||
"The organization: %s does not exist": "La organización: %s no existe",
|
||||
"The organization: %s has disabled users to signin": "La organización: %s ha desactivado el inicio de sesión de usuarios",
|
||||
"The plan: %s does not exist": "El plan: %s no existe",
|
||||
@@ -35,7 +35,7 @@
|
||||
"User's tag: %s is not listed in the application's tags": "La etiqueta del usuario: %s no está incluida en las etiquetas de la aplicación",
|
||||
"UserCode Expired": "Código de usuario expirado",
|
||||
"UserCode Invalid": "Código de usuario inválido",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "El usuario de pago %s no tiene una suscripción activa o pendiente y la aplicación %s no tiene precios predeterminados",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "El usuario de pago %s no tiene una suscripción activa o pendiente y la aplicación: %s no tiene precio predeterminado",
|
||||
"the application for user %s is not found": "no se encontró la aplicación para el usuario %s",
|
||||
"the organization: %s is not found": "no se encontró la organización: %s"
|
||||
},
|
||||
@@ -44,9 +44,9 @@
|
||||
},
|
||||
"check": {
|
||||
"%s does not meet the CIDR format requirements: %s": "%s no cumple con los requisitos del formato CIDR: %s",
|
||||
"Affiliation cannot be blank": "La afiliación no puede estar vacía",
|
||||
"Affiliation cannot be blank": "Afiliación no puede estar en blanco",
|
||||
"CIDR for IP: %s should not be empty": "El CIDR para la IP: %s no debe estar vacío",
|
||||
"Default code does not match the code's matching rules": "El código predeterminado no cumple con las reglas de validación del código",
|
||||
"Default code does not match the code's matching rules": "El código predeterminado no coincide con las reglas de coincidencia de códigos",
|
||||
"DisplayName cannot be blank": "El nombre de visualización no puede estar en blanco",
|
||||
"DisplayName is not valid real name": "El nombre de pantalla no es un nombre real válido",
|
||||
"Email already exists": "El correo electrónico ya existe",
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "Los datos faciales no coinciden",
|
||||
"Failed to parse client IP: %s": "Error al analizar la IP del cliente: %s",
|
||||
"FirstName cannot be blank": "El nombre no puede estar en blanco",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Los usuarios invitados deben actualizar su cuenta configurando un nombre de usuario y una contraseña antes de poder iniciar sesión directamente",
|
||||
"Invitation code cannot be blank": "El código de invitación no puede estar vacío",
|
||||
"Invitation code exhausted": "Código de invitación agotado",
|
||||
"Invitation code is invalid": "Código de invitación inválido",
|
||||
"Invitation code suspended": "Código de invitación suspendido",
|
||||
"LDAP user name or password incorrect": "Nombre de usuario o contraseña de Ldap incorrectos",
|
||||
"LastName cannot be blank": "El apellido no puede estar en blanco",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Cuentas múltiples con el mismo uid, por favor revise su servidor ldap",
|
||||
"Organization does not exist": "La organización no existe",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Error al importar grupos",
|
||||
"Failed to import users": "Error al importar usuarios",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Saldo insuficiente: el nuevo saldo %v estaría por debajo del límite de crédito %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Saldo insuficiente: el nuevo saldo de la organización %v estaría por debajo del límite de crédito %v",
|
||||
"Missing parameter": "Parámetro faltante",
|
||||
"Only admin user can specify user": "Solo el usuario administrador puede especificar usuario",
|
||||
"Please login first": "Por favor, inicia sesión primero",
|
||||
"The LDAP: %s does not exist": "El LDAP: %s no existe",
|
||||
"The organization: %s should have one application at least": "La organización: %s debe tener al menos una aplicación",
|
||||
"The syncer: %s does not exist": "El sincronizador: %s no existe",
|
||||
"The user: %s doesn't exist": "El usuario: %s no existe",
|
||||
"The user: %s is not found": "El usuario: %s no encontrado",
|
||||
"User is required for User category transaction": "El usuario es obligatorio para la transacción de la categoría Usuario",
|
||||
"Wrong userId": "ID de usuario incorrecto",
|
||||
"don't support captchaProvider: ": "No apoyo a captchaProvider",
|
||||
"this operation is not allowed in demo mode": "esta operación no está permitida en modo de demostración",
|
||||
@@ -145,14 +139,8 @@
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "El permiso: \"%s\" no existe"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "La lista de productos no puede estar vacía"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Error al inicializar el proveedor de verificación de ID",
|
||||
"Invalid application id": "Identificación de aplicación no válida",
|
||||
"No ID Verification provider configured": "No hay proveedor de verificación de ID configurado",
|
||||
"Provider is not an ID Verification provider": "El proveedor no es un proveedor de verificación de ID",
|
||||
"the provider: %s does not exist": "El proveedor: %s no existe"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "Receptores de correo electrónico no válidos: %s",
|
||||
"Invalid phone receivers: %s": "Receptores de teléfono no válidos: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "El objectKey: %s no está permitido",
|
||||
"The provider type: %s is not supported": "El tipo de proveedor: %s no es compatible"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "Error"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Ticket no encontrado"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "El tipo de subvención: %s no es compatible con esta aplicación",
|
||||
"Invalid application or wrong clientSecret": "Solicitud inválida o clientSecret incorrecto",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "El nombre de pantalla no puede estar vacío",
|
||||
"ID card information and real name are required": "Se requiere información de la tarjeta de identificación y el nombre real",
|
||||
"Identity verification failed": "Falló la verificación de identidad",
|
||||
"MFA email is enabled but email is empty": "El correo electrónico MFA está habilitado pero el correo está vacío",
|
||||
"MFA phone is enabled but phone number is empty": "El teléfono MFA está habilitado pero el número de teléfono está vacío",
|
||||
"New password cannot contain blank space.": "La nueva contraseña no puede contener espacios en blanco.",
|
||||
"No application found for user": "No se encontró aplicación para el usuario",
|
||||
"The new password must be different from your current password": "La nueva contraseña debe ser diferente de su contraseña actual",
|
||||
"User is already verified": "El usuario ya está verificado",
|
||||
"the user's owner and name should not be empty": "el propietario y el nombre del usuario no deben estar vacíos"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Proveedor de captcha no válido.",
|
||||
"Phone number is invalid in your region %s": "El número de teléfono es inválido en tu región %s",
|
||||
"The forgot password feature is disabled": "La función de contraseña olvidada está deshabilitada",
|
||||
"The verification code has already been used!": "¡El código de verificación ya ha sido utilizado!",
|
||||
"The verification code has not been sent yet!": "¡El código de verificación aún no ha sido enviado!",
|
||||
"Turing test failed.": "El test de Turing falló.",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "Échec d'ajout d'utilisateur",
|
||||
"Get init score failed, error: %w": "Obtention du score initiale échouée, erreur : %w",
|
||||
"Please sign out first": "Veuillez vous déconnecter en premier",
|
||||
"The application does not allow to sign up new account": "L'application ne permet pas de créer un nouveau compte"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "La méthode de connexion : connexion par e-mail n'est pas activée pour l'application",
|
||||
"The login method: login with face is not enabled for the application": "La méthode de connexion : connexion par visage n'est pas activée pour l'application",
|
||||
"The login method: login with password is not enabled for the application": "La méthode de connexion : connexion avec mot de passe n'est pas activée pour l'application",
|
||||
"The order: %s does not exist": "La commande : %s n'existe pas",
|
||||
"The organization: %s does not exist": "L'organisation : %s n'existe pas",
|
||||
"The organization: %s has disabled users to signin": "L'organisation: %s a désactivé la connexion des utilisateurs",
|
||||
"The plan: %s does not exist": "Le plan : %s n'existe pas",
|
||||
@@ -35,7 +35,7 @@
|
||||
"User's tag: %s is not listed in the application's tags": "Le tag de l'utilisateur : %s n'est pas répertorié dans les tags de l'application",
|
||||
"UserCode Expired": "Code utilisateur expiré",
|
||||
"UserCode Invalid": "Code utilisateur invalide",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "L'utilisateur payant %s n'a pas d'abonnement actif ou en attente et l'application %s n'a pas de tarification par défaut",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "L'utilisateur payant %s n'a pas d'abonnement actif ou en attente et l'application : %s n'a pas de tarification par défaut",
|
||||
"the application for user %s is not found": "L'application pour l'utilisateur %s est introuvable",
|
||||
"the organization: %s is not found": "L'organisation : %s est introuvable"
|
||||
},
|
||||
@@ -44,9 +44,9 @@
|
||||
},
|
||||
"check": {
|
||||
"%s does not meet the CIDR format requirements: %s": "%s ne respecte pas les exigences du format CIDR : %s",
|
||||
"Affiliation cannot be blank": "L'affiliation ne peut pas être vide",
|
||||
"Affiliation cannot be blank": "Affiliation ne peut pas être vide",
|
||||
"CIDR for IP: %s should not be empty": "Le CIDR pour l'IP : %s ne doit pas être vide",
|
||||
"Default code does not match the code's matching rules": "Le code par défaut ne respecte pas les règles de validation du code",
|
||||
"Default code does not match the code's matching rules": "Le code par défaut ne correspond pas aux règles de correspondance du code",
|
||||
"DisplayName cannot be blank": "Le nom d'affichage ne peut pas être vide",
|
||||
"DisplayName is not valid real name": "DisplayName n'est pas un nom réel valide",
|
||||
"Email already exists": "E-mail déjà existant",
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "Données faciales incorrectes",
|
||||
"Failed to parse client IP: %s": "Échec de l'analyse de l'IP client : %s",
|
||||
"FirstName cannot be blank": "Le prénom ne peut pas être laissé vide",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Les utilisateurs invités doivent mettre à niveau leur compte en définissant un nom d'utilisateur et un mot de passe avant de pouvoir se connecter directement",
|
||||
"Invitation code cannot be blank": "Le code d'invitation ne peut pas être vide",
|
||||
"Invitation code exhausted": "Code d'invitation épuisé",
|
||||
"Invitation code is invalid": "Code d'invitation invalide",
|
||||
"Invitation code suspended": "Code d'invitation suspendu",
|
||||
"LDAP user name or password incorrect": "Nom d'utilisateur ou mot de passe LDAP incorrect",
|
||||
"LastName cannot be blank": "Le nom de famille ne peut pas être vide",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Plusieurs comptes avec le même identifiant d'utilisateur, veuillez vérifier votre serveur LDAP",
|
||||
"Organization does not exist": "L'organisation n'existe pas",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Échec de l'importation des groupes",
|
||||
"Failed to import users": "Échec de l'importation des utilisateurs",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Solde insuffisant : le nouveau solde %v serait inférieur à la limite de crédit %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Solde insuffisant : le nouveau solde de l'organisation %v serait inférieur à la limite de crédit %v",
|
||||
"Missing parameter": "Paramètre manquant",
|
||||
"Only admin user can specify user": "Seul un administrateur peut désigner un utilisateur",
|
||||
"Please login first": "Veuillez d'abord vous connecter",
|
||||
"The LDAP: %s does not exist": "Le LDAP : %s n'existe pas",
|
||||
"The organization: %s should have one application at least": "L'organisation : %s doit avoir au moins une application",
|
||||
"The syncer: %s does not exist": "Le synchroniseur : %s n'existe pas",
|
||||
"The user: %s doesn't exist": "L'utilisateur : %s n'existe pas",
|
||||
"The user: %s is not found": "L'utilisateur : %s est introuvable",
|
||||
"User is required for User category transaction": "L'utilisateur est requis pour la transaction de catégorie Utilisateur",
|
||||
"Wrong userId": "ID utilisateur incorrect",
|
||||
"don't support captchaProvider: ": "ne prend pas en charge captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "cette opération n'est pas autorisée en mode démo",
|
||||
@@ -145,14 +139,8 @@
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "La permission : \"%s\" n'existe pas"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "La liste des produits ne peut pas être vide"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Échec de l'initialisation du fournisseur de vérification d'identité",
|
||||
"Invalid application id": "Identifiant d'application invalide",
|
||||
"No ID Verification provider configured": "Aucun fournisseur de vérification d'identité configuré",
|
||||
"Provider is not an ID Verification provider": "Le fournisseur n'est pas un fournisseur de vérification d'identité",
|
||||
"the provider: %s does not exist": "Le fournisseur : %s n'existe pas"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "Destinataires d'e-mail invalides : %s",
|
||||
"Invalid phone receivers: %s": "Destinataires de téléphone invalide : %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "session id %s is the current session and cannot be deleted"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "La clé d'objet : %s n'est pas autorisée",
|
||||
"The provider type: %s is not supported": "Le type de fournisseur : %s n'est pas pris en charge"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "Erreur"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Ticket introuvable"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Type_de_subvention : %s n'est pas pris en charge dans cette application",
|
||||
"Invalid application or wrong clientSecret": "Application invalide ou clientSecret incorrect",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Le nom d'affichage ne peut pas être vide",
|
||||
"ID card information and real name are required": "Les informations de la carte d'identité et le nom réel sont requis",
|
||||
"Identity verification failed": "Échec de la vérification d'identité",
|
||||
"MFA email is enabled but email is empty": "L'authentification MFA par e-mail est activée mais l'e-mail est vide",
|
||||
"MFA phone is enabled but phone number is empty": "L'authentification MFA par téléphone est activée mais le numéro de téléphone est vide",
|
||||
"New password cannot contain blank space.": "Le nouveau mot de passe ne peut pas contenir d'espace.",
|
||||
"No application found for user": "Aucune application trouvée pour l'utilisateur",
|
||||
"The new password must be different from your current password": "Le nouveau mot de passe doit être différent de votre mot de passe actuel",
|
||||
"User is already verified": "L'utilisateur est déjà vérifié",
|
||||
"the user's owner and name should not be empty": "le propriétaire et le nom de l'utilisateur ne doivent pas être vides"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Fournisseur de captcha invalide.",
|
||||
"Phone number is invalid in your region %s": "Le numéro de téléphone n'est pas valide dans votre région %s",
|
||||
"The forgot password feature is disabled": "La fonction de mot de passe oublié est désactivée",
|
||||
"The verification code has already been used!": "Le code de vérification a déjà été utilisé !",
|
||||
"The verification code has not been sent yet!": "Le code de vérification n'a pas encore été envoyé !",
|
||||
"Turing test failed.": "Le test de Turing a échoué.",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "ユーザーの追加に失敗しました",
|
||||
"Get init score failed, error: %w": "イニットスコアの取得に失敗しました。エラー:%w",
|
||||
"Please sign out first": "最初にサインアウトしてください",
|
||||
"The application does not allow to sign up new account": "アプリケーションは新しいアカウントの登録を許可しません"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "このアプリケーションではメールログインは有効になっていません",
|
||||
"The login method: login with face is not enabled for the application": "このアプリケーションでは顔認証ログインは有効になっていません",
|
||||
"The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません",
|
||||
"The order: %s does not exist": "注文:%s は存在しません",
|
||||
"The organization: %s does not exist": "組織「%s」は存在しません",
|
||||
"The organization: %s has disabled users to signin": "組織: %s はユーザーのサインインを無効にしました",
|
||||
"The plan: %s does not exist": "プラン: %sは存在しません",
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "顔認証データが一致しません",
|
||||
"Failed to parse client IP: %s": "クライアント IP「%s」の解析に失敗しました",
|
||||
"FirstName cannot be blank": "ファーストネームは空白にできません",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "ゲストユーザーは直接サインインする前に、ユーザー名とパスワードを設定してアカウントをアップグレードする必要があります",
|
||||
"Invitation code cannot be blank": "招待コードは空にできません",
|
||||
"Invitation code exhausted": "招待コードの使用回数が上限に達しました",
|
||||
"Invitation code is invalid": "招待コードが無効です",
|
||||
"Invitation code suspended": "招待コードは一時的に無効化されています",
|
||||
"LDAP user name or password incorrect": "Ldapのユーザー名またはパスワードが間違っています",
|
||||
"LastName cannot be blank": "姓は空白にできません",
|
||||
"Multiple accounts with same uid, please check your ldap server": "同じuidを持つ複数のアカウントがあります。あなたのLDAPサーバーを確認してください",
|
||||
"Organization does not exist": "組織は存在しません",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "グループのインポートに失敗しました",
|
||||
"Failed to import users": "ユーザーのインポートに失敗しました",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "残高不足:新しい残高 %v がクレジット制限 %v を下回ります",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "残高不足:新しい組織残高 %v がクレジット制限 %v を下回ります",
|
||||
"Missing parameter": "不足しているパラメーター",
|
||||
"Only admin user can specify user": "管理者ユーザーのみがユーザーを指定できます",
|
||||
"Please login first": "最初にログインしてください",
|
||||
"The LDAP: %s does not exist": "LDAP:%s は存在しません",
|
||||
"The organization: %s should have one application at least": "組織「%s」は少なくとも1つのアプリケーションを持っている必要があります",
|
||||
"The syncer: %s does not exist": "同期装置:%s は存在しません",
|
||||
"The user: %s doesn't exist": "そのユーザー:%sは存在しません",
|
||||
"The user: %s is not found": "ユーザー:%s が見つかりません",
|
||||
"User is required for User category transaction": "ユーザーカテゴリトランザクションにはユーザーが必要です",
|
||||
"Wrong userId": "無効なユーザーIDです",
|
||||
"don't support captchaProvider: ": "captchaProviderをサポートしないでください",
|
||||
"this operation is not allowed in demo mode": "この操作はデモモードでは許可されていません",
|
||||
@@ -145,14 +139,8 @@
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "権限「%s」は存在しません"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "商品リストは空にできません"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "ID認証プロバイダーの初期化に失敗しました",
|
||||
"Invalid application id": "アプリケーションIDが無効です",
|
||||
"No ID Verification provider configured": "ID認証プロバイダーが設定されていません",
|
||||
"Provider is not an ID Verification provider": "プロバイダーはID認証プロバイダーではありません",
|
||||
"the provider: %s does not exist": "プロバイダー%sは存在しません"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "無効な電子メール受信者:%s",
|
||||
"Invalid phone receivers: %s": "電話受信者が無効です:%s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "セッションID %s は現在のセッションであり、削除できません"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "オブジェクトキー %s は許可されていません",
|
||||
"The provider type: %s is not supported": "プロバイダータイプ:%sはサポートされていません"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "エラー"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "チケットが見つかりません"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "grant_type:%sはこのアプリケーションでサポートされていません",
|
||||
"Invalid application or wrong clientSecret": "無効なアプリケーションまたは誤ったクライアントシークレットです",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "表示名は空にできません",
|
||||
"ID card information and real name are required": "身分証明書の情報と実名が必要です",
|
||||
"Identity verification failed": "身元確認に失敗しました",
|
||||
"MFA email is enabled but email is empty": "MFA メールが有効になっていますが、メールアドレスが空です",
|
||||
"MFA phone is enabled but phone number is empty": "MFA 電話番号が有効になっていますが、電話番号が空です",
|
||||
"New password cannot contain blank space.": "新しいパスワードにはスペースを含めることはできません。",
|
||||
"No application found for user": "ユーザーのアプリケーションが見つかりません",
|
||||
"The new password must be different from your current password": "新しいパスワードは現在のパスワードと異なる必要があります",
|
||||
"User is already verified": "ユーザーは既に認証済みです",
|
||||
"the user's owner and name should not be empty": "ユーザーのオーナーと名前は空にできません"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "無効なCAPTCHAプロバイダー。",
|
||||
"Phone number is invalid in your region %s": "電話番号はあなたの地域で無効です %s",
|
||||
"The forgot password feature is disabled": "パスワードを忘れた機能は無効になっています",
|
||||
"The verification code has already been used!": "この検証コードは既に使用されています!",
|
||||
"The verification code has not been sent yet!": "検証コードはまだ送信されていません!",
|
||||
"Turing test failed.": "チューリングテストは失敗しました。",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "Nie udało się dodać użytkownika",
|
||||
"Get init score failed, error: %w": "Pobranie początkowego wyniku nie powiodło się, błąd: %w",
|
||||
"Please sign out first": "Najpierw się wyloguj",
|
||||
"The application does not allow to sign up new account": "Aplikacja nie pozwala na rejestrację nowego konta"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "Metoda logowania: logowanie przez email nie jest włączona dla aplikacji",
|
||||
"The login method: login with face is not enabled for the application": "Metoda logowania: logowanie przez twarz nie jest włączona dla aplikacji",
|
||||
"The login method: login with password is not enabled for the application": "Metoda logowania: logowanie przez hasło nie jest włączone dla aplikacji",
|
||||
"The order: %s does not exist": "Zamówienie: %s nie istnieje",
|
||||
"The organization: %s does not exist": "Organizacja: %s nie istnieje",
|
||||
"The organization: %s has disabled users to signin": "Organizacja: %s wyłączyła logowanie użytkowników",
|
||||
"The plan: %s does not exist": "Plan: %s nie istnieje",
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "Niezgodność danych twarzy",
|
||||
"Failed to parse client IP: %s": "Nie udało się przeanalizować IP klienta: %s",
|
||||
"FirstName cannot be blank": "Imię nie może być puste",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Użytkownicy-goście muszą uaktualnić swoje konto, ustawiając nazwę użytkownika i hasło, zanim będą mogli się zalogować bezpośrednio",
|
||||
"Invitation code cannot be blank": "Kod zaproszenia nie może być pusty",
|
||||
"Invitation code exhausted": "Kod zaproszenia został wykorzystany",
|
||||
"Invitation code is invalid": "Kod zaproszenia jest nieprawidłowy",
|
||||
"Invitation code suspended": "Kod zaproszenia został zawieszony",
|
||||
"LDAP user name or password incorrect": "Nazwa użytkownika LDAP lub hasło jest nieprawidłowe",
|
||||
"LastName cannot be blank": "Nazwisko nie może być puste",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Wiele kont z tym samym uid, sprawdź swój serwer ldap",
|
||||
"Organization does not exist": "Organizacja nie istnieje",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Nie udało się zaimportować grup",
|
||||
"Failed to import users": "Nie udało się zaimportować użytkowników",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Niewystarczające saldo: nowe saldo %v byłoby poniżej limitu kredytowego %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Niewystarczające saldo: nowe saldo organizacji %v byłoby poniżej limitu kredytowego %v",
|
||||
"Missing parameter": "Brakujący parametr",
|
||||
"Only admin user can specify user": "Tylko administrator może wskazać użytkownika",
|
||||
"Please login first": "Najpierw się zaloguj",
|
||||
"The LDAP: %s does not exist": "LDAP: %s nie istnieje",
|
||||
"The organization: %s should have one application at least": "Organizacja: %s powinna mieć co najmniej jedną aplikację",
|
||||
"The syncer: %s does not exist": "Synchronizer: %s nie istnieje",
|
||||
"The user: %s doesn't exist": "Użytkownik: %s nie istnieje",
|
||||
"The user: %s is not found": "Użytkownik: %s nie został znaleziony",
|
||||
"User is required for User category transaction": "Użytkownik jest wymagany do transakcji kategorii użytkownika",
|
||||
"Wrong userId": "Nieprawidłowy userId",
|
||||
"don't support captchaProvider: ": "nie obsługuje captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "ta operacja nie jest dozwolona w trybie demo",
|
||||
@@ -145,14 +139,8 @@
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "Uprawnienie: \"%s\" nie istnieje"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Lista produktów nie może być pusta"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Nie udało się zainicjować dostawcy weryfikacji ID",
|
||||
"Invalid application id": "Nieprawidłowe id aplikacji",
|
||||
"No ID Verification provider configured": "Brak skonfigurowanego dostawcy weryfikacji ID",
|
||||
"Provider is not an ID Verification provider": "Dostawca nie jest dostawcą weryfikacji ID",
|
||||
"the provider: %s does not exist": "dostawca: %s nie istnieje"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "Nieprawidłowi odbiorcy email: %s",
|
||||
"Invalid phone receivers: %s": "Nieprawidłowi odbiorcy telefonu: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "identyfikator sesji %s jest bieżącą sesją i nie może być usunięty"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "Klucz obiektu: %s jest niedozwolony",
|
||||
"The provider type: %s is not supported": "Typ dostawcy: %s nie jest obsługiwany"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "Błąd"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Nie znaleziono biletu"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s nie jest obsługiwany w tej aplikacji",
|
||||
"Invalid application or wrong clientSecret": "Nieprawidłowa aplikacja lub błędny clientSecret",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Nazwa wyświetlana nie może być pusta",
|
||||
"ID card information and real name are required": "Wymagane są informacje z dowodu osobistego i prawdziwe nazwisko",
|
||||
"Identity verification failed": "Weryfikacja tożsamości nie powiodła się",
|
||||
"MFA email is enabled but email is empty": "MFA email jest włączone, ale email jest pusty",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefon jest włączony, ale numer telefonu jest pusty",
|
||||
"New password cannot contain blank space.": "Nowe hasło nie może zawierać spacji.",
|
||||
"No application found for user": "Nie znaleziono aplikacji dla użytkownika",
|
||||
"The new password must be different from your current password": "Nowe hasło musi różnić się od obecnego hasła",
|
||||
"User is already verified": "Użytkownik jest już zweryfikowany",
|
||||
"the user's owner and name should not be empty": "właściciel i nazwa użytkownika nie powinny być puste"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Nieprawidłowy dostawca captcha.",
|
||||
"Phone number is invalid in your region %s": "Numer telefonu jest nieprawidłowy w twoim regionie %s",
|
||||
"The forgot password feature is disabled": "Funkcja \"Zapomniałem hasła\" jest wyłączona",
|
||||
"The verification code has already been used!": "Kod weryfikacyjny został już wykorzystany!",
|
||||
"The verification code has not been sent yet!": "Kod weryfikacyjny nie został jeszcze wysłany!",
|
||||
"Turing test failed.": "Test Turinga nie powiódł się.",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "Falha ao adicionar usuário",
|
||||
"Get init score failed, error: %w": "Falha ao obter pontuação inicial, erro: %w",
|
||||
"Please sign out first": "Por favor, saia primeiro",
|
||||
"The application does not allow to sign up new account": "O aplicativo não permite a criação de novas contas"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "O método de login com e-mail não está habilitado para o aplicativo",
|
||||
"The login method: login with face is not enabled for the application": "O método de login com reconhecimento facial não está habilitado para o aplicativo",
|
||||
"The login method: login with password is not enabled for the application": "O método de login com senha não está habilitado para o aplicativo",
|
||||
"The order: %s does not exist": "O pedido: %s não existe",
|
||||
"The organization: %s does not exist": "A organização: %s não existe",
|
||||
"The organization: %s has disabled users to signin": "A organização: %s desativou o login de usuários",
|
||||
"The plan: %s does not exist": "O plano: %s não existe",
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "Dados faciais não correspondem",
|
||||
"Failed to parse client IP: %s": "Falha ao analisar o IP do cliente: %s",
|
||||
"FirstName cannot be blank": "O primeiro nome não pode estar em branco",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Usuários convidados devem atualizar suas contas definindo um nome de usuário e senha antes de poderem entrar diretamente",
|
||||
"Invitation code cannot be blank": "O código de convite não pode estar em branco",
|
||||
"Invitation code exhausted": "O código de convite foi esgotado",
|
||||
"Invitation code is invalid": "Código de convite inválido",
|
||||
"Invitation code suspended": "Código de convite suspenso",
|
||||
"LDAP user name or password incorrect": "Nome de usuário ou senha LDAP incorretos",
|
||||
"LastName cannot be blank": "O sobrenome não pode estar em branco",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Múltiplas contas com o mesmo uid, verifique seu servidor LDAP",
|
||||
"Organization does not exist": "A organização não existe",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Falha ao importar grupos",
|
||||
"Failed to import users": "Falha ao importar usuários",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Saldo insuficiente: o novo saldo %v estaria abaixo do limite de crédito %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Saldo insuficiente: o novo saldo da organização %v estaria abaixo do limite de crédito %v",
|
||||
"Missing parameter": "Parâmetro ausente",
|
||||
"Only admin user can specify user": "Apenas um administrador pode especificar um usuário",
|
||||
"Please login first": "Por favor, faça login primeiro",
|
||||
"The LDAP: %s does not exist": "O LDAP: %s não existe",
|
||||
"The organization: %s should have one application at least": "A organização: %s deve ter pelo menos um aplicativo",
|
||||
"The syncer: %s does not exist": "O sincronizador: %s não existe",
|
||||
"The user: %s doesn't exist": "O usuário: %s não existe",
|
||||
"The user: %s is not found": "O usuário: %s não foi encontrado",
|
||||
"User is required for User category transaction": "Usuário é obrigatório para transação de categoria de usuário",
|
||||
"Wrong userId": "ID de usuário incorreto",
|
||||
"don't support captchaProvider: ": "captchaProvider não suportado: ",
|
||||
"this operation is not allowed in demo mode": "esta operação não é permitida no modo de demonstração",
|
||||
@@ -145,14 +139,8 @@
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "A permissão: \"%s\" não existe"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "A lista de produtos não pode estar vazia"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Falha ao inicializar provedor de verificação de ID",
|
||||
"Invalid application id": "ID de aplicativo inválido",
|
||||
"No ID Verification provider configured": "Nenhum provedor de verificação de ID configurado",
|
||||
"Provider is not an ID Verification provider": "Provedor não é um provedor de verificação de ID",
|
||||
"the provider: %s does not exist": "O provedor: %s não existe"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "Destinatários de e-mail inválidos: %s",
|
||||
"Invalid phone receivers: %s": "Destinatários de telefone inválidos: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "ID da sessão %s é a sessão atual e não pode ser excluída"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "A chave de objeto: %s não é permitida",
|
||||
"The provider type: %s is not supported": "O tipo de provedor: %s não é suportado"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "Erro"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Ticket não encontrado"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s não é suportado neste aplicativo",
|
||||
"Invalid application or wrong clientSecret": "Aplicativo inválido ou clientSecret incorreto",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "O nome de exibição não pode estar vazio",
|
||||
"ID card information and real name are required": "Informações do documento de identidade e nome verdadeiro são obrigatórios",
|
||||
"Identity verification failed": "Falha na verificação de identidade",
|
||||
"MFA email is enabled but email is empty": "MFA por e-mail está habilitado, mas o e-mail está vazio",
|
||||
"MFA phone is enabled but phone number is empty": "MFA por telefone está habilitado, mas o número de telefone está vazio",
|
||||
"New password cannot contain blank space.": "A nova senha não pode conter espaços em branco.",
|
||||
"No application found for user": "Nenhum aplicativo encontrado para o usuário",
|
||||
"The new password must be different from your current password": "A nova senha deve ser diferente da senha atual",
|
||||
"User is already verified": "Usuário já está verificado",
|
||||
"the user's owner and name should not be empty": "O proprietário e o nome do usuário não devem estar vazios"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Provedor de captcha inválido.",
|
||||
"Phone number is invalid in your region %s": "Número de telefone inválido na sua região %s",
|
||||
"The forgot password feature is disabled": "A funcionalidade de esqueci a senha está desabilitada",
|
||||
"The verification code has already been used!": "O código de verificação já foi utilizado!",
|
||||
"The verification code has not been sent yet!": "O código de verificação ainda não foi enviado!",
|
||||
"Turing test failed.": "O teste de Turing falhou.",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "Kullanıcı eklenemedi",
|
||||
"Get init score failed, error: %w": "Başlangıç puanı alınamadı, hata: %w",
|
||||
"Please sign out first": "Lütfen önce çıkış yapın",
|
||||
"The application does not allow to sign up new account": "Uygulama yeni hesap kaydına izin vermiyor"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "Uygulama için e-posta ile giriş yöntemi etkin değil",
|
||||
"The login method: login with face is not enabled for the application": "Uygulama için yüz ile giriş yöntemi etkin değil",
|
||||
"The login method: login with password is not enabled for the application": "Şifre ile giriş yöntemi bu uygulama için etkin değil",
|
||||
"The order: %s does not exist": "Sipariş: %s mevcut değil",
|
||||
"The organization: %s does not exist": "Organizasyon: %s mevcut değil",
|
||||
"The organization: %s has disabled users to signin": "Organizasyon: %s kullanıcıların oturum açmasını devre dışı bıraktı",
|
||||
"The plan: %s does not exist": "Plan: %s mevcut değil",
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "Yüz verisi uyuşmazlığı",
|
||||
"Failed to parse client IP: %s": "İstemci IP'si ayrıştırılamadı: %s",
|
||||
"FirstName cannot be blank": "Ad boş olamaz",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Misafir kullanıcılar doğrudan giriş yapabilmek için kullanıcı adı ve şifre belirleyerek hesaplarını yükseltmelidir",
|
||||
"Invitation code cannot be blank": "Davet kodu boş olamaz",
|
||||
"Invitation code exhausted": "Davet kodu kullanım dışı",
|
||||
"Invitation code is invalid": "Davet kodu geçersiz",
|
||||
"Invitation code suspended": "Davet kodu askıya alındı",
|
||||
"LDAP user name or password incorrect": "LDAP kullanıcı adı veya şifre yanlış",
|
||||
"LastName cannot be blank": "Soyad boş olamaz",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Aynı uid'ye sahip birden fazla hesap, lütfen ldap sunucunuzu kontrol edin",
|
||||
"Organization does not exist": "Organizasyon bulunamadı",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Gruplar içe aktarılamadı",
|
||||
"Failed to import users": "Kullanıcılar içe aktarılamadı",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Yetersiz bakiye: yeni bakiye %v kredi limitinin altında olacak %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Yetersiz bakiye: yeni organizasyon bakiyesi %v kredi limitinin altında olacak %v",
|
||||
"Missing parameter": "Eksik parametre",
|
||||
"Only admin user can specify user": "Yalnızca yönetici kullanıcı kullanıcı belirleyebilir",
|
||||
"Please login first": "Lütfen önce giriş yapın",
|
||||
"The LDAP: %s does not exist": "LDAP: %s mevcut değil",
|
||||
"The organization: %s should have one application at least": "Organizasyon: %s en az bir uygulamaya sahip olmalı",
|
||||
"The syncer: %s does not exist": "Senkronizasyon: %s mevcut değil",
|
||||
"The user: %s doesn't exist": "Kullanıcı: %s bulunamadı",
|
||||
"The user: %s is not found": "Kullanıcı: %s bulunamadı",
|
||||
"User is required for User category transaction": "Kullanıcı kategorisi işlemi için kullanıcı gerekli",
|
||||
"Wrong userId": "Yanlış kullanıcı kimliği",
|
||||
"don't support captchaProvider: ": "captchaProvider desteklenmiyor: ",
|
||||
"this operation is not allowed in demo mode": "bu işlem demo modunda izin verilmiyor",
|
||||
@@ -145,14 +139,8 @@
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "İzin: \"%s\" mevcut değil"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Ürün listesi boş olamaz"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Kimlik Doğrulama sağlayıcısı başlatılamadı",
|
||||
"Invalid application id": "Geçersiz uygulama id",
|
||||
"No ID Verification provider configured": "Kimlik Doğrulama sağlayıcısı yapılandırılmamış",
|
||||
"Provider is not an ID Verification provider": "Sağlayıcı bir Kimlik Doğrulama sağlayıcısı değil",
|
||||
"the provider: %s does not exist": "provider: %s bulunamadı"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "Geçersiz e-posta alıcıları: %s",
|
||||
"Invalid phone receivers: %s": "Geçersiz telefon alıcıları: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "oturum kimliği %s geçerli oturumdur ve silinemez"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "objectKey: %s izin verilmiyor",
|
||||
"The provider type: %s is not supported": "provider türü: %s desteklenmiyor"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "Hata"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Bilet bulunamadı"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s bu uygulamada desteklenmiyor",
|
||||
"Invalid application or wrong clientSecret": "Geçersiz uygulama veya yanlış clientSecret",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Görünen ad boş olamaz",
|
||||
"ID card information and real name are required": "Kimlik kartı bilgileri ve gerçek adı gereklidir",
|
||||
"Identity verification failed": "Kimlik doğrulama başarısız",
|
||||
"MFA email is enabled but email is empty": "MFA e-postası etkin ancak e-posta boş",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefonu etkin ancak telefon numarası boş",
|
||||
"New password cannot contain blank space.": "Yeni şifre boşluk içeremez.",
|
||||
"No application found for user": "Kullanıcı için uygulama bulunamadı",
|
||||
"The new password must be different from your current password": "Yeni şifre mevcut şifrenizden farklı olmalıdır",
|
||||
"User is already verified": "Kullanıcı zaten doğrulanmış",
|
||||
"the user's owner and name should not be empty": "kullanıcının sahibi ve adı boş olmamalıdır"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Geçersiz captcha sağlayıcı.",
|
||||
"Phone number is invalid in your region %s": "Telefon numaranız bölgenizde geçersiz %s",
|
||||
"The forgot password feature is disabled": "Şifremi unuttum özelliği devre dışı",
|
||||
"The verification code has already been used!": "Doğrulama kodu zaten kullanılmış!",
|
||||
"The verification code has not been sent yet!": "Doğrulama kodu henüz gönderilmedi!",
|
||||
"Turing test failed.": "Turing testi başarısız.",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "Не вдалося додати користувача",
|
||||
"Get init score failed, error: %w": "Не вдалося отримати початковий бал, помилка: %w",
|
||||
"Please sign out first": "Спочатку вийдіть із системи",
|
||||
"The application does not allow to sign up new account": "Додаток не дозволяє реєструвати нові облікові записи"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "Метод входу через email не увімкнено для цього додатка",
|
||||
"The login method: login with face is not enabled for the application": "Метод входу через обличчя не увімкнено для цього додатка",
|
||||
"The login method: login with password is not enabled for the application": "Метод входу через пароль не увімкнено для цього додатка",
|
||||
"The order: %s does not exist": "Замовлення: %s не існує",
|
||||
"The organization: %s does not exist": "Організація: %s не існує",
|
||||
"The organization: %s has disabled users to signin": "Організація: %s вимкнула вхід користувачів",
|
||||
"The plan: %s does not exist": "План: %s не існує",
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "Невідповідність даних обличчя",
|
||||
"Failed to parse client IP: %s": "Не вдалося розібрати IP клієнта: %s",
|
||||
"FirstName cannot be blank": "Ім’я не може бути порожнім",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Гостьові користувачі повинні оновити свій обліковий запис, встановивши ім'я користувача та пароль, перш ніж вони зможуть увійти безпосередньо",
|
||||
"Invitation code cannot be blank": "Код запрошення не може бути порожнім",
|
||||
"Invitation code exhausted": "Код запрошення вичерпано",
|
||||
"Invitation code is invalid": "Код запрошення недійсний",
|
||||
"Invitation code suspended": "Код запрошення призупинено",
|
||||
"LDAP user name or password incorrect": "Ім’я користувача або пароль LDAP неправильні",
|
||||
"LastName cannot be blank": "Прізвище не може бути порожнім",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Кілька облікових записів з однаковим uid, перевірте ваш ldap-сервер",
|
||||
"Organization does not exist": "Організація не існує",
|
||||
@@ -83,8 +83,8 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "Користувачу заборонено вхід, зверніться до адміністратора",
|
||||
"The user: %s doesn't exist in LDAP server": "Користувач: %s не існує на сервері LDAP",
|
||||
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "Ім’я користувача може містити лише буквено-цифрові символи, підкреслення або дефіси, не може мати послідовні дефіси або підкреслення та не може починатися або закінчуватися дефісом або підкресленням.",
|
||||
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "Значення \"%s\" для поля облікового запису \"%s\" не відповідає регулярному виразу облікового запису",
|
||||
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "Значення \"%s\" для поля реєстрації \"%s\" не відповідає регулярному виразу поля реєстрації додатка \"%s\"",
|
||||
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "Värdet \"%s\" för kontofältet \"%s\" matchar inte kontots regex",
|
||||
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "Värdet \"%s\" för registreringsfältet \"%s\" matchar inte registreringsfältets regex för applikationen \"%s\"",
|
||||
"Username already exists": "Ім’я користувача вже існує",
|
||||
"Username cannot be an email address": "Ім’я користувача не може бути email-адресою",
|
||||
"Username cannot contain white spaces": "Ім’я користувача не може містити пробіли",
|
||||
@@ -94,7 +94,7 @@
|
||||
"Username supports email format. Also The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline. Also pay attention to the email format.": "Ім’я користувача підтримує формат email. Також може містити лише буквено-цифрові символи, підкреслення або дефіси, не може мати послідовні дефіси або підкреслення та не може починатися або закінчуватися дефісом або підкресленням. Зверніть увагу на формат email.",
|
||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Ви ввели неправильний пароль або код забагато разів, зачекайте %d хвилин і спробуйте знову",
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Ваша IP-адреса: %s заблокована відповідно до конфігурації: ",
|
||||
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Ваш пароль застарів. Будь ласка, скиньте пароль, натиснувши \"Забув пароль\"",
|
||||
"Your password has expired. Please reset your password by clicking \"Forgot password\"": "Ditt lösenord har gått ut. Återställ det genom att klicka på \"Glömt lösenord\"",
|
||||
"Your region is not allow to signup by phone": "У вашому регіоні реєстрація за телефоном недоступна",
|
||||
"password or code is incorrect": "пароль або код неправильний",
|
||||
"password or code is incorrect, you have %s remaining chances": "пароль або код неправильний, у вас залишилось %s спроб",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Не вдалося імпортувати групи",
|
||||
"Failed to import users": "Не вдалося імпортувати користувачів",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Недостатній баланс: новий баланс %v буде нижче кредитного ліміту %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Недостатній баланс: новий баланс організації %v буде нижче кредитного ліміту %v",
|
||||
"Missing parameter": "Відсутній параметр",
|
||||
"Only admin user can specify user": "Лише адміністратор може вказати користувача",
|
||||
"Please login first": "Спочатку увійдіть",
|
||||
"The LDAP: %s does not exist": "LDAP: %s не існує",
|
||||
"The organization: %s should have one application at least": "Організація: %s має мати щонайменше один додаток",
|
||||
"The syncer: %s does not exist": "Синхронізатор: %s не існує",
|
||||
"The user: %s doesn't exist": "Користувач: %s не існує",
|
||||
"The user: %s is not found": "Користувач: %s не знайдено",
|
||||
"User is required for User category transaction": "Користувач обов'язковий для транзакції категорії користувача",
|
||||
"Wrong userId": "Неправильний userId",
|
||||
"don't support captchaProvider: ": "не підтримується captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "ця операція недоступна в демо-режимі",
|
||||
@@ -143,16 +137,10 @@
|
||||
"adding a new user to the 'built-in' organization is currently disabled. Please note: all users in the 'built-in' organization are global administrators in Casdoor. Refer to the docs: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. If you still wish to create a user for the 'built-in' organization, go to the organization's settings page and enable the 'Has privilege consent' option.": "Додавання нового користувача до організації «built-in» (вбудованої) на даний момент вимкнено. Зауважте: усі користувачі в організації «built-in» є глобальними адміністраторами в Casdoor. Дивіться документацію: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Якщо ви все ще хочете створити користувача для організації «built-in», перейдіть на сторінку налаштувань організації та увімкніть опцію «Має згоду на привілеї»."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "Дозвіл: \"%s\" не існує"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Список товарів не може бути порожнім"
|
||||
"The permission: \"%s\" doesn't exist": "Behörigheten: \"%s\" finns inte"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Не вдалося ініціалізувати провайдера верифікації ID",
|
||||
"Invalid application id": "Недійсний id додатка",
|
||||
"No ID Verification provider configured": "Провайдер верифікації ID не налаштований",
|
||||
"Provider is not an ID Verification provider": "Провайдер не є провайдером верифікації ID",
|
||||
"the provider: %s does not exist": "провайдер: %s не існує"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "Недійсні отримувачі Email: %s",
|
||||
"Invalid phone receivers: %s": "Недійсні отримувачі телефону: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "ідентифікатор сесії %s є поточною сесією і не може бути видалений"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "objectKey: %s не дозволено",
|
||||
"The provider type: %s is not supported": "Тип провайдера: %s не підтримується"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "Помилка"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Квиток не знайдено"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s не підтримується в цьому додатку",
|
||||
"Invalid application or wrong clientSecret": "Недійсний додаток або неправильний clientSecret",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Відображуване ім’я не може бути порожнім",
|
||||
"ID card information and real name are required": "Інформація про посвідчення особи та справжнє ім'я обов'язкові",
|
||||
"Identity verification failed": "Верифікація особи не вдалася",
|
||||
"MFA email is enabled but email is empty": "MFA email увімкнено, але email порожній",
|
||||
"MFA phone is enabled but phone number is empty": "MFA телефон увімкнено, але номер телефону порожній",
|
||||
"New password cannot contain blank space.": "Новий пароль не може містити пробіли.",
|
||||
"No application found for user": "Не знайдено додаток для користувача",
|
||||
"The new password must be different from your current password": "Новий пароль повинен відрізнятися від поточного пароля",
|
||||
"User is already verified": "Користувач уже верифікований",
|
||||
"the user's owner and name should not be empty": "власник ім’я користувача не повинні бути порожніми"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Недійсний провайдер captcha.",
|
||||
"Phone number is invalid in your region %s": "Номер телефону недійсний у вашому регіоні %s",
|
||||
"The forgot password feature is disabled": "Функція відновлення пароля вимкнена",
|
||||
"The verification code has already been used!": "Код підтвердження вже використано!",
|
||||
"The verification code has not been sent yet!": "Код підтвердження ще не надіслано!",
|
||||
"Turing test failed.": "Тест Тюрінга не пройдено.",
|
||||
@@ -219,8 +196,8 @@
|
||||
"Unknown type": "Невідомий тип",
|
||||
"Wrong verification code!": "Неправильний код підтвердження!",
|
||||
"You should verify your code in %d min!": "Ви маєте підтвердити код за %d хв!",
|
||||
"please add a SMS provider to the \"Providers\" list for the application: %s": "будь ласка, додайте SMS-провайдера до списку \"Провайдери\" для додатка: %s",
|
||||
"please add an Email provider to the \"Providers\" list for the application: %s": "будь ласка, додайте Email-провайдера до списку \"Провайдери\" для додатка: %s",
|
||||
"please add a SMS provider to the \"Providers\" list for the application: %s": "lägg till en SMS-leverantör i listan \"Leverantörer\" för applikationen: %s",
|
||||
"please add an Email provider to the \"Providers\" list for the application: %s": "lägg till en e-postleverantör i listan \"Leverantörer\" för applikationen: %s",
|
||||
"the user does not exist, please sign up first": "користувача не існує, спочатку зареєструйтесь"
|
||||
},
|
||||
"webauthn": {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "Không thể thêm người dùng",
|
||||
"Get init score failed, error: %w": "Lấy điểm khởi đầu thất bại, lỗi: %w",
|
||||
"Please sign out first": "Vui lòng đăng xuất trước",
|
||||
"The application does not allow to sign up new account": "Ứng dụng không cho phép đăng ký tài khoản mới"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "Phương thức đăng nhập bằng email chưa được bật cho ứng dụng",
|
||||
"The login method: login with face is not enabled for the application": "Phương thức đăng nhập bằng khuôn mặt chưa được bật cho ứng dụng",
|
||||
"The login method: login with password is not enabled for the application": "Phương thức đăng nhập: đăng nhập bằng mật khẩu không được kích hoạt cho ứng dụng",
|
||||
"The order: %s does not exist": "Đơn hàng: %s không tồn tại",
|
||||
"The organization: %s does not exist": "Tổ chức: %s không tồn tại",
|
||||
"The organization: %s has disabled users to signin": "Tổ chức: %s đã vô hiệu hóa đăng nhập của người dùng",
|
||||
"The plan: %s does not exist": "Kế hoạch: %s không tồn tại",
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "Dữ liệu khuôn mặt không khớp",
|
||||
"Failed to parse client IP: %s": "Không thể phân tích IP khách: %s",
|
||||
"FirstName cannot be blank": "Tên không được để trống",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "Người dùng khách phải nâng cấp tài khoản bằng cách đặt tên người dùng và mật khẩu trước khi có thể đăng nhập trực tiếp",
|
||||
"Invitation code cannot be blank": "Mã mời không được để trống",
|
||||
"Invitation code exhausted": "Mã mời đã hết",
|
||||
"Invitation code is invalid": "Mã mời không hợp lệ",
|
||||
"Invitation code suspended": "Mã mời đã bị tạm ngưng",
|
||||
"LDAP user name or password incorrect": "Tên người dùng hoặc mật khẩu Ldap không chính xác",
|
||||
"LastName cannot be blank": "Họ không thể để trống",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Nhiều tài khoản với cùng một uid, vui lòng kiểm tra máy chủ ldap của bạn",
|
||||
"Organization does not exist": "Tổ chức không tồn tại",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "Không thể nhập nhóm",
|
||||
"Failed to import users": "Không thể nhập người dùng",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "Số dư không đủ: số dư mới %v sẽ thấp hơn giới hạn tín dụng %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "Số dư không đủ: số dư tổ chức mới %v sẽ thấp hơn giới hạn tín dụng %v",
|
||||
"Missing parameter": "Thiếu tham số",
|
||||
"Only admin user can specify user": "Chỉ người dùng quản trị mới có thể chỉ định người dùng",
|
||||
"Please login first": "Vui lòng đăng nhập trước",
|
||||
"The LDAP: %s does not exist": "LDAP: %s không tồn tại",
|
||||
"The organization: %s should have one application at least": "Tổ chức: %s cần có ít nhất một ứng dụng",
|
||||
"The syncer: %s does not exist": "Bộ đồng bộ: %s không tồn tại",
|
||||
"The user: %s doesn't exist": "Người dùng: %s không tồn tại",
|
||||
"The user: %s is not found": "Người dùng: %s không được tìm thấy",
|
||||
"User is required for User category transaction": "Người dùng được yêu cầu cho giao dịch danh mục Người dùng",
|
||||
"Wrong userId": "ID người dùng sai",
|
||||
"don't support captchaProvider: ": "không hỗ trợ captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "thao tác này không được phép trong chế độ demo",
|
||||
@@ -145,14 +139,8 @@
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "Quyền: \"%s\" không tồn tại"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "Danh sách sản phẩm không thể trống"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "Không thể khởi tạo nhà cung cấp Xác minh ID",
|
||||
"Invalid application id": "Sai ID ứng dụng",
|
||||
"No ID Verification provider configured": "Không có nhà cung cấp Xác minh ID được cấu hình",
|
||||
"Provider is not an ID Verification provider": "Nhà cung cấp không phải là nhà cung cấp Xác minh ID",
|
||||
"the provider: %s does not exist": "Nhà cung cấp: %s không tồn tại"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "Người nhận Email không hợp lệ: %s",
|
||||
"Invalid phone receivers: %s": "Người nhận điện thoại không hợp lệ: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "id phiên %s là phiên hiện tại và không thể bị xóa"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "Khóa đối tượng: %s không được phép",
|
||||
"The provider type: %s is not supported": "Loại nhà cung cấp: %s không được hỗ trợ"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "Lỗi"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "Không tìm thấy vé"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Loại cấp phép: %s không được hỗ trợ trong ứng dụng này",
|
||||
"Invalid application or wrong clientSecret": "Đơn đăng ký không hợp lệ hoặc sai clientSecret",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Tên hiển thị không thể trống",
|
||||
"ID card information and real name are required": "Thông tin chứng minh nhân dân và tên thật là bắt buộc",
|
||||
"Identity verification failed": "Xác minh danh tính thất bại",
|
||||
"MFA email is enabled but email is empty": "MFA email đã bật nhưng email trống",
|
||||
"MFA phone is enabled but phone number is empty": "MFA điện thoại đã bật nhưng số điện thoại trống",
|
||||
"New password cannot contain blank space.": "Mật khẩu mới không thể chứa dấu trắng.",
|
||||
"No application found for user": "Không tìm thấy ứng dụng cho người dùng",
|
||||
"The new password must be different from your current password": "Mật khẩu mới phải khác với mật khẩu hiện tại của bạn",
|
||||
"User is already verified": "Người dùng đã được xác minh",
|
||||
"the user's owner and name should not be empty": "chủ sở hữu và tên người dùng không được để trống"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Nhà cung cấp captcha không hợp lệ.",
|
||||
"Phone number is invalid in your region %s": "Số điện thoại không hợp lệ trong vùng của bạn %s",
|
||||
"The forgot password feature is disabled": "Tính năng quên mật khẩu đã bị tắt",
|
||||
"The verification code has already been used!": "Mã xác thực đã được sử dụng!",
|
||||
"The verification code has not been sent yet!": "Mã xác thực chưa được gửi!",
|
||||
"Turing test failed.": "Kiểm định Turing thất bại.",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"account": {
|
||||
"Failed to add user": "添加用户失败",
|
||||
"Get init score failed, error: %w": "初始化分数失败: %w",
|
||||
"Please sign out first": "请先退出登录",
|
||||
"The application does not allow to sign up new account": "该应用不允许注册新用户"
|
||||
},
|
||||
"auth": {
|
||||
@@ -22,7 +23,6 @@
|
||||
"The login method: login with email is not enabled for the application": "该应用禁止采用邮箱登录方式",
|
||||
"The login method: login with face is not enabled for the application": "该应用禁止采用人脸登录",
|
||||
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",
|
||||
"The order: %s does not exist": "订单: %s 不存在",
|
||||
"The organization: %s does not exist": "组织: %s 不存在",
|
||||
"The organization: %s has disabled users to signin": "组织: %s 禁止用户登录",
|
||||
"The plan: %s does not exist": "计划: %s不存在",
|
||||
@@ -35,7 +35,7 @@
|
||||
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中",
|
||||
"UserCode Expired": "用户代码已过期",
|
||||
"UserCode Invalid": "用户代码无效",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "付费用户 %s 没有激活或待处理的订阅,且应用 %s 没有默认定价",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s 没有激活或正在等待订阅并且应用: %s 没有默认值",
|
||||
"the application for user %s is not found": "未找到用户 %s 的应用程序",
|
||||
"the organization: %s is not found": "组织: %s 不存在"
|
||||
},
|
||||
@@ -57,11 +57,11 @@
|
||||
"Face data mismatch": "人脸不匹配",
|
||||
"Failed to parse client IP: %s": "无法解析客户端 IP 地址: %s",
|
||||
"FirstName cannot be blank": "名不可以为空",
|
||||
"Guest users must upgrade their account by setting a username and password before they can sign in directly": "访客用户必须通过设置用户名和密码来升级账户,然后才能直接登录",
|
||||
"Invitation code cannot be blank": "邀请码不能为空",
|
||||
"Invitation code exhausted": "邀请码使用次数已耗尽",
|
||||
"Invitation code is invalid": "邀请码无效",
|
||||
"Invitation code suspended": "邀请码已被禁止使用",
|
||||
"LDAP user name or password incorrect": "LDAP密码错误",
|
||||
"LastName cannot be blank": "姓不可以为空",
|
||||
"Multiple accounts with same uid, please check your ldap server": "多个帐户具有相同的uid,请检查您的 LDAP 服务器",
|
||||
"Organization does not exist": "组织不存在",
|
||||
@@ -106,17 +106,11 @@
|
||||
"general": {
|
||||
"Failed to import groups": "导入群组失败",
|
||||
"Failed to import users": "导入用户失败",
|
||||
"Insufficient balance: new balance %v would be below credit limit %v": "余额不足:新余额 %v 将低于信用限额 %v",
|
||||
"Insufficient balance: new organization balance %v would be below credit limit %v": "余额不足:新组织余额 %v 将低于信用限额 %v",
|
||||
"Missing parameter": "缺少参数",
|
||||
"Only admin user can specify user": "仅管理员用户可以指定用户",
|
||||
"Please login first": "请先登录",
|
||||
"The LDAP: %s does not exist": "LDAP: %s 不存在",
|
||||
"The organization: %s should have one application at least": "组织: %s 应该拥有至少一个应用",
|
||||
"The syncer: %s does not exist": "同步器: %s 不存在",
|
||||
"The user: %s doesn't exist": "用户: %s不存在",
|
||||
"The user: %s is not found": "用户: %s 未找到",
|
||||
"User is required for User category transaction": "用户类别交易需要用户",
|
||||
"Wrong userId": "错误的 userId",
|
||||
"don't support captchaProvider: ": "不支持验证码提供商: ",
|
||||
"this operation is not allowed in demo mode": "demo模式下不允许该操作",
|
||||
@@ -145,14 +139,8 @@
|
||||
"permission": {
|
||||
"The permission: \"%s\" doesn't exist": "权限: \"%s\" 不存在"
|
||||
},
|
||||
"product": {
|
||||
"Product list cannot be empty": "产品列表不能为空"
|
||||
},
|
||||
"provider": {
|
||||
"Failed to initialize ID Verification provider": "初始化身份验证提供商失败",
|
||||
"Invalid application id": "无效的应用ID",
|
||||
"No ID Verification provider configured": "未配置身份验证提供商",
|
||||
"Provider is not an ID Verification provider": "提供商不是身份验证提供商",
|
||||
"the provider: %s does not exist": "提供商: %s不存在"
|
||||
},
|
||||
"resource": {
|
||||
@@ -170,9 +158,6 @@
|
||||
"Invalid Email receivers: %s": "无效的邮箱收件人: %s",
|
||||
"Invalid phone receivers: %s": "无效的手机短信收信人: %s"
|
||||
},
|
||||
"session": {
|
||||
"session id %s is the current session and cannot be deleted": "会话ID %s 是当前会话,无法删除"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "objectKey: %s被禁止",
|
||||
"The provider type: %s is not supported": "不支持的提供商类型: %s"
|
||||
@@ -180,9 +165,6 @@
|
||||
"subscription": {
|
||||
"Error": "错误"
|
||||
},
|
||||
"ticket": {
|
||||
"Ticket not found": "工单未找到"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "该应用不支持Grant_type: %s",
|
||||
"Invalid application or wrong clientSecret": "无效应用或错误的clientSecret",
|
||||
@@ -192,14 +174,10 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "显示名称不可为空",
|
||||
"ID card information and real name are required": "需要身份证信息和真实姓名",
|
||||
"Identity verification failed": "身份验证失败",
|
||||
"MFA email is enabled but email is empty": "MFA 电子邮件已启用,但电子邮件为空",
|
||||
"MFA phone is enabled but phone number is empty": "MFA 电话已启用,但电话号码为空",
|
||||
"New password cannot contain blank space.": "新密码不可以包含空格",
|
||||
"No application found for user": "未找到用户的应用程序",
|
||||
"The new password must be different from your current password": "新密码必须与您当前的密码不同",
|
||||
"User is already verified": "用户已验证",
|
||||
"the user's owner and name should not be empty": "用户的组织和名称不能为空"
|
||||
},
|
||||
"util": {
|
||||
@@ -210,7 +188,6 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "非法的验证码提供商",
|
||||
"Phone number is invalid in your region %s": "您所在地区的电话号码无效 %s",
|
||||
"The forgot password feature is disabled": "忘记密码功能已被禁用",
|
||||
"The verification code has already been used!": "验证码已使用过!",
|
||||
"The verification code has not been sent yet!": "验证码未发送!",
|
||||
"Turing test failed.": "验证码还未发送",
|
||||
|
||||
19
i18n/util.go
19
i18n/util.go
@@ -98,22 +98,15 @@ func Translate(language string, errorText string) string {
|
||||
if langMap[language] == nil {
|
||||
file, err := f.ReadFile(fmt.Sprintf("locales/%s/data.json", language))
|
||||
if err != nil {
|
||||
originalLanguage := language
|
||||
language = "en"
|
||||
file, err = f.ReadFile(fmt.Sprintf("locales/%s/data.json", language))
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Translate error: the language \"%s\" is not supported, err = %s", originalLanguage, err.Error())
|
||||
}
|
||||
return fmt.Sprintf("Translate error: the language \"%s\" is not supported, err = %s", language, err.Error())
|
||||
}
|
||||
|
||||
if langMap[language] == nil {
|
||||
data := I18nData{}
|
||||
err = util.JsonToStruct(string(file), &data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
langMap[language] = data
|
||||
data := I18nData{}
|
||||
err = util.JsonToStruct(string(file), &data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
langMap[language] = data
|
||||
}
|
||||
|
||||
res := langMap[language][tokens[0]][tokens[1]]
|
||||
|
||||
@@ -17,7 +17,6 @@ package idp
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -103,7 +102,7 @@ func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
return nil, err
|
||||
}
|
||||
if pToken.ErrMsg != "" {
|
||||
return nil, errors.New(pToken.ErrMsg)
|
||||
return nil, fmt.Errorf(pToken.ErrMsg)
|
||||
}
|
||||
|
||||
token := &oauth2.Token{
|
||||
|
||||
@@ -264,31 +264,27 @@ func rsaSignWithRSA256(signContent string, privateKey string) (string, error) {
|
||||
|
||||
// privateKey in database is a string, format it to PEM style
|
||||
func formatPrivateKey(privateKey string) string {
|
||||
// Check if the key is already in PEM format
|
||||
if strings.HasPrefix(privateKey, "-----BEGIN PRIVATE KEY-----") ||
|
||||
strings.HasPrefix(privateKey, "-----BEGIN RSA PRIVATE KEY-----") {
|
||||
// Key is already in PEM format, return as is
|
||||
return privateKey
|
||||
}
|
||||
|
||||
// Remove any whitespace from the key
|
||||
privateKey = strings.ReplaceAll(privateKey, "\n", "")
|
||||
privateKey = strings.ReplaceAll(privateKey, "\r", "")
|
||||
privateKey = strings.ReplaceAll(privateKey, " ", "")
|
||||
|
||||
// Format the key with line breaks every 64 characters using strings.Builder
|
||||
var builder strings.Builder
|
||||
for i := 0; i < len(privateKey); i += 64 {
|
||||
end := i + 64
|
||||
if end > len(privateKey) {
|
||||
end = len(privateKey)
|
||||
}
|
||||
builder.WriteString(privateKey[i:end])
|
||||
if end < len(privateKey) {
|
||||
builder.WriteString("\n")
|
||||
// each line length is 64
|
||||
preFmtPrivateKey := ""
|
||||
for i := 0; ; {
|
||||
if i+64 <= len(privateKey) {
|
||||
preFmtPrivateKey = preFmtPrivateKey + privateKey[i:i+64] + "\n"
|
||||
i += 64
|
||||
} else {
|
||||
preFmtPrivateKey = preFmtPrivateKey + privateKey[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
privateKey = strings.Trim(preFmtPrivateKey, "\n")
|
||||
|
||||
// add pkcs#8 BEGIN and END
|
||||
return "-----BEGIN PRIVATE KEY-----\n" + builder.String() + "\n-----END PRIVATE KEY-----"
|
||||
PemBegin := "-----BEGIN PRIVATE KEY-----\n"
|
||||
PemEnd := "\n-----END PRIVATE KEY-----"
|
||||
if !strings.HasPrefix(privateKey, PemBegin) {
|
||||
privateKey = PemBegin + privateKey
|
||||
}
|
||||
if !strings.HasSuffix(privateKey, PemEnd) {
|
||||
privateKey = privateKey + PemEnd
|
||||
}
|
||||
return privateKey
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ package idp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -159,7 +158,7 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
}
|
||||
|
||||
if dtUserInfo.OpenId == "" || dtUserInfo.UnionId == "" {
|
||||
return nil, errors.New(string(data))
|
||||
return nil, fmt.Errorf(string(data))
|
||||
}
|
||||
|
||||
countryCode, err := util.GetCountryCode(dtUserInfo.StateCode, dtUserInfo.Mobile)
|
||||
@@ -268,7 +267,7 @@ func (idp *DingTalkIdProvider) getUserId(unionId string, accessToken string) (st
|
||||
if data.ErrCode == 60121 {
|
||||
return "", fmt.Errorf("该应用只允许本企业内部用户登录,您不属于该企业,无法登录")
|
||||
} else if data.ErrCode != 0 {
|
||||
return "", errors.New(data.ErrMessage)
|
||||
return "", fmt.Errorf(data.ErrMessage)
|
||||
}
|
||||
return data.Result.UserId, nil
|
||||
}
|
||||
@@ -295,7 +294,7 @@ func (idp *DingTalkIdProvider) getUserCorpEmail(userId string, accessToken strin
|
||||
return "", "", "", err
|
||||
}
|
||||
if data.ErrMessage != "ok" {
|
||||
return "", "", "", errors.New(data.ErrMessage)
|
||||
return "", "", "", fmt.Errorf(data.ErrMessage)
|
||||
}
|
||||
return data.Result.Mobile, data.Result.Email, data.Result.UnionId, nil
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -125,7 +124,7 @@ func (idp *WeChatIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
|
||||
// {"errcode":40163,"errmsg":"code been used, rid: 6206378a-793424c0-2e4091cc"}
|
||||
if strings.Contains(buf.String(), "errcode") {
|
||||
return nil, errors.New(buf.String())
|
||||
return nil, fmt.Errorf(buf.String())
|
||||
}
|
||||
|
||||
var wechatAccessToken WechatAccessToken
|
||||
|
||||
@@ -17,7 +17,6 @@ package idp
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -94,7 +93,7 @@ func (idp *WeChatMobileIdProvider) GetToken(code string) (*oauth2.Token, error)
|
||||
|
||||
// Check for error response
|
||||
if bytes.Contains(buf.Bytes(), []byte("errcode")) {
|
||||
return nil, errors.New(buf.String())
|
||||
return nil, fmt.Errorf(buf.String())
|
||||
}
|
||||
|
||||
var wechatAccessToken WechatAccessToken
|
||||
|
||||
@@ -67,8 +67,6 @@
|
||||
{"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": "First name", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Last 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"},
|
||||
@@ -83,28 +81,14 @@
|
||||
{"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": "ID card info", "visible": true, "viewRule": "Self", "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": "Language", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Gender", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Birthday", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Education", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Balance", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Balance credit", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Balance currency", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Cart", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Transactions", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Score", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Karma", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"name": "Ranking", "visible": true, "viewRule": "Public", "modifyRule": "Self"},
|
||||
{"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": "API key", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Roles", "visible": true, "viewRule": "Public", "modifyRule": "Immutable"},
|
||||
{"name": "Permissions", "visible": true, "viewRule": "Public", "modifyRule": "Immutable"},
|
||||
{"name": "Groups", "visible": true, "viewRule": "Public", "modifyRule": "Admin"},
|
||||
@@ -114,14 +98,9 @@
|
||||
{"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": "MFA items", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "WebAuthn credentials", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Last change password time", "visible": true, "viewRule": "Admin", "modifyRule": "Admin"},
|
||||
{"name": "Managed accounts", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Face ID", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "MFA accounts", "visible": true, "viewRule": "Self", "modifyRule": "Self"},
|
||||
{"name": "Need update password", "visible": true, "viewRule": "Admin", "modifyRule": "Admin"},
|
||||
{"name": "IP whitelist", "visible": true, "viewRule": "Admin", "modifyRule": "Admin"}
|
||||
{"name": "MFA accounts", "visible": true, "viewRule": "Self", "modifyRule": "Self"}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
43
ip/ip.go
43
ip/ip.go
@@ -1,43 +0,0 @@
|
||||
// Copyright 2024 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ip
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
func InitIpDb() {
|
||||
err := Init("ip/17monipdb.dat")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func IsAbroadIp(ip string) bool {
|
||||
// If it's an intranet IP, it's not abroad
|
||||
if util.IsIntranetIp(ip) {
|
||||
return false
|
||||
}
|
||||
|
||||
info, err := Find(ip)
|
||||
if err != nil {
|
||||
fmt.Printf("error: ip = %s, error = %s\n", ip, err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
return info.Country != "中国"
|
||||
}
|
||||
199
ip/ip17mon.go
199
ip/ip17mon.go
@@ -1,199 +0,0 @@
|
||||
// Copyright 2022 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ip
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
)
|
||||
|
||||
const Null = "N/A"
|
||||
|
||||
var (
|
||||
ErrInvalidIp = errors.New("invalid ip format")
|
||||
std *Locator
|
||||
)
|
||||
|
||||
// Init default locator with dataFile
|
||||
func Init(dataFile string) (err error) {
|
||||
if std != nil {
|
||||
return
|
||||
}
|
||||
std, err = NewLocator(dataFile)
|
||||
return
|
||||
}
|
||||
|
||||
// Init default locator with data
|
||||
func InitWithData(data []byte) {
|
||||
if std != nil {
|
||||
return
|
||||
}
|
||||
std = NewLocatorWithData(data)
|
||||
return
|
||||
}
|
||||
|
||||
// Find locationInfo by ip string
|
||||
// It will return err when ipstr is not a valid format
|
||||
func Find(ipstr string) (*LocationInfo, error) {
|
||||
return std.Find(ipstr)
|
||||
}
|
||||
|
||||
// Find locationInfo by uint32
|
||||
func FindByUint(ip uint32) *LocationInfo {
|
||||
return std.FindByUint(ip)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// New locator with dataFile
|
||||
func NewLocator(dataFile string) (loc *Locator, err error) {
|
||||
data, err := ioutil.ReadFile(dataFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
loc = NewLocatorWithData(data)
|
||||
return
|
||||
}
|
||||
|
||||
// New locator with data
|
||||
func NewLocatorWithData(data []byte) (loc *Locator) {
|
||||
loc = new(Locator)
|
||||
loc.init(data)
|
||||
return
|
||||
}
|
||||
|
||||
type Locator struct {
|
||||
textData []byte
|
||||
indexData1 []uint32
|
||||
indexData2 []int
|
||||
indexData3 []int
|
||||
index []int
|
||||
}
|
||||
|
||||
type LocationInfo struct {
|
||||
Country string
|
||||
Region string
|
||||
City string
|
||||
Isp string
|
||||
}
|
||||
|
||||
// Find locationInfo by ip string
|
||||
// It will return err when ipstr is not a valid format
|
||||
func (loc *Locator) Find(ipstr string) (info *LocationInfo, err error) {
|
||||
ip := net.ParseIP(ipstr).To4()
|
||||
if ip == nil || ip.To4() == nil {
|
||||
err = ErrInvalidIp
|
||||
return
|
||||
}
|
||||
info = loc.FindByUint(binary.BigEndian.Uint32([]byte(ip)))
|
||||
return
|
||||
}
|
||||
|
||||
// Find locationInfo by uint32
|
||||
func (loc *Locator) FindByUint(ip uint32) (info *LocationInfo) {
|
||||
end := len(loc.indexData1) - 1
|
||||
if ip>>24 != 0xff {
|
||||
end = loc.index[(ip>>24)+1]
|
||||
}
|
||||
idx := loc.findIndexOffset(ip, loc.index[ip>>24], end)
|
||||
off := loc.indexData2[idx]
|
||||
return newLocationInfo(loc.textData[off : off+loc.indexData3[idx]])
|
||||
}
|
||||
|
||||
// binary search
|
||||
func (loc *Locator) findIndexOffset(ip uint32, start, end int) int {
|
||||
for start < end {
|
||||
mid := (start + end) / 2
|
||||
if ip > loc.indexData1[mid] {
|
||||
start = mid + 1
|
||||
} else {
|
||||
end = mid
|
||||
}
|
||||
}
|
||||
|
||||
if loc.indexData1[end] >= ip {
|
||||
return end
|
||||
}
|
||||
|
||||
return start
|
||||
}
|
||||
|
||||
func (loc *Locator) init(data []byte) {
|
||||
textoff := int(binary.BigEndian.Uint32(data[:4]))
|
||||
|
||||
loc.textData = data[textoff-1024:]
|
||||
|
||||
loc.index = make([]int, 256)
|
||||
for i := 0; i < 256; i++ {
|
||||
off := 4 + i*4
|
||||
loc.index[i] = int(binary.LittleEndian.Uint32(data[off : off+4]))
|
||||
}
|
||||
|
||||
nidx := (textoff - 4 - 1024 - 1024) / 8
|
||||
|
||||
loc.indexData1 = make([]uint32, nidx)
|
||||
loc.indexData2 = make([]int, nidx)
|
||||
loc.indexData3 = make([]int, nidx)
|
||||
|
||||
for i := 0; i < nidx; i++ {
|
||||
off := 4 + 1024 + i*8
|
||||
loc.indexData1[i] = binary.BigEndian.Uint32(data[off : off+4])
|
||||
loc.indexData2[i] = int(uint32(data[off+4]) | uint32(data[off+5])<<8 | uint32(data[off+6])<<16)
|
||||
loc.indexData3[i] = int(data[off+7])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func newLocationInfo(str []byte) *LocationInfo {
|
||||
var info *LocationInfo
|
||||
|
||||
fields := bytes.Split(str, []byte("\t"))
|
||||
switch len(fields) {
|
||||
case 4:
|
||||
// free version
|
||||
info = &LocationInfo{
|
||||
Country: string(fields[0]),
|
||||
Region: string(fields[1]),
|
||||
City: string(fields[2]),
|
||||
}
|
||||
case 5:
|
||||
// pay version
|
||||
info = &LocationInfo{
|
||||
Country: string(fields[0]),
|
||||
Region: string(fields[1]),
|
||||
City: string(fields[2]),
|
||||
Isp: string(fields[4]),
|
||||
}
|
||||
default:
|
||||
panic("unexpected ip info:" + string(str))
|
||||
}
|
||||
|
||||
if len(info.Country) == 0 {
|
||||
info.Country = Null
|
||||
}
|
||||
if len(info.Region) == 0 {
|
||||
info.Region = Null
|
||||
}
|
||||
if len(info.City) == 0 {
|
||||
info.City = Null
|
||||
}
|
||||
if len(info.Isp) == 0 {
|
||||
info.Isp = Null
|
||||
}
|
||||
return info
|
||||
}
|
||||
@@ -203,101 +203,49 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
||||
return
|
||||
}
|
||||
|
||||
orgCache := make(map[string]*object.Organization)
|
||||
|
||||
for _, user := range users {
|
||||
if _, ok := orgCache[user.Owner]; !ok {
|
||||
org, err := object.GetOrganizationByUser(user)
|
||||
if err != nil {
|
||||
log.Printf("handleSearch: failed to get organization for user %s: %v", user.Name, err)
|
||||
}
|
||||
orgCache[user.Owner] = org
|
||||
}
|
||||
org := orgCache[user.Owner]
|
||||
|
||||
e := buildUserSearchEntry(user, string(r.BaseObject()), resolveRequestAttributes(r.Attributes()), org)
|
||||
w.Write(e)
|
||||
}
|
||||
w.Write(res)
|
||||
}
|
||||
|
||||
// resolveRequestAttributes expands the "*" wildcard to the full list of additional LDAP attributes.
|
||||
func resolveRequestAttributes(attrs message.AttributeSelection) []string {
|
||||
result := make([]string, 0, len(attrs))
|
||||
for _, attr := range attrs {
|
||||
if string(attr) == "*" {
|
||||
result = make([]string, 0, len(AdditionalLdapAttributes))
|
||||
for _, a := range AdditionalLdapAttributes {
|
||||
result = append(result, string(a))
|
||||
}
|
||||
return result
|
||||
}
|
||||
result = append(result, string(attr))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// buildUserSearchEntry constructs an LDAP search result entry for the given user,
|
||||
// respecting the organization's LdapAttributes filter.
|
||||
func buildUserSearchEntry(user *object.User, baseDN string, attrs []string, org *object.Organization) message.SearchResultEntry {
|
||||
dn := fmt.Sprintf("uid=%s,cn=%s,%s", user.Id, user.Name, baseDN)
|
||||
e := ldap.NewSearchResultEntry(dn)
|
||||
uidNumberStr := fmt.Sprintf("%v", hash(user.Name))
|
||||
if IsLdapAttrAllowed(org, "uidNumber") {
|
||||
dn := fmt.Sprintf("uid=%s,cn=%s,%s", user.Id, user.Name, string(r.BaseObject()))
|
||||
e := ldap.NewSearchResultEntry(dn)
|
||||
uidNumberStr := fmt.Sprintf("%v", hash(user.Name))
|
||||
e.AddAttribute("uidNumber", message.AttributeValue(uidNumberStr))
|
||||
}
|
||||
if IsLdapAttrAllowed(org, "gidNumber") {
|
||||
e.AddAttribute("gidNumber", message.AttributeValue(uidNumberStr))
|
||||
}
|
||||
if IsLdapAttrAllowed(org, "homeDirectory") {
|
||||
e.AddAttribute("homeDirectory", message.AttributeValue("/home/"+user.Name))
|
||||
}
|
||||
if IsLdapAttrAllowed(org, "cn") {
|
||||
e.AddAttribute("cn", message.AttributeValue(user.Name))
|
||||
}
|
||||
if IsLdapAttrAllowed(org, "uid") {
|
||||
e.AddAttribute("uid", message.AttributeValue(user.Id))
|
||||
}
|
||||
if IsLdapAttrAllowed(org, "mail") {
|
||||
e.AddAttribute("mail", message.AttributeValue(user.Email))
|
||||
}
|
||||
if IsLdapAttrAllowed(org, "mobile") {
|
||||
e.AddAttribute("mobile", message.AttributeValue(user.Phone))
|
||||
}
|
||||
if IsLdapAttrAllowed(org, "sn") {
|
||||
e.AddAttribute("sn", message.AttributeValue(user.LastName))
|
||||
}
|
||||
if IsLdapAttrAllowed(org, "givenName") {
|
||||
e.AddAttribute("givenName", message.AttributeValue(user.FirstName))
|
||||
}
|
||||
// Add POSIX attributes for Linux machine login support
|
||||
if IsLdapAttrAllowed(org, "loginShell") {
|
||||
// Add POSIX attributes for Linux machine login support
|
||||
e.AddAttribute("loginShell", getAttribute("loginShell", user))
|
||||
}
|
||||
if IsLdapAttrAllowed(org, "gecos") {
|
||||
e.AddAttribute("gecos", getAttribute("gecos", user))
|
||||
}
|
||||
// Add SSH public key if available
|
||||
if IsLdapAttrAllowed(org, "sshPublicKey") {
|
||||
// Add SSH public key if available
|
||||
sshKey := getAttribute("sshPublicKey", user)
|
||||
if sshKey != "" {
|
||||
e.AddAttribute("sshPublicKey", sshKey)
|
||||
}
|
||||
}
|
||||
// Add objectClass for posixAccount
|
||||
e.AddAttribute("objectClass", "posixAccount")
|
||||
if IsLdapAttrAllowed(org, ldapMemberOfAttr) {
|
||||
// Add objectClass for posixAccount
|
||||
e.AddAttribute("objectClass", "posixAccount")
|
||||
for _, group := range user.Groups {
|
||||
e.AddAttribute(ldapMemberOfAttr, message.AttributeValue(group))
|
||||
}
|
||||
}
|
||||
for _, attr := range attrs {
|
||||
if !IsLdapAttrAllowed(org, attr) {
|
||||
continue
|
||||
attrs := r.Attributes()
|
||||
for _, attr := range attrs {
|
||||
if string(attr) == "*" {
|
||||
attrs = AdditionalLdapAttributes
|
||||
break
|
||||
}
|
||||
}
|
||||
e.AddAttribute(message.AttributeDescription(attr), getAttribute(attr, user))
|
||||
for _, attr := range attrs {
|
||||
e.AddAttribute(message.AttributeDescription(attr), getAttribute(string(attr), user))
|
||||
if string(attr) == "title" {
|
||||
e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user))
|
||||
}
|
||||
}
|
||||
|
||||
w.Write(e)
|
||||
}
|
||||
return e
|
||||
w.Write(res)
|
||||
}
|
||||
|
||||
func handleRootSearch(w ldap.ResponseWriter, r *message.SearchRequest, res *message.SearchResultDone, m *ldap.Message) {
|
||||
|
||||
14
ldap/util.go
14
ldap/util.go
@@ -198,20 +198,6 @@ func stringInSlice(value string, list []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsLdapAttrAllowed checks whether the given LDAP attribute is allowed for the organization.
|
||||
// An empty filter or a filter containing "All" means all attributes are allowed.
|
||||
func IsLdapAttrAllowed(org *object.Organization, attr string) bool {
|
||||
if org == nil || len(org.LdapAttributes) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, f := range org.LdapAttributes {
|
||||
if strings.EqualFold(f, "All") || strings.EqualFold(f, attr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func buildUserFilterCondition(filter interface{}) (builder.Cond, error) {
|
||||
switch f := filter.(type) {
|
||||
case message.FilterAnd:
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// 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 log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EntryAdder persists a log entry to the backing store.
|
||||
// Parameters map to the Entry table columns: owner, name (unique ID),
|
||||
// createdTime (RFC3339), provider (the log provider name), and message (the
|
||||
// log content). This indirection keeps the log package free of import-cycle
|
||||
// dependencies on the object package.
|
||||
type EntryAdder func(owner, name, createdTime, provider, message string) error
|
||||
|
||||
// PermissionLogProvider records Casbin authorization decisions as Entry rows.
|
||||
// It implements LogProvider; actual storage is delegated to an EntryAdder so
|
||||
// that the log package remains free of import-cycle dependencies on object.
|
||||
type PermissionLogProvider struct {
|
||||
providerName string
|
||||
addEntry EntryAdder
|
||||
}
|
||||
|
||||
// NewPermissionLogProvider creates a PermissionLogProvider with the given
|
||||
// provider name, backed by addEntry.
|
||||
func NewPermissionLogProvider(providerName string, addEntry EntryAdder) *PermissionLogProvider {
|
||||
return &PermissionLogProvider{providerName: providerName, addEntry: addEntry}
|
||||
}
|
||||
|
||||
// Write stores one permission-log entry.
|
||||
// severity follows syslog conventions (e.g. info, err).
|
||||
func (p *PermissionLogProvider) Write(severity string, message string) error {
|
||||
name := fmt.Sprintf("%x", time.Now().UnixNano())
|
||||
createdTime := time.Now().UTC().Format(time.RFC3339)
|
||||
return p.addEntry("built-in", name, createdTime, p.providerName, fmt.Sprintf("[%s] %s", severity, message))
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// 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 log
|
||||
|
||||
import "fmt"
|
||||
|
||||
// LogProvider sends application log lines to an external logging backend.
|
||||
// Severity uses syslog-style names (e.g. emerg, alert, crit, err, warning, notice, info, debug).
|
||||
type LogProvider interface {
|
||||
Write(severity string, message string) error
|
||||
}
|
||||
|
||||
// GetLogProvider returns a concrete log provider for the given type and connection settings.
|
||||
// The title parameter is used as the syslog/event-log tag for System Log.
|
||||
// Types that are not yet implemented return a non-nil error.
|
||||
func GetLogProvider(typ string, _ string, _ int, title string) (LogProvider, error) {
|
||||
switch typ {
|
||||
case "System Log":
|
||||
tag := title
|
||||
if tag == "" {
|
||||
tag = "casdoor"
|
||||
}
|
||||
return NewSystemLogProvider(tag)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported log provider type: %s", typ)
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// 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 log
|
||||
|
||||
// SystemLogProvider writes log lines to the operating-system's native logging
|
||||
// facility (syslog on Linux/Unix, Event Log on Windows).
|
||||
// It implements LogProvider; the platform-specific open/write/close logic is
|
||||
// provided by system_log_unix.go and system_log_windows.go via the
|
||||
// platformLogger interface.
|
||||
type SystemLogProvider struct {
|
||||
tag string
|
||||
logger platformLogger
|
||||
}
|
||||
|
||||
// platformLogger abstracts the OS-level logging primitives so that
|
||||
// SystemLogProvider stays free of build-tag clutter.
|
||||
type platformLogger interface {
|
||||
// log writes a single line at the given syslog-style severity.
|
||||
log(severity, message string) error
|
||||
// close releases any OS resource held by the logger.
|
||||
close() error
|
||||
}
|
||||
|
||||
// NewSystemLogProvider opens the OS logging facility with the supplied tag and
|
||||
// returns a ready-to-use SystemLogProvider.
|
||||
func NewSystemLogProvider(tag string) (*SystemLogProvider, error) {
|
||||
pl, err := newPlatformLogger(tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SystemLogProvider{tag: tag, logger: pl}, nil
|
||||
}
|
||||
|
||||
// Write implements LogProvider.
|
||||
func (s *SystemLogProvider) Write(severity string, message string) error {
|
||||
return s.logger.log(severity, message)
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// 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.
|
||||
|
||||
//go:build !windows
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/syslog"
|
||||
)
|
||||
|
||||
type unixLogger struct {
|
||||
writer *syslog.Writer
|
||||
}
|
||||
|
||||
func newPlatformLogger(tag string) (platformLogger, error) {
|
||||
w, err := syslog.New(syslog.LOG_INFO|syslog.LOG_DAEMON, tag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SystemLogProvider: failed to open syslog: %w", err)
|
||||
}
|
||||
return &unixLogger{writer: w}, nil
|
||||
}
|
||||
|
||||
func (u *unixLogger) log(severity, message string) error {
|
||||
switch severity {
|
||||
case "emerg":
|
||||
return u.writer.Emerg(message)
|
||||
case "alert":
|
||||
return u.writer.Alert(message)
|
||||
case "crit":
|
||||
return u.writer.Crit(message)
|
||||
case "err", "error":
|
||||
return u.writer.Err(message)
|
||||
case "warning", "warn":
|
||||
return u.writer.Warning(message)
|
||||
case "notice":
|
||||
return u.writer.Notice(message)
|
||||
case "debug":
|
||||
return u.writer.Debug(message)
|
||||
default: // "info" and anything else
|
||||
return u.writer.Info(message)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *unixLogger) close() error {
|
||||
return u.writer.Close()
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// 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.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
)
|
||||
|
||||
type windowsLogger struct {
|
||||
writer *eventlog.Log
|
||||
}
|
||||
|
||||
func newPlatformLogger(tag string) (platformLogger, error) {
|
||||
// Ensure the event source exists; ignore the error if it already does.
|
||||
_ = eventlog.InstallAsEventCreate(tag, eventlog.Info|eventlog.Warning|eventlog.Error)
|
||||
|
||||
w, err := eventlog.Open(tag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SystemLogProvider: failed to open Windows Event Log: %w", err)
|
||||
}
|
||||
return &windowsLogger{writer: w}, nil
|
||||
}
|
||||
|
||||
func (w *windowsLogger) log(severity, message string) error {
|
||||
switch severity {
|
||||
case "emerg", "alert", "crit", "err", "error":
|
||||
return w.writer.Error(1, message)
|
||||
case "warning", "warn":
|
||||
return w.writer.Warning(2, message)
|
||||
default: // "notice", "info", "debug" and anything else
|
||||
return w.writer.Info(3, message)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *windowsLogger) close() error {
|
||||
return w.writer.Close()
|
||||
}
|
||||
15
main.go
15
main.go
@@ -29,7 +29,6 @@ import (
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/casdoor/casdoor/radius"
|
||||
"github.com/casdoor/casdoor/routers"
|
||||
"github.com/casdoor/casdoor/service"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
@@ -71,14 +70,9 @@ func main() {
|
||||
authz.InitApi()
|
||||
object.InitUserManager()
|
||||
object.InitFromFile()
|
||||
object.InitCasvisorConfig()
|
||||
object.InitCleanupTokens()
|
||||
|
||||
object.InitSiteMap()
|
||||
if len(object.SiteMap) != 0 {
|
||||
object.InitRuleMap()
|
||||
object.StartMonitorSitesLoop()
|
||||
}
|
||||
|
||||
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
|
||||
util.SafeGoroutine(func() { controllers.InitCLIDownloader() })
|
||||
|
||||
@@ -132,12 +126,5 @@ func main() {
|
||||
go radius.StartRadiusServer()
|
||||
go object.ClearThroughputPerSecond()
|
||||
|
||||
// Start webhook delivery worker
|
||||
object.StartWebhookDeliveryWorker()
|
||||
|
||||
if len(object.SiteMap) != 0 {
|
||||
service.Start()
|
||||
}
|
||||
|
||||
web.Run(fmt.Sprintf(":%v", port))
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mcpself
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -12,10 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mcpself
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
@@ -121,58 +120,3 @@ func (c *McpController) GetAcceptLanguage() string {
|
||||
}
|
||||
return language
|
||||
}
|
||||
|
||||
// GetTokenFromRequest extracts the Bearer token from the Authorization header
|
||||
func (c *McpController) GetTokenFromRequest() string {
|
||||
authHeader := c.Ctx.Request.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Extract Bearer token
|
||||
parts := strings.SplitN(authHeader, " ", 2)
|
||||
if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
|
||||
return ""
|
||||
}
|
||||
|
||||
return parts[1]
|
||||
}
|
||||
|
||||
// GetClaimsFromToken parses and validates the JWT token and returns the claims
|
||||
// Returns nil if no token is present or if token is invalid
|
||||
func (c *McpController) GetClaimsFromToken() *object.Claims {
|
||||
tokenString := c.GetTokenFromRequest()
|
||||
if tokenString == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try to find the application for this token
|
||||
// For MCP, we'll try to parse using the first available application's certificate
|
||||
// In a production scenario, you might want to use a specific MCP application
|
||||
token, err := object.GetTokenByAccessToken(tokenString)
|
||||
if err != nil || token == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
application, err := object.GetApplication(token.Application)
|
||||
if err != nil || application == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
claims, err := object.ParseJwtTokenByApplication(tokenString, application)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return claims
|
||||
}
|
||||
|
||||
// GetScopesFromClaims extracts the scopes from JWT claims and returns them as a slice
|
||||
func GetScopesFromClaims(claims *object.Claims) []string {
|
||||
if claims == nil || claims.Scope == "" {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Scopes are space-separated in OAuth 2.0
|
||||
return strings.Split(claims.Scope, " ")
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mcpself
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -268,160 +268,7 @@ func (c *McpController) handlePing(req McpRequest) {
|
||||
}
|
||||
|
||||
func (c *McpController) handleToolsList(req McpRequest) {
|
||||
allTools := c.getAllTools()
|
||||
|
||||
// Get JWT claims from the request
|
||||
claims := c.GetClaimsFromToken()
|
||||
|
||||
// If no token is present, check session authentication
|
||||
if claims == nil {
|
||||
username := c.GetSessionUsername()
|
||||
// If user is authenticated via session, return all tools (backward compatibility)
|
||||
if username != "" {
|
||||
result := McpListToolsResult{
|
||||
Tools: allTools,
|
||||
}
|
||||
c.McpResponseOk(req.ID, result)
|
||||
return
|
||||
}
|
||||
|
||||
// Unauthenticated request - return all tools for discovery
|
||||
// This allows clients to see what tools are available before authenticating
|
||||
result := McpListToolsResult{
|
||||
Tools: allTools,
|
||||
}
|
||||
c.McpResponseOk(req.ID, result)
|
||||
return
|
||||
}
|
||||
|
||||
// Token-based authentication - filter tools by scopes
|
||||
grantedScopes := GetScopesFromClaims(claims)
|
||||
allowedTools := GetToolsForScopes(grantedScopes, BuiltinScopes)
|
||||
|
||||
// Filter tools based on allowed scopes
|
||||
var filteredTools []McpTool
|
||||
for _, tool := range allTools {
|
||||
if allowedTools[tool.Name] {
|
||||
filteredTools = append(filteredTools, tool)
|
||||
}
|
||||
}
|
||||
|
||||
result := McpListToolsResult{
|
||||
Tools: filteredTools,
|
||||
}
|
||||
|
||||
c.McpResponseOk(req.ID, result)
|
||||
}
|
||||
|
||||
func (c *McpController) handleToolsCall(req McpRequest) {
|
||||
var params McpCallToolParams
|
||||
err := json.Unmarshal(req.Params, ¶ms)
|
||||
if err != nil {
|
||||
c.sendInvalidParamsError(req.ID, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Check scope-tool permission
|
||||
if !c.checkToolPermission(req.ID, params.Name) {
|
||||
return // Error already sent by checkToolPermission
|
||||
}
|
||||
|
||||
// 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.McpResponseError(req.ID, -32602, "Invalid tool name", fmt.Sprintf("Tool '%s' not found", params.Name))
|
||||
}
|
||||
}
|
||||
|
||||
// checkToolPermission validates that the current token has the required scope for the tool
|
||||
// Returns false and sends an error response if permission is denied
|
||||
func (c *McpController) checkToolPermission(id interface{}, toolName string) bool {
|
||||
// Get JWT claims from the request
|
||||
claims := c.GetClaimsFromToken()
|
||||
|
||||
// If no token is present, check if the user is authenticated via session
|
||||
if claims == nil {
|
||||
username := c.GetSessionUsername()
|
||||
// If user is authenticated via session (e.g., session cookie), allow access
|
||||
// This maintains backward compatibility with existing session-based auth
|
||||
if username != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// No authentication present - deny access
|
||||
c.sendInsufficientScopeError(id, toolName, []string{})
|
||||
return false
|
||||
}
|
||||
|
||||
// Extract scopes from claims
|
||||
grantedScopes := GetScopesFromClaims(claims)
|
||||
|
||||
// Get allowed tools for the granted scopes
|
||||
allowedTools := GetToolsForScopes(grantedScopes, BuiltinScopes)
|
||||
|
||||
// Check if the requested tool is allowed
|
||||
if !allowedTools[toolName] {
|
||||
c.sendInsufficientScopeError(id, toolName, grantedScopes)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// sendInsufficientScopeError sends an error response for insufficient scope
|
||||
func (c *McpController) sendInsufficientScopeError(id interface{}, toolName string, grantedScopes []string) {
|
||||
// Find required scope for this tool
|
||||
requiredScope := GetRequiredScopeForTool(toolName, BuiltinScopes)
|
||||
|
||||
errorData := map[string]interface{}{
|
||||
"tool": toolName,
|
||||
"granted_scopes": grantedScopes,
|
||||
}
|
||||
if requiredScope != "" {
|
||||
errorData["required_scope"] = requiredScope
|
||||
}
|
||||
|
||||
c.McpResponseError(id, -32001, "insufficient_scope", errorData)
|
||||
}
|
||||
|
||||
// getAllTools returns all available MCP tools
|
||||
func (c *McpController) getAllTools() []McpTool {
|
||||
return []McpTool{
|
||||
tools := []McpTool{
|
||||
{
|
||||
Name: "get_applications",
|
||||
Description: "Get all applications for a specific owner",
|
||||
@@ -497,4 +344,60 @@ func (c *McpController) getAllTools() []McpTool {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
result := McpListToolsResult{
|
||||
Tools: tools,
|
||||
}
|
||||
|
||||
c.McpResponseOk(req.ID, result)
|
||||
}
|
||||
|
||||
func (c *McpController) handleToolsCall(req McpRequest) {
|
||||
var params McpCallToolParams
|
||||
err := json.Unmarshal(req.Params, ¶ms)
|
||||
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.McpResponseError(req.ID, -32602, "Invalid tool name", fmt.Sprintf("Tool '%s' not found", params.Name))
|
||||
}
|
||||
}
|
||||
51
mcp/util.go
51
mcp/util.go
@@ -1,51 +0,0 @@
|
||||
// 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 (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
mcpsdk "github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func GetServerTools(owner, name, url, token string) ([]*mcpsdk.Tool, error) {
|
||||
var session *mcpsdk.ClientSession
|
||||
var err error
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10)
|
||||
defer cancel()
|
||||
client := mcpsdk.NewClient(&mcpsdk.Implementation{Name: util.GetId(owner, name), Version: "1.0.0"}, nil)
|
||||
if token != "" {
|
||||
httpClient := oauth2.NewClient(ctx, oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}))
|
||||
session, err = client.Connect(ctx, &mcpsdk.StreamableClientTransport{Endpoint: url, HTTPClient: httpClient}, nil)
|
||||
} else {
|
||||
session, err = client.Connect(ctx, &mcpsdk.StreamableClientTransport{Endpoint: url}, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
toolResult, err := session.ListTools(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return toolResult.Tools, nil
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
// 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 mcpself
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
// BuiltinScopes defines the default scope-to-tool mappings for Casdoor's MCP server
|
||||
var BuiltinScopes = []*object.ScopeItem{
|
||||
{
|
||||
Name: "application:read",
|
||||
DisplayName: "Read Applications",
|
||||
Description: "View application list and details",
|
||||
Tools: []string{"get_applications", "get_application"},
|
||||
},
|
||||
{
|
||||
Name: "application:write",
|
||||
DisplayName: "Manage Applications",
|
||||
Description: "Create, update, and delete applications",
|
||||
Tools: []string{"add_application", "update_application", "delete_application"},
|
||||
},
|
||||
{
|
||||
Name: "user:read",
|
||||
DisplayName: "Read Users",
|
||||
Description: "View user list and details",
|
||||
Tools: []string{"get_users", "get_user"},
|
||||
},
|
||||
{
|
||||
Name: "user:write",
|
||||
DisplayName: "Manage Users",
|
||||
Description: "Create, update, and delete users",
|
||||
Tools: []string{"add_user", "update_user", "delete_user"},
|
||||
},
|
||||
{
|
||||
Name: "organization:read",
|
||||
DisplayName: "Read Organizations",
|
||||
Description: "View organization list and details",
|
||||
Tools: []string{"get_organizations", "get_organization"},
|
||||
},
|
||||
{
|
||||
Name: "organization:write",
|
||||
DisplayName: "Manage Organizations",
|
||||
Description: "Create, update, and delete organizations",
|
||||
Tools: []string{"add_organization", "update_organization", "delete_organization"},
|
||||
},
|
||||
{
|
||||
Name: "permission:read",
|
||||
DisplayName: "Read Permissions",
|
||||
Description: "View permission list and details",
|
||||
Tools: []string{"get_permissions", "get_permission"},
|
||||
},
|
||||
{
|
||||
Name: "permission:write",
|
||||
DisplayName: "Manage Permissions",
|
||||
Description: "Create, update, and delete permissions",
|
||||
Tools: []string{"add_permission", "update_permission", "delete_permission"},
|
||||
},
|
||||
{
|
||||
Name: "role:read",
|
||||
DisplayName: "Read Roles",
|
||||
Description: "View role list and details",
|
||||
Tools: []string{"get_roles", "get_role"},
|
||||
},
|
||||
{
|
||||
Name: "role:write",
|
||||
DisplayName: "Manage Roles",
|
||||
Description: "Create, update, and delete roles",
|
||||
Tools: []string{"add_role", "update_role", "delete_role"},
|
||||
},
|
||||
{
|
||||
Name: "provider:read",
|
||||
DisplayName: "Read Providers",
|
||||
Description: "View provider list and details",
|
||||
Tools: []string{"get_providers", "get_provider"},
|
||||
},
|
||||
{
|
||||
Name: "provider:write",
|
||||
DisplayName: "Manage Providers",
|
||||
Description: "Create, update, and delete providers",
|
||||
Tools: []string{"add_provider", "update_provider", "delete_provider"},
|
||||
},
|
||||
{
|
||||
Name: "token:read",
|
||||
DisplayName: "Read Tokens",
|
||||
Description: "View token list and details",
|
||||
Tools: []string{"get_tokens", "get_token"},
|
||||
},
|
||||
{
|
||||
Name: "token:write",
|
||||
DisplayName: "Manage Tokens",
|
||||
Description: "Delete tokens",
|
||||
Tools: []string{"delete_token"},
|
||||
},
|
||||
}
|
||||
|
||||
// ConvenienceScopes defines alias scopes that expand to multiple resource scopes
|
||||
var ConvenienceScopes = map[string][]string{
|
||||
"read": {"application:read", "user:read", "organization:read", "permission:read", "role:read", "provider:read", "token:read"},
|
||||
"write": {"application:write", "user:write", "organization:write", "permission:write", "role:write", "provider:write", "token:write"},
|
||||
"admin": {"application:read", "application:write", "user:read", "user:write", "organization:read", "organization:write", "permission:read", "permission:write", "role:read", "role:write", "provider:read", "provider:write", "token:read", "token:write"},
|
||||
}
|
||||
|
||||
// GetToolsForScopes returns a map of tools allowed by the given scopes
|
||||
// The grantedScopes are the scopes present in the token
|
||||
// The registry contains the scope-to-tool mappings (either BuiltinScopes or Application.Scopes)
|
||||
func GetToolsForScopes(grantedScopes []string, registry []*object.ScopeItem) map[string]bool {
|
||||
allowed := make(map[string]bool)
|
||||
|
||||
// Expand convenience scopes first
|
||||
expandedScopes := make([]string, 0)
|
||||
for _, scopeName := range grantedScopes {
|
||||
if expansion, isConvenience := ConvenienceScopes[scopeName]; isConvenience {
|
||||
expandedScopes = append(expandedScopes, expansion...)
|
||||
} else {
|
||||
expandedScopes = append(expandedScopes, scopeName)
|
||||
}
|
||||
}
|
||||
|
||||
// Map scopes to tools
|
||||
for _, scopeName := range expandedScopes {
|
||||
for _, item := range registry {
|
||||
if item.Name == scopeName {
|
||||
for _, tool := range item.Tools {
|
||||
allowed[tool] = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allowed
|
||||
}
|
||||
|
||||
// GetRequiredScopeForTool returns the first scope that provides access to the given tool
|
||||
// Returns an empty string if no scope is found for the tool
|
||||
func GetRequiredScopeForTool(toolName string, registry []*object.ScopeItem) string {
|
||||
for _, scopeItem := range registry {
|
||||
for _, tool := range scopeItem.Tools {
|
||||
if tool == toolName {
|
||||
return scopeItem.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -41,7 +41,6 @@ type Adapter struct {
|
||||
Database string `xorm:"varchar(100)" json:"database"`
|
||||
|
||||
*xormadapter.Adapter `xorm:"-" json:"-"`
|
||||
engine *xorm.Engine
|
||||
}
|
||||
|
||||
func GetAdapterCount(owner, field, value string) (int64, error) {
|
||||
@@ -147,7 +146,7 @@ func (adapter *Adapter) GetId() string {
|
||||
}
|
||||
|
||||
func (adapter *Adapter) InitAdapter() error {
|
||||
if adapter.Adapter != nil && adapter.engine != nil {
|
||||
if adapter.Adapter != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -200,15 +199,11 @@ func (adapter *Adapter) InitAdapter() error {
|
||||
|
||||
tableName := adapter.Table
|
||||
|
||||
xa, err := xormadapter.NewAdapterByEngineWithTableName(engine, tableName, "")
|
||||
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(engine, tableName, "")
|
||||
if err != nil {
|
||||
_ = engine.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
adapter.engine = engine
|
||||
adapter.Adapter = xa
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
||||
"github.com/xorm-io/xorm"
|
||||
)
|
||||
|
||||
type SafeAdapter struct {
|
||||
*xormadapter.Adapter
|
||||
engine *xorm.Engine
|
||||
tableName string
|
||||
}
|
||||
|
||||
func NewSafeAdapter(a *Adapter) *SafeAdapter {
|
||||
if a == nil || a.Adapter == nil || a.engine == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &SafeAdapter{
|
||||
Adapter: a.Adapter,
|
||||
engine: a.engine,
|
||||
tableName: a.Table,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *SafeAdapter) RemovePolicy(sec string, ptype string, rule []string) error {
|
||||
line := a.buildCasbinRule(ptype, rule)
|
||||
|
||||
session := a.engine.NewSession()
|
||||
defer session.Close()
|
||||
|
||||
if a.tableName != "" {
|
||||
session = session.Table(a.tableName)
|
||||
}
|
||||
|
||||
_, err := session.
|
||||
MustCols("ptype", "v0", "v1", "v2", "v3", "v4", "v5").
|
||||
Delete(line)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *SafeAdapter) RemovePolicies(sec string, ptype string, rules [][]string) error {
|
||||
_, err := a.engine.Transaction(func(tx *xorm.Session) (interface{}, error) {
|
||||
for _, rule := range rules {
|
||||
line := a.buildCasbinRule(ptype, rule)
|
||||
|
||||
var session *xorm.Session
|
||||
if a.tableName != "" {
|
||||
session = tx.Table(a.tableName)
|
||||
} else {
|
||||
session = tx
|
||||
}
|
||||
|
||||
_, err := session.
|
||||
MustCols("ptype", "v0", "v1", "v2", "v3", "v4", "v5").
|
||||
Delete(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *SafeAdapter) buildCasbinRule(ptype string, rule []string) *xormadapter.CasbinRule {
|
||||
line := xormadapter.CasbinRule{Ptype: ptype}
|
||||
|
||||
l := len(rule)
|
||||
if l > 0 {
|
||||
line.V0 = rule[0]
|
||||
}
|
||||
if l > 1 {
|
||||
line.V1 = rule[1]
|
||||
}
|
||||
if l > 2 {
|
||||
line.V2 = rule[2]
|
||||
}
|
||||
if l > 3 {
|
||||
line.V3 = rule[3]
|
||||
}
|
||||
if l > 4 {
|
||||
line.V4 = rule[4]
|
||||
}
|
||||
if l > 5 {
|
||||
line.V5 = rule[5]
|
||||
}
|
||||
|
||||
return &line
|
||||
}
|
||||
118
object/agent.go
118
object/agent.go
@@ -1,118 +0,0 @@
|
||||
// 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 object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
|
||||
type Agent struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
|
||||
Url string `xorm:"varchar(500)" json:"url"`
|
||||
Token string `xorm:"varchar(500)" json:"token"`
|
||||
Application string `xorm:"varchar(100)" json:"application"`
|
||||
}
|
||||
|
||||
func GetAgents(owner string) ([]*Agent, error) {
|
||||
agents := []*Agent{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&agents, &Agent{Owner: owner})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return agents, nil
|
||||
}
|
||||
|
||||
func getAgent(owner string, name string) (*Agent, error) {
|
||||
agent := Agent{Owner: owner, Name: name}
|
||||
existed, err := ormer.Engine.Get(&agent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &agent, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func GetAgent(id string) (*Agent, error) {
|
||||
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
|
||||
return getAgent(owner, name)
|
||||
}
|
||||
|
||||
func UpdateAgent(id string, agent *Agent) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
|
||||
if a, err := getAgent(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if a == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
agent.UpdatedTime = util.GetCurrentTime()
|
||||
|
||||
_, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(agent)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func AddAgent(agent *Agent) (bool, error) {
|
||||
affected, err := ormer.Engine.Insert(agent)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func DeleteAgent(agent *Agent) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{agent.Owner, agent.Name}).Delete(&Agent{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func (agent *Agent) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", agent.Owner, agent.Name)
|
||||
}
|
||||
|
||||
func GetAgentCount(owner, field, value string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
return session.Count(&Agent{})
|
||||
}
|
||||
|
||||
func GetPaginationAgents(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Agent, error) {
|
||||
agents := []*Agent{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&agents)
|
||||
if err != nil {
|
||||
return agents, err
|
||||
}
|
||||
|
||||
return agents, nil
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -62,17 +61,9 @@ type SamlItem struct {
|
||||
}
|
||||
|
||||
type JwtItem struct {
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category"`
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type ScopeItem struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Description string `json:"description"`
|
||||
Tools []string `json:"tools"` // MCP tools allowed by this scope
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type Application struct {
|
||||
@@ -81,9 +72,6 @@ type Application struct {
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Category string `xorm:"varchar(20)" json:"category"`
|
||||
Type string `xorm:"varchar(20)" json:"type"`
|
||||
Scopes []*ScopeItem `xorm:"mediumtext" json:"scopes"`
|
||||
Logo string `xorm:"varchar(200)" json:"logo"`
|
||||
Title string `xorm:"varchar(100)" json:"title"`
|
||||
Favicon string `xorm:"varchar(200)" json:"favicon"`
|
||||
@@ -126,7 +114,6 @@ type Application struct {
|
||||
|
||||
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
||||
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
||||
ClientCert string `xorm:"varchar(100)" json:"clientCert"`
|
||||
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
|
||||
ForcedRedirectOrigin string `xorm:"varchar(100)" json:"forcedRedirectOrigin"`
|
||||
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
|
||||
@@ -156,17 +143,6 @@ type Application struct {
|
||||
FailedSigninLimit int `json:"failedSigninLimit"`
|
||||
FailedSigninFrozenTime int `json:"failedSigninFrozenTime"`
|
||||
CodeResendTimeout int `json:"codeResendTimeout"`
|
||||
|
||||
CustomScopes []*ScopeDescription `xorm:"mediumtext" json:"customScopes"`
|
||||
|
||||
// Reverse proxy fields
|
||||
Domain string `xorm:"varchar(100)" json:"domain"`
|
||||
OtherDomains []string `xorm:"varchar(1000)" json:"otherDomains"`
|
||||
UpstreamHost string `xorm:"varchar(100)" json:"upstreamHost"`
|
||||
SslMode string `xorm:"varchar(100)" json:"sslMode"`
|
||||
SslCert string `xorm:"varchar(100)" json:"sslCert"`
|
||||
|
||||
CertObj *Cert `xorm:"-"`
|
||||
}
|
||||
|
||||
func GetApplicationCount(owner, field, value string) (int64, error) {
|
||||
@@ -659,7 +635,7 @@ func GetMaskedApplications(applications []*Application, userId string) []*Applic
|
||||
|
||||
func GetAllowedApplications(applications []*Application, userId string, lang string) ([]*Application, error) {
|
||||
if userId == "" {
|
||||
return nil, errors.New(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
}
|
||||
|
||||
if isUserIdGlobalAdmin(userId) {
|
||||
@@ -671,7 +647,7 @@ func GetAllowedApplications(applications []*Application, userId string, lang str
|
||||
return nil, err
|
||||
}
|
||||
if user == nil {
|
||||
return nil, errors.New(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
}
|
||||
|
||||
if user.IsAdmin {
|
||||
@@ -693,21 +669,6 @@ func GetAllowedApplications(applications []*Application, userId string, lang str
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func checkMultipleCaptchaProviders(application *Application, lang string) error {
|
||||
var captchaProviders []string
|
||||
for _, providerItem := range application.Providers {
|
||||
if providerItem.Provider != nil && providerItem.Provider.Category == "Captcha" {
|
||||
captchaProviders = append(captchaProviders, providerItem.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(captchaProviders) > 1 {
|
||||
return fmt.Errorf(i18n.Translate(lang, "general:Multiple captcha providers are not allowed in the same application: %s"), strings.Join(captchaProviders, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateApplication(id string, application *Application, isGlobalAdmin bool, lang string) (bool, error) {
|
||||
owner, name, err := util.GetOwnerAndNameFromIdWithError(id)
|
||||
if err != nil {
|
||||
@@ -719,7 +680,7 @@ func UpdateApplication(id string, application *Application, isGlobalAdmin bool,
|
||||
}
|
||||
|
||||
if !isGlobalAdmin && oldApplication.Organization != application.Organization {
|
||||
return false, errors.New(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
return false, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
}
|
||||
|
||||
if name == "app-built-in" {
|
||||
@@ -746,16 +707,6 @@ func UpdateApplication(id string, application *Application, isGlobalAdmin bool,
|
||||
return false, fmt.Errorf("only applications belonging to built-in organization can be shared")
|
||||
}
|
||||
|
||||
err = checkMultipleCaptchaProviders(application, lang)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = validateCustomScopes(application.CustomScopes, lang)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, providerItem := range application.Providers {
|
||||
providerItem.Provider = nil
|
||||
}
|
||||
@@ -811,11 +762,6 @@ func AddApplication(application *Application) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
err = validateCustomScopes(application.CustomScopes, "en")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, providerItem := range application.Providers {
|
||||
providerItem.Provider = nil
|
||||
}
|
||||
|
||||
121
object/cert.go
121
object/cert.go
@@ -16,12 +16,9 @@ package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/certificate"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
type Cert struct {
|
||||
@@ -36,13 +33,6 @@ type Cert struct {
|
||||
BitSize int `json:"bitSize"`
|
||||
ExpireInYears int `json:"expireInYears"`
|
||||
|
||||
ExpireTime string `xorm:"varchar(100)" json:"expireTime"`
|
||||
DomainExpireTime string `xorm:"varchar(100)" json:"domainExpireTime"`
|
||||
Provider string `xorm:"varchar(100)" json:"provider"`
|
||||
Account string `xorm:"varchar(100)" json:"account"`
|
||||
AccessKey string `xorm:"varchar(100)" json:"accessKey"`
|
||||
AccessSecret string `xorm:"varchar(100)" json:"accessSecret"`
|
||||
|
||||
Certificate string `xorm:"mediumtext" json:"certificate"`
|
||||
PrivateKey string `xorm:"mediumtext" json:"privateKey"`
|
||||
}
|
||||
@@ -234,20 +224,6 @@ func (p *Cert) populateContent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.Type == "SSL" {
|
||||
if p.Certificate != "" {
|
||||
expireTime, err := util.GetCertExpireTime(p.Certificate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.ExpireTime = expireTime
|
||||
} else {
|
||||
p.ExpireTime = ""
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(p.CryptoAlgorithm) < 3 {
|
||||
err := fmt.Errorf("populateContent() error, unsupported crypto algorithm: %s", p.CryptoAlgorithm)
|
||||
return err
|
||||
@@ -282,42 +258,6 @@ func (p *Cert) populateContent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func RenewCert(cert *Cert) (bool, error) {
|
||||
useProxy := false
|
||||
if cert.Provider == "GoDaddy" {
|
||||
useProxy = true
|
||||
}
|
||||
|
||||
client, err := GetAcmeClient(useProxy)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var certStr, privateKey string
|
||||
if cert.Provider == "Aliyun" {
|
||||
certStr, privateKey, err = certificate.ObtainCertificateAli(client, cert.Name, cert.AccessKey, cert.AccessSecret)
|
||||
} else if cert.Provider == "GoDaddy" {
|
||||
certStr, privateKey, err = certificate.ObtainCertificateGoDaddy(client, cert.Name, cert.AccessKey, cert.AccessSecret)
|
||||
} else {
|
||||
return false, fmt.Errorf("unknown provider: %s", cert.Provider)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
expireTime, err := util.GetCertExpireTime(certStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
cert.ExpireTime = expireTime
|
||||
cert.Certificate = certStr
|
||||
cert.PrivateKey = privateKey
|
||||
|
||||
return UpdateCert(cert.GetId(), cert)
|
||||
}
|
||||
|
||||
func getCertByApplication(application *Application) (*Cert, error) {
|
||||
if application.Cert != "" {
|
||||
return getCertByName(application.Cert)
|
||||
@@ -348,64 +288,3 @@ func certChangeTrigger(oldName string, newName string) error {
|
||||
|
||||
return session.Commit()
|
||||
}
|
||||
|
||||
func getBaseDomain(domain string) (string, error) {
|
||||
// abc.com -> abc.com
|
||||
// abc.com.it -> abc.com.it
|
||||
// subdomain.abc.io -> abc.io
|
||||
// subdomain.abc.org.us -> abc.org.us
|
||||
return publicsuffix.EffectiveTLDPlusOne(domain)
|
||||
}
|
||||
|
||||
func GetCertByDomain(domain string) (*Cert, error) {
|
||||
if domain == "" {
|
||||
return nil, fmt.Errorf("GetCertByDomain() error: domain should not be empty")
|
||||
}
|
||||
|
||||
cert, ok := certMap[domain]
|
||||
if ok {
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
baseDomain, err := getBaseDomain(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert, ok = certMap[baseDomain]
|
||||
if ok {
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func getCertMap() (map[string]*Cert, error) {
|
||||
certs, err := GetGlobalCerts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := map[string]*Cert{}
|
||||
for _, cert := range certs {
|
||||
res[cert.Name] = cert
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (p *Cert) isCertNearExpire() (bool, error) {
|
||||
if p.ExpireTime == "" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
expireTime, err := time.Parse(time.RFC3339, p.ExpireTime)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
duration := expireTime.Sub(now)
|
||||
res := duration <= 7*24*time.Hour
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/likexian/whois"
|
||||
whoisparser "github.com/likexian/whois-parser"
|
||||
)
|
||||
|
||||
func getDomainExpireTime(domainName string) (string, error) {
|
||||
domainName, err := util.GetBaseDomain(domainName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
server := ""
|
||||
if strings.HasSuffix(domainName, ".com") || strings.HasSuffix(domainName, ".net") {
|
||||
server = "whois.verisign-grs.com"
|
||||
} else if strings.HasSuffix(domainName, ".org") {
|
||||
server = "whois.pir.org"
|
||||
} else if strings.HasSuffix(domainName, ".io") {
|
||||
server = "whois.nic.io"
|
||||
} else if strings.HasSuffix(domainName, ".co") {
|
||||
server = "whois.nic.co"
|
||||
} else if strings.HasSuffix(domainName, ".cn") {
|
||||
server = "whois.cnnic.cn"
|
||||
} else if strings.HasSuffix(domainName, ".run") {
|
||||
server = "whois.nic.run"
|
||||
} else {
|
||||
server = "grs-whois.hichina.com" // com, net, cc, tv
|
||||
}
|
||||
|
||||
client := whois.NewClient()
|
||||
//if server != "whois.cnnic.cn" && server != "grs-whois.hichina.com" {
|
||||
// dialer := proxy.GetProxyDialer()
|
||||
// if dialer != nil {
|
||||
// client.SetDialer(dialer)
|
||||
// }
|
||||
//}
|
||||
|
||||
data, err := client.Whois(domainName, server)
|
||||
if err != nil {
|
||||
if !strings.HasSuffix(domainName, ".run") || data == "" {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
whoisInfo, err := whoisparser.Parse(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
res := whoisInfo.Domain.ExpirationDateInTime.Local().Format(time.RFC3339)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func GetDomainExpireTime(domainName string) (string, error) {
|
||||
return getDomainExpireTime(domainName)
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -193,7 +192,7 @@ func CheckInvitationDefaultCode(code string, defaultCode string, lang string) er
|
||||
if matched, err := util.IsInvitationCodeMatch(code, defaultCode); err != nil {
|
||||
return err
|
||||
} else if !matched {
|
||||
return errors.New(i18n.Translate(lang, "check:Default code does not match the code's matching rules"))
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Default code does not match the code's matching rules"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -226,7 +225,7 @@ func checkSigninErrorTimes(user *User, lang string) error {
|
||||
|
||||
func CheckPassword(user *User, password string, lang string, options ...bool) error {
|
||||
if password == "" {
|
||||
return errors.New(i18n.Translate(lang, "check:Password cannot be empty"))
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Password cannot be empty"))
|
||||
}
|
||||
|
||||
enableCaptcha := false
|
||||
@@ -247,7 +246,7 @@ func CheckPassword(user *User, password string, lang string, options ...bool) er
|
||||
return err
|
||||
}
|
||||
if organization == nil {
|
||||
return errors.New(i18n.Translate(lang, "check:Organization does not exist"))
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Organization does not exist"))
|
||||
}
|
||||
|
||||
passwordType := user.PasswordType
|
||||
@@ -336,7 +335,7 @@ func CheckLdapUserPassword(user *User, password string, lang string, options ...
|
||||
}
|
||||
if len(searchResult.Entries) > 1 {
|
||||
conn.Close()
|
||||
return errors.New(i18n.Translate(lang, "check:Multiple accounts with same uid, please check your ldap server"))
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Multiple accounts with same uid, please check your ldap server"))
|
||||
}
|
||||
|
||||
hit = true
|
||||
@@ -378,12 +377,12 @@ func CheckUserPassword(organization string, username string, password string, la
|
||||
}
|
||||
|
||||
if user.IsForbidden {
|
||||
return nil, errors.New(i18n.Translate(lang, "check:The user is forbidden to sign in, please contact the administrator"))
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user is forbidden to sign in, please contact the administrator"))
|
||||
}
|
||||
|
||||
// Prevent direct login for guest users without upgrading
|
||||
if user.Tag == "guest-user" {
|
||||
return nil, errors.New(i18n.Translate(lang, "check:Guest users must upgrade their account by setting a username and password before they can sign in directly"))
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:Guest users must upgrade their account by setting a username and password before they can sign in directly"))
|
||||
}
|
||||
|
||||
if isSigninViaLdap {
|
||||
@@ -394,7 +393,7 @@ func CheckUserPassword(organization string, username string, password string, la
|
||||
|
||||
if user.Ldap != "" {
|
||||
if !isSigninViaLdap && !isPasswordWithLdapEnabled {
|
||||
return nil, errors.New(i18n.Translate(lang, "check:password or code is incorrect"))
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect"))
|
||||
}
|
||||
|
||||
// only for LDAP users
|
||||
@@ -423,7 +422,7 @@ func CheckUserPassword(organization string, username string, password string, la
|
||||
|
||||
func CheckUserPermission(requestUserId, userId string, strict bool, lang string) (bool, error) {
|
||||
if requestUserId == "" {
|
||||
return false, errors.New(i18n.Translate(lang, "general:Please login first"))
|
||||
return false, fmt.Errorf(i18n.Translate(lang, "general:Please login first"))
|
||||
}
|
||||
|
||||
userOwner := util.GetOwnerFromId(userId)
|
||||
@@ -455,7 +454,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool, lang string)
|
||||
}
|
||||
|
||||
if requestUser == nil {
|
||||
return false, errors.New(i18n.Translate(lang, "check:Session outdated, please login again"))
|
||||
return false, fmt.Errorf(i18n.Translate(lang, "check:Session outdated, please login again"))
|
||||
}
|
||||
if requestUser.IsGlobalAdmin() {
|
||||
hasPermission = true
|
||||
@@ -470,7 +469,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool, lang string)
|
||||
}
|
||||
}
|
||||
|
||||
return hasPermission, errors.New(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
return hasPermission, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
|
||||
}
|
||||
|
||||
func CheckApiPermission(userId string, organization string, path string, method string) (bool, error) {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
@@ -35,7 +34,7 @@ func CheckEntryIp(clientIp string, user *User, application *Application, organiz
|
||||
if user != nil {
|
||||
err = isEntryIpAllowd(user.IpWhitelist, entryIp, lang)
|
||||
if err != nil {
|
||||
return errors.New(err.Error() + user.Name)
|
||||
return fmt.Errorf(err.Error() + user.Name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +42,7 @@ func CheckEntryIp(clientIp string, user *User, application *Application, organiz
|
||||
err = isEntryIpAllowd(application.IpWhitelist, entryIp, lang)
|
||||
if err != nil {
|
||||
application.IpRestriction = err.Error() + application.Name
|
||||
return errors.New(err.Error() + application.Name)
|
||||
return fmt.Errorf(err.Error() + application.Name)
|
||||
} else {
|
||||
application.IpRestriction = ""
|
||||
}
|
||||
@@ -57,7 +56,7 @@ func CheckEntryIp(clientIp string, user *User, application *Application, organiz
|
||||
err = isEntryIpAllowd(organization.IpWhitelist, entryIp, lang)
|
||||
if err != nil {
|
||||
organization.IpRestriction = err.Error() + organization.Name
|
||||
return errors.New(err.Error() + organization.Name)
|
||||
return fmt.Errorf(err.Error() + organization.Name)
|
||||
} else {
|
||||
organization.IpRestriction = ""
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
@@ -28,7 +28,7 @@ func checkPasswordExpired(user *User, lang string) error {
|
||||
return err
|
||||
}
|
||||
if organization == nil {
|
||||
return errors.New(i18n.Translate(lang, "check:Organization does not exist"))
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Organization does not exist"))
|
||||
}
|
||||
|
||||
passwordExpireDays := organization.PasswordExpireDays
|
||||
@@ -39,7 +39,7 @@ func checkPasswordExpired(user *User, lang string) error {
|
||||
lastChangePasswordTime := user.LastChangePasswordTime
|
||||
if lastChangePasswordTime == "" {
|
||||
if user.CreatedTime == "" {
|
||||
return errors.New(i18n.Translate(lang, "check:Your password has expired. Please reset your password by clicking \"Forgot password\""))
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Your password has expired. Please reset your password by clicking \"Forgot password\""))
|
||||
}
|
||||
lastChangePasswordTime = user.CreatedTime
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func checkPasswordExpired(user *User, lang string) error {
|
||||
lastTime := util.String2Time(lastChangePasswordTime)
|
||||
expireTime := lastTime.AddDate(0, 0, passwordExpireDays)
|
||||
if time.Now().After(expireTime) {
|
||||
return errors.New(i18n.Translate(lang, "check:Your password has expired. Please reset your password by clicking \"Forgot password\""))
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Your password has expired. Please reset your password by clicking \"Forgot password\""))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -100,7 +99,7 @@ func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
|
||||
|
||||
leftChances := failedSigninLimit - user.SigninWrongTimes
|
||||
if leftChances == 0 && enableCaptcha {
|
||||
return errors.New(i18n.Translate(lang, "check:password or code is incorrect"))
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect"))
|
||||
} else if leftChances >= 0 {
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect, you have %s remaining chances"), strconv.Itoa(leftChances))
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ import "github.com/casdoor/casdoor/email"
|
||||
|
||||
// TestSmtpServer Test the SMTP server
|
||||
func TestSmtpServer(provider *Provider) error {
|
||||
sslMode := getSslMode(provider)
|
||||
smtpEmailProvider := email.NewSmtpEmailProvider(provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.Type, sslMode, provider.EnableProxy)
|
||||
smtpEmailProvider := email.NewSmtpEmailProvider(provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.Type, provider.DisableSsl, provider.EnableProxy)
|
||||
sender, err := smtpEmailProvider.Dialer.Dial()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -32,8 +31,7 @@ func TestSmtpServer(provider *Provider) error {
|
||||
}
|
||||
|
||||
func SendEmail(provider *Provider, title string, content string, dest []string, sender string) error {
|
||||
sslMode := getSslMode(provider)
|
||||
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, sslMode, provider.Endpoint, provider.Method, provider.HttpHeaders, provider.UserMapping, provider.IssuerUrl, provider.EnableProxy)
|
||||
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl, provider.Endpoint, provider.Method, provider.HttpHeaders, provider.UserMapping, provider.IssuerUrl, provider.EnableProxy)
|
||||
|
||||
fromAddress := provider.ClientId2
|
||||
if fromAddress == "" {
|
||||
@@ -47,19 +45,3 @@ func SendEmail(provider *Provider, title string, content string, dest []string,
|
||||
|
||||
return emailProvider.Send(fromAddress, fromName, dest, title, content)
|
||||
}
|
||||
|
||||
// getSslMode returns the SSL mode for the provider, with backward compatibility for DisableSsl
|
||||
func getSslMode(provider *Provider) string {
|
||||
// If SslMode is set, use it
|
||||
if provider.SslMode != "" {
|
||||
return provider.SslMode
|
||||
}
|
||||
|
||||
// Backward compatibility: convert DisableSsl to SslMode
|
||||
if provider.DisableSsl {
|
||||
return "Disable"
|
||||
}
|
||||
|
||||
// Default to "Auto" for new configurations or when DisableSsl is false
|
||||
return "Auto"
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ func (enforcer *Enforcer) InitEnforcer() error {
|
||||
return err
|
||||
}
|
||||
|
||||
casbinEnforcer, err := casbin.NewEnforcer(m.Model, NewSafeAdapter(a))
|
||||
casbinEnforcer, err := casbin.NewEnforcer(m.Model, a.Adapter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
137
object/entry.go
137
object/entry.go
@@ -1,137 +0,0 @@
|
||||
// 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 object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
|
||||
type Entry struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
|
||||
Provider string `xorm:"varchar(100)" json:"provider"`
|
||||
Application string `xorm:"varchar(100)" json:"application"`
|
||||
|
||||
Url string `xorm:"varchar(500)" json:"url"`
|
||||
Token string `xorm:"varchar(500)" json:"token"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
Message string `xorm:"mediumtext" json:"message"`
|
||||
}
|
||||
|
||||
func NewTraceEntry(message []byte) *Entry {
|
||||
currentTime := util.GetCurrentTime()
|
||||
traceId := fmt.Sprintf("trace_%s_%s", util.GenerateSimpleTimeId(), util.GetRandomName())
|
||||
|
||||
return &Entry{
|
||||
Owner: CasdoorOrganization,
|
||||
Name: traceId,
|
||||
CreatedTime: currentTime,
|
||||
UpdatedTime: currentTime,
|
||||
DisplayName: traceId,
|
||||
Type: "trace",
|
||||
Message: string(message),
|
||||
}
|
||||
}
|
||||
|
||||
func GetEntries(owner string) ([]*Entry, error) {
|
||||
entries := []*Entry{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&entries, &Entry{Owner: owner})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func getEntry(owner string, name string) (*Entry, error) {
|
||||
entry := Entry{Owner: owner, Name: name}
|
||||
existed, err := ormer.Engine.Get(&entry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &entry, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func GetEntry(id string) (*Entry, error) {
|
||||
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
|
||||
return getEntry(owner, name)
|
||||
}
|
||||
|
||||
func UpdateEntry(id string, entry *Entry) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
|
||||
if e, err := getEntry(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if e == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
entry.UpdatedTime = util.GetCurrentTime()
|
||||
|
||||
_, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(entry)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func AddEntry(entry *Entry) (bool, error) {
|
||||
affected, err := ormer.Engine.Insert(entry)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func DeleteEntry(entry *Entry) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{entry.Owner, entry.Name}).Delete(&Entry{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func (entry *Entry) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", entry.Owner, entry.Name)
|
||||
}
|
||||
|
||||
func GetEntryCount(owner, field, value string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
return session.Count(&Entry{})
|
||||
}
|
||||
|
||||
func GetPaginationEntries(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Entry, error) {
|
||||
entries := []*Entry{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&entries)
|
||||
if err != nil {
|
||||
return entries, err
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -50,12 +49,7 @@ func GetDashboard(owner string) (*map[string][]int64, error) {
|
||||
dashboard[tableName+"Counts"] = make([]int64, 31)
|
||||
tableFullName := tableNamePrefix + tableName
|
||||
go func(ch chan error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ch <- fmt.Errorf("panic in dashboard goroutine: %v", r)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
defer wg.Done()
|
||||
dashboardDateItems := []DashboardDateItem{}
|
||||
var countResult int64
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user