mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-21 03:10:33 +08:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
91fa024f0b | ||
![]() |
aedef1eea1 | ||
![]() |
70f2988f09 | ||
![]() |
2dcdfbe6d3 | ||
![]() |
c92d34e27c | ||
![]() |
dfbf7753c3 | ||
![]() |
ba732b3075 | ||
![]() |
ca13247572 | ||
![]() |
108fdc174f | ||
![]() |
a741c5179a | ||
![]() |
6676cc8ff3 | ||
![]() |
13de019d08 | ||
![]() |
53ad454962 | ||
![]() |
fb203a6f30 | ||
![]() |
f716a0985f | ||
![]() |
340fbe135d | ||
![]() |
79119760f2 | ||
![]() |
4dd67a8dcb | ||
![]() |
deed857788 | ||
![]() |
802995ed16 | ||
![]() |
b14554a5ba | ||
![]() |
4665ffa759 | ||
![]() |
f914e8e929 | ||
![]() |
dc33b41107 | ||
![]() |
ee8dd23a56 |
25
.github/workflows/build.yml
vendored
25
.github/workflows/build.yml
vendored
@@ -35,6 +35,10 @@ jobs:
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14.17.0'
|
||||
# cache
|
||||
- uses: c-hive/gha-yarn-cache@v2
|
||||
with:
|
||||
directory: ./web
|
||||
- run: yarn install && CI=false yarn run build
|
||||
working-directory: ./web
|
||||
|
||||
@@ -53,11 +57,30 @@ jobs:
|
||||
go build -race -ldflags "-extldflags '-static'"
|
||||
working-directory: ./
|
||||
|
||||
linter:
|
||||
name: Go-Linter
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ go-tests ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '^1.16.5'
|
||||
|
||||
# gen a dummy config file
|
||||
- run: touch dummy.yml
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: --disable-all -c dummy.yml -E=gofumpt --max-same-issues=0 --timeout 5m --modules-download-mode=mod
|
||||
|
||||
release-and-push:
|
||||
name: Release And Push
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
|
||||
needs: [ frontend, backend ]
|
||||
needs: [ frontend, backend, linter ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
@@ -31,7 +31,7 @@ run:
|
||||
- api
|
||||
# skip-files:
|
||||
# - ".*_test\\.go$"
|
||||
modules-download-mode: vendor
|
||||
modules-download-mode: mod
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
lll:
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
@@ -80,7 +80,7 @@ func (captcha *AliyunCaptchaProvider) VerifyCaptcha(token, clientSecret string)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
@@ -58,7 +58,7 @@ func (captcha *GEETESTCaptchaProvider) VerifyCaptcha(token, clientSecret string)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ package captcha
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -43,7 +43,7 @@ func (captcha *HCaptchaProvider) VerifyCaptcha(token, clientSecret string) (bool
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ package captcha
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -43,7 +43,7 @@ func (captcha *ReCaptchaProvider) VerifyCaptcha(token, clientSecret string) (boo
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@@ -15,4 +15,5 @@ socks5Proxy = "127.0.0.1:10808"
|
||||
verificationCodeTimeout = 10
|
||||
initScore = 2000
|
||||
logPostOnly = true
|
||||
origin =
|
||||
origin =
|
||||
staticBaseUrl = "https://cdn.casbin.org"
|
@@ -11,6 +11,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package conf
|
||||
|
||||
import (
|
||||
|
@@ -274,6 +274,7 @@ func (c *ApiController) GetAccount() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetUserinfo
|
||||
// UserInfo
|
||||
// @Title UserInfo
|
||||
// @Tag Account API
|
||||
|
@@ -118,7 +118,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
}
|
||||
|
||||
} else {
|
||||
resp = wrapErrorResponse(fmt.Errorf("Unknown response type: %s", form.Type))
|
||||
resp = wrapErrorResponse(fmt.Errorf("unknown response type: %s", form.Type))
|
||||
}
|
||||
|
||||
// if user did not check auto signin
|
||||
|
@@ -23,11 +23,13 @@ import (
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// ApiController
|
||||
// controller for handlers under /api uri
|
||||
type ApiController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
// RootController
|
||||
// controller for handlers directly under / (root)
|
||||
type RootController struct {
|
||||
ApiController
|
||||
|
@@ -31,7 +31,7 @@ const (
|
||||
InvalidProxyCallback string = "INVALID_PROXY_CALLBACK"
|
||||
InvalidTicket string = "INVALID_TICKET"
|
||||
InvalidService string = "INVALID_SERVICE"
|
||||
InteralError string = "INTERNAL_ERROR"
|
||||
InternalError string = "INTERNAL_ERROR"
|
||||
UnauthorizedService string = "UNAUTHORIZED_SERVICE"
|
||||
)
|
||||
|
||||
@@ -116,7 +116,7 @@ func (c *RootController) CasP3ServiceAndProxyValidate() {
|
||||
}
|
||||
// make a request to pgturl passing pgt and pgtiou
|
||||
if err != nil {
|
||||
c.sendCasAuthenticationResponseErr(InteralError, err.Error(), format)
|
||||
c.sendCasAuthenticationResponseErr(InternalError, err.Error(), format)
|
||||
return
|
||||
}
|
||||
param := pgtUrlObj.Query()
|
||||
@@ -126,7 +126,7 @@ func (c *RootController) CasP3ServiceAndProxyValidate() {
|
||||
|
||||
request, err := http.NewRequest("GET", pgtUrlObj.String(), nil)
|
||||
if err != nil {
|
||||
c.sendCasAuthenticationResponseErr(InteralError, err.Error(), format)
|
||||
c.sendCasAuthenticationResponseErr(InternalError, err.Error(), format)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ func (c *RootController) SamlValidate() {
|
||||
return
|
||||
}
|
||||
|
||||
envelopReponse := struct {
|
||||
envelopResponse := struct {
|
||||
XMLName xml.Name `xml:"SOAP-ENV:Envelope"`
|
||||
Xmlns string `xml:"xmlns:SOAP-ENV"`
|
||||
Body struct {
|
||||
@@ -222,15 +222,15 @@ func (c *RootController) SamlValidate() {
|
||||
Content string `xml:",innerxml"`
|
||||
}
|
||||
}{}
|
||||
envelopReponse.Xmlns = "http://schemas.xmlsoap.org/soap/envelope/"
|
||||
envelopReponse.Body.Content = response
|
||||
envelopResponse.Xmlns = "http://schemas.xmlsoap.org/soap/envelope/"
|
||||
envelopResponse.Body.Content = response
|
||||
|
||||
data, err := xml.Marshal(envelopReponse)
|
||||
data, err := xml.Marshal(envelopResponse)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.Ctx.Output.Body([]byte(data))
|
||||
c.Ctx.Output.Body(data)
|
||||
}
|
||||
|
||||
func (c *RootController) sendCasProxyResponseErr(code, msg, format string) {
|
||||
|
@@ -48,6 +48,7 @@ func (c *ApiController) GetCerts() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetCert
|
||||
// @Title GetCert
|
||||
// @Tag Cert API
|
||||
// @Description get cert
|
||||
@@ -61,6 +62,7 @@ func (c *ApiController) GetCert() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateCert
|
||||
// @Title UpdateCert
|
||||
// @Tag Cert API
|
||||
// @Description update cert
|
||||
@@ -81,6 +83,7 @@ func (c *ApiController) UpdateCert() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddCert
|
||||
// @Title AddCert
|
||||
// @Tag Cert API
|
||||
// @Description add cert
|
||||
@@ -98,6 +101,7 @@ func (c *ApiController) AddCert() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteCert
|
||||
// @Title DeleteCert
|
||||
// @Tag Cert API
|
||||
// @Description delete cert
|
||||
|
88
controllers/enforcer.go
Normal file
88
controllers/enforcer.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
)
|
||||
|
||||
func (c *ApiController) Enforce() {
|
||||
userId := c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError("Please sign in first")
|
||||
return
|
||||
}
|
||||
|
||||
var permissionRule object.PermissionRule
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permissionRule)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.Enforce(userId, &permissionRule)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) BatchEnforce() {
|
||||
userId := c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError("Please sign in first")
|
||||
return
|
||||
}
|
||||
|
||||
var permissionRules []object.PermissionRule
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permissionRules)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.BatchEnforce(userId, permissionRules)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) GetAllObjects() {
|
||||
userId := c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError("Please sign in first")
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetAllObjects(userId)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) GetAllActions() {
|
||||
userId := c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError("Please sign in first")
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetAllActions(userId)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) GetAllRoles() {
|
||||
userId := c.GetSessionUsername()
|
||||
if userId == "" {
|
||||
c.ResponseError("Please sign in first")
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetAllRoles(userId)
|
||||
c.ServeJSON()
|
||||
}
|
@@ -44,6 +44,7 @@ type LdapSyncResp struct {
|
||||
Failed []object.LdapRespUser `json:"failed"`
|
||||
}
|
||||
|
||||
// GetLdapUser
|
||||
// @Tag Account API
|
||||
// @Title GetLdapser
|
||||
// @router /get-ldap-user [post]
|
||||
@@ -100,6 +101,7 @@ func (c *ApiController) GetLdapUser() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetLdaps
|
||||
// @Tag Account API
|
||||
// @Title GetLdaps
|
||||
// @router /get-ldaps [post]
|
||||
@@ -110,6 +112,7 @@ func (c *ApiController) GetLdaps() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetLdap
|
||||
// @Tag Account API
|
||||
// @Title GetLdap
|
||||
// @router /get-ldap [post]
|
||||
@@ -125,6 +128,7 @@ func (c *ApiController) GetLdap() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddLdap
|
||||
// @Tag Account API
|
||||
// @Title AddLdap
|
||||
// @router /add-ldap [post]
|
||||
@@ -159,6 +163,7 @@ func (c *ApiController) AddLdap() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateLdap
|
||||
// @Tag Account API
|
||||
// @Title UpdateLdap
|
||||
// @router /update-ldap [post]
|
||||
@@ -186,6 +191,7 @@ func (c *ApiController) UpdateLdap() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteLdap
|
||||
// @Tag Account API
|
||||
// @Title DeleteLdap
|
||||
// @router /delete-ldap [post]
|
||||
@@ -201,6 +207,7 @@ func (c *ApiController) DeleteLdap() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// SyncLdapUsers
|
||||
// @Tag Account API
|
||||
// @Title SyncLdapUsers
|
||||
// @router /sync-ldap-users [post]
|
||||
@@ -223,6 +230,7 @@ func (c *ApiController) SyncLdapUsers() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// CheckLdapUsersExist
|
||||
// @Tag Account API
|
||||
// @Title CheckLdapUserExist
|
||||
// @router /check-ldap-users-exist [post]
|
||||
|
@@ -16,6 +16,7 @@ package controllers
|
||||
|
||||
import "github.com/casdoor/casdoor/object"
|
||||
|
||||
// GetOidcDiscovery
|
||||
// @Title GetOidcDiscovery
|
||||
// @Tag OIDC API
|
||||
// @Description Get Oidc Discovery
|
||||
@@ -27,6 +28,7 @@ func (c *RootController) GetOidcDiscovery() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetJwks
|
||||
// @Title GetJwks
|
||||
// @Tag OIDC API
|
||||
// @Success 200 {object} jose.JSONWebKey
|
||||
|
@@ -67,6 +67,7 @@ func (c *ApiController) GetUserPayments() {
|
||||
c.ResponseOk(payments)
|
||||
}
|
||||
|
||||
// GetPayment
|
||||
// @Title GetPayment
|
||||
// @Tag Payment API
|
||||
// @Description get payment
|
||||
@@ -80,6 +81,7 @@ func (c *ApiController) GetPayment() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdatePayment
|
||||
// @Title UpdatePayment
|
||||
// @Tag Payment API
|
||||
// @Description update payment
|
||||
@@ -100,6 +102,7 @@ func (c *ApiController) UpdatePayment() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddPayment
|
||||
// @Title AddPayment
|
||||
// @Tag Payment API
|
||||
// @Description add payment
|
||||
@@ -117,6 +120,7 @@ func (c *ApiController) AddPayment() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeletePayment
|
||||
// @Title DeletePayment
|
||||
// @Tag Payment API
|
||||
// @Description delete payment
|
||||
@@ -134,6 +138,7 @@ func (c *ApiController) DeletePayment() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// NotifyPayment
|
||||
// @Title NotifyPayment
|
||||
// @Tag Payment API
|
||||
// @Description notify payment
|
||||
@@ -159,6 +164,7 @@ func (c *ApiController) NotifyPayment() {
|
||||
}
|
||||
}
|
||||
|
||||
// InvoicePayment
|
||||
// @Title InvoicePayment
|
||||
// @Tag Payment API
|
||||
// @Description invoice payment
|
||||
|
@@ -48,6 +48,25 @@ func (c *ApiController) GetPermissions() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetPermissionsBySubmitter
|
||||
// @Title GetPermissionsBySubmitter
|
||||
// @Tag Permission API
|
||||
// @Description get permissions by submitter
|
||||
// @Success 200 {array} object.Permission The Response object
|
||||
// @router /get-permissions-by-submitter [get]
|
||||
func (c *ApiController) GetPermissionsBySubmitter() {
|
||||
userId, ok := c.RequireSignedIn()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
owner, username := util.GetOwnerAndNameFromId(userId)
|
||||
permissions := object.GetPermissionsBySubmitter(owner, username)
|
||||
c.ResponseOk(permissions, len(permissions))
|
||||
return
|
||||
}
|
||||
|
||||
// GetPermission
|
||||
// @Title GetPermission
|
||||
// @Tag Permission API
|
||||
// @Description get permission
|
||||
@@ -61,6 +80,7 @@ func (c *ApiController) GetPermission() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdatePermission
|
||||
// @Title UpdatePermission
|
||||
// @Tag Permission API
|
||||
// @Description update permission
|
||||
@@ -81,6 +101,7 @@ func (c *ApiController) UpdatePermission() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddPermission
|
||||
// @Title AddPermission
|
||||
// @Tag Permission API
|
||||
// @Description add permission
|
||||
@@ -98,6 +119,7 @@ func (c *ApiController) AddPermission() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeletePermission
|
||||
// @Title DeletePermission
|
||||
// @Tag Permission API
|
||||
// @Description delete permission
|
||||
|
@@ -49,6 +49,7 @@ func (c *ApiController) GetProducts() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetProduct
|
||||
// @Title GetProduct
|
||||
// @Tag Product API
|
||||
// @Description get product
|
||||
@@ -65,6 +66,7 @@ func (c *ApiController) GetProduct() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateProduct
|
||||
// @Title UpdateProduct
|
||||
// @Tag Product API
|
||||
// @Description update product
|
||||
@@ -85,6 +87,7 @@ func (c *ApiController) UpdateProduct() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddProduct
|
||||
// @Title AddProduct
|
||||
// @Tag Product API
|
||||
// @Description add product
|
||||
@@ -102,6 +105,7 @@ func (c *ApiController) AddProduct() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteProduct
|
||||
// @Title DeleteProduct
|
||||
// @Tag Product API
|
||||
// @Description delete product
|
||||
@@ -119,6 +123,7 @@ func (c *ApiController) DeleteProduct() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// BuyProduct
|
||||
// @Title BuyProduct
|
||||
// @Tag Product API
|
||||
// @Description buy product
|
||||
|
@@ -48,6 +48,7 @@ func (c *ApiController) GetProviders() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetProvider
|
||||
// @Title GetProvider
|
||||
// @Tag Provider API
|
||||
// @Description get provider
|
||||
@@ -61,6 +62,7 @@ func (c *ApiController) GetProvider() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateProvider
|
||||
// @Title UpdateProvider
|
||||
// @Tag Provider API
|
||||
// @Description update provider
|
||||
@@ -81,6 +83,7 @@ func (c *ApiController) UpdateProvider() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddProvider
|
||||
// @Title AddProvider
|
||||
// @Tag Provider API
|
||||
// @Description add provider
|
||||
@@ -98,6 +101,7 @@ func (c *ApiController) AddProvider() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteProvider
|
||||
// @Title DeleteProvider
|
||||
// @Tag Provider API
|
||||
// @Description delete provider
|
||||
|
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetResources
|
||||
// @router /get-resources [get]
|
||||
// @Tag Resource API
|
||||
// @Title GetResources
|
||||
@@ -50,6 +51,7 @@ func (c *ApiController) GetResources() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetResource
|
||||
// @Tag Resource API
|
||||
// @Title GetResource
|
||||
// @router /get-resource [get]
|
||||
@@ -60,6 +62,7 @@ func (c *ApiController) GetResource() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateResource
|
||||
// @Tag Resource API
|
||||
// @Title UpdateResource
|
||||
// @router /update-resource [post]
|
||||
@@ -76,6 +79,7 @@ func (c *ApiController) UpdateResource() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddResource
|
||||
// @Tag Resource API
|
||||
// @Title AddResource
|
||||
// @router /add-resource [post]
|
||||
@@ -90,6 +94,7 @@ func (c *ApiController) AddResource() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteResource
|
||||
// @Tag Resource API
|
||||
// @Title DeleteResource
|
||||
// @router /delete-resource [post]
|
||||
@@ -115,6 +120,7 @@ func (c *ApiController) DeleteResource() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UploadResource
|
||||
// @Tag Resource API
|
||||
// @Title UploadResource
|
||||
// @router /upload-resource [post]
|
||||
|
@@ -48,6 +48,7 @@ func (c *ApiController) GetRoles() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetRole
|
||||
// @Title GetRole
|
||||
// @Tag Role API
|
||||
// @Description get role
|
||||
@@ -61,6 +62,7 @@ func (c *ApiController) GetRole() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateRole
|
||||
// @Title UpdateRole
|
||||
// @Tag Role API
|
||||
// @Description update role
|
||||
@@ -81,6 +83,7 @@ func (c *ApiController) UpdateRole() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddRole
|
||||
// @Title AddRole
|
||||
// @Tag Role API
|
||||
// @Description add role
|
||||
@@ -98,6 +101,7 @@ func (c *ApiController) AddRole() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteRole
|
||||
// @Title DeleteRole
|
||||
// @Tag Role API
|
||||
// @Description delete role
|
||||
|
@@ -48,6 +48,7 @@ func (c *ApiController) GetSyncers() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetSyncer
|
||||
// @Title GetSyncer
|
||||
// @Tag Syncer API
|
||||
// @Description get syncer
|
||||
@@ -61,6 +62,7 @@ func (c *ApiController) GetSyncer() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateSyncer
|
||||
// @Title UpdateSyncer
|
||||
// @Tag Syncer API
|
||||
// @Description update syncer
|
||||
@@ -81,6 +83,7 @@ func (c *ApiController) UpdateSyncer() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddSyncer
|
||||
// @Title AddSyncer
|
||||
// @Tag Syncer API
|
||||
// @Description add syncer
|
||||
@@ -98,6 +101,7 @@ func (c *ApiController) AddSyncer() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteSyncer
|
||||
// @Title DeleteSyncer
|
||||
// @Tag Syncer API
|
||||
// @Description delete syncer
|
||||
@@ -115,6 +119,7 @@ func (c *ApiController) DeleteSyncer() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// RunSyncer
|
||||
// @Title RunSyncer
|
||||
// @Tag Syncer API
|
||||
// @Description run syncer
|
||||
|
@@ -255,7 +255,7 @@ func (c *ApiController) RefreshToken() {
|
||||
// @router /login/oauth/logout [get]
|
||||
func (c *ApiController) TokenLogout() {
|
||||
token := c.Input().Get("id_token_hint")
|
||||
flag, application := object.DeleteTokenByAceessToken(token)
|
||||
flag, application := object.DeleteTokenByAccessToken(token)
|
||||
redirectUri := c.Input().Get("post_logout_redirect_uri")
|
||||
state := c.Input().Get("state")
|
||||
if application != nil && object.CheckRedirectUriValid(application, redirectUri) {
|
||||
@@ -269,10 +269,11 @@ func (c *ApiController) TokenLogout() {
|
||||
// IntrospectToken
|
||||
// @Title IntrospectToken
|
||||
// @Description The introspection endpoint is an OAuth 2.0 endpoint that takes a
|
||||
// parameter representing an OAuth 2.0 token and returns a JSON document
|
||||
// representing the meta information surrounding the
|
||||
// token, including whether this token is currently active.
|
||||
// This endpoint only support Basic Authorization.
|
||||
// parameter representing an OAuth 2.0 token and returns a JSON document
|
||||
// representing the meta information surrounding the
|
||||
// token, including whether this token is currently active.
|
||||
// This endpoint only support Basic Authorization.
|
||||
//
|
||||
// @Param 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"
|
||||
// @Success 200 {object} object.IntrospectionResponse The Response object
|
||||
@@ -288,7 +289,7 @@ func (c *ApiController) IntrospectToken() {
|
||||
if clientId == "" || clientSecret == "" {
|
||||
c.ResponseError("empty clientId or clientSecret")
|
||||
c.Data["json"] = &object.TokenError{
|
||||
Error: object.INVALID_REQUEST,
|
||||
Error: object.InvalidRequest,
|
||||
}
|
||||
c.SetTokenErrorHttpStatus()
|
||||
c.ServeJSON()
|
||||
@@ -299,7 +300,7 @@ func (c *ApiController) IntrospectToken() {
|
||||
if application == nil || application.ClientSecret != clientSecret {
|
||||
c.ResponseError("invalid application or wrong clientSecret")
|
||||
c.Data["json"] = &object.TokenError{
|
||||
Error: object.INVALID_CLIENT,
|
||||
Error: object.InvalidClient,
|
||||
}
|
||||
c.SetTokenErrorHttpStatus()
|
||||
return
|
||||
|
@@ -298,6 +298,7 @@ func (c *ApiController) SetPassword() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// CheckUserPassword
|
||||
// @Title CheckUserPassword
|
||||
// @router /check-user-password [post]
|
||||
// @Tag User API
|
||||
|
@@ -55,7 +55,7 @@ func (c *ApiController) ResponseError(error string, data ...interface{}) {
|
||||
func (c *ApiController) SetTokenErrorHttpStatus() {
|
||||
_, ok := c.Data["json"].(*object.TokenError)
|
||||
if ok {
|
||||
if c.Data["json"].(*object.TokenError).Error == object.INVALID_CLIENT {
|
||||
if c.Data["json"].(*object.TokenError).Error == object.InvalidClient {
|
||||
c.Ctx.Output.SetStatus(401)
|
||||
c.Ctx.Output.Header("WWW-Authenticate", "Basic realm=\"OAuth2\"")
|
||||
} else {
|
||||
|
@@ -98,7 +98,7 @@ func (c *ApiController) SendVerificationCode() {
|
||||
return
|
||||
}
|
||||
|
||||
sendResp := errors.New("Invalid dest type")
|
||||
sendResp := errors.New("invalid dest type")
|
||||
|
||||
if user == nil && checkUser != "" && checkUser != "true" {
|
||||
name := application.Organization
|
||||
|
@@ -16,7 +16,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/duo-labs/webauthn/webauthn"
|
||||
)
|
||||
|
||||
// WebAuthnSignupBegin
|
||||
// @Title WebAuthnSignupBegin
|
||||
// @Tag User API
|
||||
// @Description WebAuthn Registration Flow 1st stage
|
||||
@@ -53,6 +54,7 @@ func (c *ApiController) WebAuthnSignupBegin() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// WebAuthnSignupFinish
|
||||
// @Title WebAuthnSignupFinish
|
||||
// @Tag User API
|
||||
// @Description WebAuthn Registration Flow 2nd stage
|
||||
@@ -72,7 +74,7 @@ func (c *ApiController) WebAuthnSignupFinish() {
|
||||
c.ResponseError("Please call WebAuthnSignupBegin first")
|
||||
return
|
||||
}
|
||||
c.Ctx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody))
|
||||
c.Ctx.Request.Body = io.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody))
|
||||
|
||||
credential, err := webauthnObj.FinishRegistration(user, sessionData, c.Ctx.Request)
|
||||
if err != nil {
|
||||
@@ -84,6 +86,7 @@ func (c *ApiController) WebAuthnSignupFinish() {
|
||||
c.ResponseOk()
|
||||
}
|
||||
|
||||
// WebAuthnSigninBegin
|
||||
// @Title WebAuthnSigninBegin
|
||||
// @Tag Login API
|
||||
// @Description WebAuthn Login Flow 1st stage
|
||||
@@ -110,6 +113,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// WebAuthnSigninFinish
|
||||
// @Title WebAuthnSigninBegin
|
||||
// @Tag Login API
|
||||
// @Description WebAuthn Login Flow 2nd stage
|
||||
@@ -124,7 +128,7 @@ func (c *ApiController) WebAuthnSigninFinish() {
|
||||
c.ResponseError("Please call WebAuthnSigninBegin first")
|
||||
return
|
||||
}
|
||||
c.Ctx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody))
|
||||
c.Ctx.Request.Body = io.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody))
|
||||
userId := string(sessionData.UserID)
|
||||
user := object.GetUser(userId)
|
||||
_, err := webauthnObj.FinishLogin(user, sessionData, c.Ctx.Request)
|
||||
|
@@ -48,6 +48,7 @@ func (c *ApiController) GetWebhooks() {
|
||||
}
|
||||
}
|
||||
|
||||
// GetWebhook
|
||||
// @Title GetWebhook
|
||||
// @Tag Webhook API
|
||||
// @Description get webhook
|
||||
@@ -61,6 +62,7 @@ func (c *ApiController) GetWebhook() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateWebhook
|
||||
// @Title UpdateWebhook
|
||||
// @Tag Webhook API
|
||||
// @Description update webhook
|
||||
@@ -81,6 +83,7 @@ func (c *ApiController) UpdateWebhook() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddWebhook
|
||||
// @Title AddWebhook
|
||||
// @Tag Webhook API
|
||||
// @Description add webhook
|
||||
@@ -98,6 +101,7 @@ func (c *ApiController) AddWebhook() {
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteWebhook
|
||||
// @Title DeleteWebhook
|
||||
// @Tag Webhook API
|
||||
// @Description delete webhook
|
||||
|
2
go.mod
2
go.mod
@@ -10,7 +10,7 @@ require (
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/casbin/casbin/v2 v2.30.1
|
||||
github.com/casbin/xorm-adapter/v2 v2.5.1
|
||||
github.com/casdoor/go-sms-sender v0.2.0
|
||||
github.com/casdoor/go-sms-sender v0.3.0
|
||||
github.com/casdoor/goth v1.69.0-FIX2
|
||||
github.com/casdoor/oss v1.2.0
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
|
4
go.sum
4
go.sum
@@ -98,8 +98,8 @@ github.com/casbin/casbin/v2 v2.30.1 h1:P5HWadDL7olwUXNdcuKUBk+x75Y2eitFxYTcLNKeK
|
||||
github.com/casbin/casbin/v2 v2.30.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
||||
github.com/casbin/xorm-adapter/v2 v2.5.1 h1:BkpIxRHKa0s3bSMx173PpuU7oTs+Zw7XmD0BIta0HGM=
|
||||
github.com/casbin/xorm-adapter/v2 v2.5.1/go.mod h1:AeH4dBKHC9/zYxzdPVHhPDzF8LYLqjDdb767CWJoV54=
|
||||
github.com/casdoor/go-sms-sender v0.2.0 h1:52bin4EBOPzOee64s9UK7jxd22FODvT9/+Y/Z+PSHpg=
|
||||
github.com/casdoor/go-sms-sender v0.2.0/go.mod h1:fsZsNnALvFIo+HFcE1U/oCQv4ZT42FdglXKMsEm3WSk=
|
||||
github.com/casdoor/go-sms-sender v0.3.0 h1:c4bWVcKZhO2L3Xu1oy7aeVkCK6HRJkW/b5K1xU9mV60=
|
||||
github.com/casdoor/go-sms-sender v0.3.0/go.mod h1:fsZsNnALvFIo+HFcE1U/oCQv4ZT42FdglXKMsEm3WSk=
|
||||
github.com/casdoor/goth v1.69.0-FIX2 h1:RgfIMkL9kekylgxHHK2ZY8ASAwOGns2HVlaBwLu7Bcs=
|
||||
github.com/casdoor/goth v1.69.0-FIX2/go.mod h1:Om55nRo8CkeDkPSNBbzXW4G5uI28ZUkSk5S69dPek3s=
|
||||
github.com/casdoor/oss v1.2.0 h1:ozLAE+nnNdFQBWbzH8U9spzaO8h8NrB57lBcdyMUUQ8=
|
||||
|
14
idp/adfs.go
14
idp/adfs.go
@@ -19,7 +19,7 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
@@ -77,6 +77,7 @@ type AdfsToken struct {
|
||||
ErrMsg string `json:"error_description"`
|
||||
}
|
||||
|
||||
// GetToken
|
||||
// get more detail via: https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/overview/ad-fs-openid-connect-oauth-flows-scenarios#request-an-access-token
|
||||
func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
payload := url.Values{}
|
||||
@@ -88,7 +89,7 @@ func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -109,6 +110,7 @@ func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetUserInfo
|
||||
// Since the userinfo endpoint of ADFS only returns sub,
|
||||
// the id_token is used to resolve the userinfo
|
||||
func (idp *AdfsIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
@@ -122,10 +124,10 @@ func (idp *AdfsIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
}
|
||||
tokenSrc := []byte(token.AccessToken)
|
||||
publicKey, _ := keyset.Keys[0].Materialize()
|
||||
id_token, _ := jwt.Parse(bytes.NewReader(tokenSrc), jwt.WithVerify(jwa.RS256, publicKey))
|
||||
sid, _ := id_token.Get("sid")
|
||||
upn, _ := id_token.Get("upn")
|
||||
name, _ := id_token.Get("unique_name")
|
||||
idToken, _ := jwt.Parse(bytes.NewReader(tokenSrc), jwt.WithVerify(jwa.RS256, publicKey))
|
||||
sid, _ := idToken.Get("sid")
|
||||
upn, _ := idToken.Get("upn")
|
||||
name, _ := idToken.Get("unique_name")
|
||||
userinfo := &UserInfo{
|
||||
Id: sid.(string),
|
||||
Username: name.(string),
|
||||
|
@@ -24,7 +24,6 @@ import (
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
@@ -205,7 +204,7 @@ func (idp *AlipayIdProvider) postWithBody(body interface{}, targetUrl string) ([
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
@@ -97,7 +97,7 @@ func (idp *BaiduIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -76,6 +75,7 @@ type BilibiliIdProviderTokenResponse struct {
|
||||
Data BilibiliProviderToken `json:"data"`
|
||||
}
|
||||
|
||||
// GetToken
|
||||
/*
|
||||
{
|
||||
"code": 0,
|
||||
@@ -170,7 +170,7 @@ func (idp *BilibiliIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -204,7 +204,7 @@ func (idp *BilibiliIdProvider) postWithBody(body interface{}, url string) ([]byt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ package idp
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
@@ -71,7 +71,7 @@ func (idp *CasdoorIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -131,7 +131,7 @@ func (idp *CasdoorIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
_ "net/url"
|
||||
_ "time"
|
||||
@@ -84,7 +84,7 @@ func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -101,7 +100,7 @@ func (idp *DingTalkIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
|
||||
token := &oauth2.Token{
|
||||
AccessToken: pToken.AccessToken,
|
||||
Expiry: time.Unix(time.Now().Unix()+int64(pToken.ExpiresIn), 0),
|
||||
Expiry: time.Unix(time.Now().Unix()+pToken.ExpiresIn, 0),
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
@@ -145,7 +144,7 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -180,7 +179,7 @@ func (idp *DingTalkIdProvider) postWithBody(body interface{}, url string) ([]byt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
@@ -98,7 +98,7 @@ func (idp *DouyinIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -177,7 +177,7 @@ func (idp *DouyinIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -71,6 +71,7 @@ type FacebookCheckToken struct {
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
// FacebookCheckTokenData
|
||||
// Get more detail via: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken
|
||||
type FacebookCheckTokenData struct {
|
||||
UserId string `json:"user_id"`
|
||||
|
@@ -19,7 +19,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -93,7 +92,7 @@ func (idp *GiteeIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rbs, err := ioutil.ReadAll(resp.Body)
|
||||
rbs, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -202,7 +201,7 @@ func (idp *GithubIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -236,7 +235,7 @@ func (idp *GithubIdProvider) postWithBody(body interface{}, url string) ([]byte,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ package idp
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -85,7 +85,7 @@ func (idp *GitlabIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func (idp *GitlabIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
@@ -95,7 +95,7 @@ func (idp *GoogleIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -207,6 +207,7 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
|
||||
return &idp
|
||||
}
|
||||
|
||||
// SetHttpClient
|
||||
// Goth's idp all implement the Client method, but since the goth.Provider interface does not provide to modify idp's client method, reflection is required
|
||||
func (idp *GothIdProvider) SetHttpClient(client *http.Client) {
|
||||
idpClient := reflect.ValueOf(idp.Provider).Elem().FieldByName("HTTPClient")
|
||||
|
@@ -17,7 +17,7 @@ package idp
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
@@ -58,6 +58,7 @@ type InfoflowInterToken struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
}
|
||||
|
||||
// GetToken
|
||||
// get more detail via: https://qy.baidu.com/doc/index.html#/inner_quickstart/flow?id=%E8%8E%B7%E5%8F%96accesstoken
|
||||
func (idp *InfoflowInternalIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
pTokenParams := &struct {
|
||||
@@ -69,7 +70,7 @@ func (idp *InfoflowInternalIdProvider) GetToken(code string) (*oauth2.Token, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -137,6 +138,7 @@ type InfoflowInternalUserInfo struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// GetUserInfo
|
||||
// get more detail via: https://qy.baidu.com/doc/index.html#/inner_serverapi/contacts?id=%e8%8e%b7%e5%8f%96%e6%88%90%e5%91%98
|
||||
func (idp *InfoflowInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
// Get userid first
|
||||
@@ -147,7 +149,7 @@ func (idp *InfoflowInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserIn
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -165,7 +167,7 @@ func (idp *InfoflowInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserIn
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -63,6 +62,7 @@ type InfoflowToken struct {
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
}
|
||||
|
||||
// GetToken
|
||||
// get more detail via: https://qy.baidu.com/doc/index.html#/third_serverapi/authority
|
||||
func (idp *InfoflowIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
pTokenParams := &struct {
|
||||
@@ -134,6 +134,7 @@ type InfoflowUserInfo struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// GetUserInfo
|
||||
// get more detail via: https://qy.baidu.com/doc/index.html#/third_serverapi/contacts?id=%e8%8e%b7%e5%8f%96%e6%88%90%e5%91%98
|
||||
func (idp *InfoflowIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
// Get userid first
|
||||
@@ -144,7 +145,7 @@ func (idp *InfoflowIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -162,7 +163,7 @@ func (idp *InfoflowIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -197,7 +198,7 @@ func (idp *InfoflowIdProvider) postWithBody(body interface{}, url string) ([]byt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@ package idp
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -173,7 +172,7 @@ func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -204,7 +203,7 @@ func (idp *LarkIdProvider) postWithBody(body interface{}, url string) ([]byte, e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -85,7 +84,7 @@ func (idp *LinkedInIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rbs, err := ioutil.ReadAll(resp.Body)
|
||||
rbs, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -323,7 +322,7 @@ func (idp *LinkedInIdProvider) GetUrlRespWithAuthorization(url, token string) ([
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
bs, err := ioutil.ReadAll(resp.Body)
|
||||
bs, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ package idp
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
@@ -114,7 +114,7 @@ func (idp *OktaIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -178,7 +178,7 @@ func (idp *OktaIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
@@ -75,7 +75,7 @@ func (idp *QqIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
tokenContent, err := ioutil.ReadAll(resp.Body)
|
||||
tokenContent, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -148,7 +148,7 @@ func (idp *QqIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
openIdBody, err := ioutil.ReadAll(resp.Body)
|
||||
openIdBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -167,7 +167,7 @@ func (idp *QqIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
userInfoBody, err := ioutil.ReadAll(resp.Body)
|
||||
userInfoBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -144,7 +144,7 @@ type WechatUserInfo struct {
|
||||
City string `json:"city"` // City filled in by general user's personal data
|
||||
Province string `json:"province"` // Province filled in by ordinary user's personal information
|
||||
Country string `json:"country"` // Country, such as China is CN
|
||||
Headimgurl string `json:"headimgurl"` // User avatar, the last value represents the size of the square avatar (there are optional values of 0, 46, 64, 96, 132, 0 represents a 640*640 square avatar), this item is empty when the user does not have a avatar
|
||||
Headimgurl string `json:"headimgurl"` // User avatar, the last value represents the size of the square avatar (there are optional values of 0, 46, 64, 96, 132, 0 represents a 640*640 square avatar), this item is empty when the user does not have an avatar
|
||||
Privilege []string `json:"privilege"` // User Privilege information, json array, such as Wechat Woka user (chinaunicom)
|
||||
Unionid string `json:"unionid"` // Unified user identification. For an application under a WeChat open platform account, the unionid of the same user is unique.
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ package idp
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
@@ -65,7 +65,7 @@ func (idp *WeChatMiniProgramIdProvider) GetSessionByCode(code string) (*WeChatMi
|
||||
return nil, err
|
||||
}
|
||||
defer sessionResponse.Body.Close()
|
||||
data, err := ioutil.ReadAll(sessionResponse.Body)
|
||||
data, err := io.ReadAll(sessionResponse.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -17,13 +17,14 @@ package idp
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// WeComInternalIdProvider
|
||||
// This idp is using wecom internal application api as idp
|
||||
type WeComInternalIdProvider struct {
|
||||
Client *http.Client
|
||||
@@ -72,7 +73,7 @@ func (idp *WeComInternalIdProvider) GetToken(code string) (*oauth2.Token, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -123,7 +124,7 @@ func (idp *WeComInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -144,7 +145,7 @@ func (idp *WeComInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err = ioutil.ReadAll(resp.Body)
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -195,7 +194,7 @@ func (idp *WeComIdProvider) postWithBody(body interface{}, url string) ([]byte,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -19,7 +19,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -92,7 +91,7 @@ func (idp *WeiBoIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
return
|
||||
}
|
||||
}(resp.Body)
|
||||
bs, err := ioutil.ReadAll(resp.Body)
|
||||
bs, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,6 +18,8 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/casdoor/casdoor/cred"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
@@ -29,6 +31,11 @@ var (
|
||||
reFieldWhiteList *regexp.Regexp
|
||||
)
|
||||
|
||||
const (
|
||||
SigninWrongTimesLimit = 5
|
||||
LastSignWrongTimeDuration = time.Minute * 15
|
||||
)
|
||||
|
||||
func init() {
|
||||
reWhiteSpace, _ = regexp.Compile(`\s`)
|
||||
reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
|
||||
@@ -42,11 +49,25 @@ func CheckUserSignup(application *Application, organization *Organization, usern
|
||||
if application.IsSignupItemVisible("Username") {
|
||||
if len(username) <= 1 {
|
||||
return "username must have at least 2 characters"
|
||||
} else if reWhiteSpace.MatchString(username) {
|
||||
}
|
||||
if unicode.IsDigit(rune(username[0])) {
|
||||
return "username cannot start with a digit"
|
||||
}
|
||||
if util.IsEmailValid(username) {
|
||||
return "username cannot be an email address"
|
||||
}
|
||||
if reWhiteSpace.MatchString(username) {
|
||||
return "username cannot contain white spaces"
|
||||
} else if HasUserByField(organization.Name, "name", username) {
|
||||
}
|
||||
if HasUserByField(organization.Name, "name", username) {
|
||||
return "username already exists"
|
||||
}
|
||||
if HasUserByField(organization.Name, "email", username) {
|
||||
return "email already exists"
|
||||
}
|
||||
if HasUserByField(organization.Name, "phone", username) {
|
||||
return "phone already exists"
|
||||
}
|
||||
}
|
||||
|
||||
if len(password) <= 5 {
|
||||
@@ -112,7 +133,32 @@ func CheckUserSignup(application *Application, organization *Organization, usern
|
||||
return ""
|
||||
}
|
||||
|
||||
func checkSigninErrorTimes(user *User) string {
|
||||
if user.SigninWrongTimes >= SigninWrongTimesLimit {
|
||||
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
|
||||
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
|
||||
seconds := int(LastSignWrongTimeDuration.Seconds() - passedTime.Seconds())
|
||||
|
||||
// deny the login if the error times is greater than the limit and the last login time is less than the duration
|
||||
if seconds > 0 {
|
||||
return fmt.Sprintf("You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again", seconds/60, seconds%60)
|
||||
}
|
||||
|
||||
// reset the error times
|
||||
user.SigninWrongTimes = 0
|
||||
|
||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, user.IsGlobalAdmin)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func CheckPassword(user *User, password string) string {
|
||||
// check the login error times
|
||||
if msg := checkSigninErrorTimes(user); msg != "" {
|
||||
return msg
|
||||
}
|
||||
|
||||
organization := GetOrganizationByUser(user)
|
||||
if organization == nil {
|
||||
return "organization does not exist"
|
||||
@@ -122,14 +168,17 @@ func CheckPassword(user *User, password string) string {
|
||||
if credManager != nil {
|
||||
if organization.MasterPassword != "" {
|
||||
if credManager.IsPasswordCorrect(password, organization.MasterPassword, "", organization.PasswordSalt) {
|
||||
resetUserSigninErrorTimes(user)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) {
|
||||
resetUserSigninErrorTimes(user)
|
||||
return ""
|
||||
}
|
||||
return "password incorrect"
|
||||
|
||||
return recordSigninErrorInfo(user)
|
||||
} else {
|
||||
return fmt.Sprintf("unsupported password type: %s", organization.PasswordType)
|
||||
}
|
||||
|
@@ -14,7 +14,11 @@
|
||||
|
||||
package object
|
||||
|
||||
import "regexp"
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
var reRealName *regexp.Regexp
|
||||
|
||||
@@ -29,3 +33,32 @@ func init() {
|
||||
func isValidRealName(s string) bool {
|
||||
return reRealName.MatchString(s)
|
||||
}
|
||||
|
||||
func resetUserSigninErrorTimes(user *User) {
|
||||
// if the password is correct and wrong times is not zero, reset the error times
|
||||
if user.SigninWrongTimes == 0 {
|
||||
return
|
||||
}
|
||||
user.SigninWrongTimes = 0
|
||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin)
|
||||
}
|
||||
|
||||
func recordSigninErrorInfo(user *User) string {
|
||||
// increase failed login count
|
||||
user.SigninWrongTimes++
|
||||
|
||||
if user.SigninWrongTimes >= SigninWrongTimesLimit {
|
||||
// record the latest failed login time
|
||||
user.LastSigninWrongTime = time.Now().UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// update user
|
||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin)
|
||||
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
|
||||
if leftChances > 0 {
|
||||
return fmt.Sprintf("password is incorrect, you have %d remaining chances", leftChances)
|
||||
}
|
||||
|
||||
// don't show the chance error message if the user has no chance left
|
||||
return fmt.Sprintf("You have entered the wrong password too many times, please wait for %d minutes and try again", int(LastSignWrongTimeDuration.Minutes()))
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ func SendEmail(provider *Provider, title string, content string, dest string, se
|
||||
// DailSmtpServer Dail Smtp server
|
||||
func DailSmtpServer(provider *Provider) error {
|
||||
dialer := gomail.NewDialer(provider.Host, provider.Port, provider.ClientId, provider.ClientSecret)
|
||||
dialer.SSL = !provider.DisableSsl
|
||||
|
||||
sender, err := dialer.Dial()
|
||||
if err != nil {
|
||||
|
@@ -16,8 +16,10 @@ package object
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/duo-labs/webauthn/webauthn"
|
||||
)
|
||||
@@ -36,6 +38,8 @@ func InitDb() {
|
||||
initWebAuthn()
|
||||
}
|
||||
|
||||
var staticBaseUrl = beego.AppConfig.String("staticBaseUrl")
|
||||
|
||||
func initBuiltInOrganization() bool {
|
||||
organization := getOrganization("admin", "built-in")
|
||||
if organization != nil {
|
||||
@@ -48,10 +52,10 @@ func initBuiltInOrganization() bool {
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Built-in Organization",
|
||||
WebsiteUrl: "https://example.com",
|
||||
Favicon: "https://cdn.casbin.com/static/favicon.ico",
|
||||
Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", staticBaseUrl),
|
||||
PasswordType: "plain",
|
||||
PhonePrefix: "86",
|
||||
DefaultAvatar: "https://casbin.org/img/casbin.svg",
|
||||
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", staticBaseUrl),
|
||||
Tags: []string{},
|
||||
AccountItems: []*AccountItem{
|
||||
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||
@@ -100,7 +104,7 @@ func initBuiltInUser() {
|
||||
Type: "normal-user",
|
||||
Password: "123",
|
||||
DisplayName: "Admin",
|
||||
Avatar: "https://casbin.org/img/casbin.svg",
|
||||
Avatar: fmt.Sprintf("%s/img/casbin.svg", staticBaseUrl),
|
||||
Email: "admin@example.com",
|
||||
Phone: "12345678910",
|
||||
Address: []string{},
|
||||
@@ -130,7 +134,7 @@ func initBuiltInApplication() {
|
||||
Name: "app-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Casdoor",
|
||||
Logo: "https://cdn.casbin.com/logo/logo_1024x256.png",
|
||||
Logo: fmt.Sprintf("%s/img/casdoor-logo_1185x256.png", staticBaseUrl),
|
||||
HomepageUrl: "https://casdoor.org",
|
||||
Organization: "built-in",
|
||||
Cert: "cert-built-in",
|
||||
@@ -158,11 +162,11 @@ func initBuiltInApplication() {
|
||||
func readTokenFromFile() (string, string) {
|
||||
pemPath := "./object/token_jwt_key.pem"
|
||||
keyPath := "./object/token_jwt_key.key"
|
||||
pem, err := ioutil.ReadFile(pemPath)
|
||||
pem, err := os.ReadFile(pemPath)
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
key, err := ioutil.ReadFile(keyPath)
|
||||
key, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
@@ -31,6 +31,7 @@ func GetLdapAutoSynchronizer() *LdapAutoSynchronizer {
|
||||
return globalLdapAutoSynchronizer
|
||||
}
|
||||
|
||||
// StartAutoSync
|
||||
// start autosync for specified ldap, old existing autosync goroutine will be ceased
|
||||
func (l *LdapAutoSynchronizer) StartAutoSync(ldapId string) error {
|
||||
l.Lock()
|
||||
@@ -95,6 +96,7 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// LdapAutoSynchronizerStartUpAll
|
||||
// start all autosync goroutine for existing ldap servers in each organizations
|
||||
func (l *LdapAutoSynchronizer) LdapAutoSynchronizerStartUpAll() {
|
||||
organizations := []*Organization{}
|
||||
|
@@ -16,12 +16,7 @@ package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/model"
|
||||
xormadapter "github.com/casbin/xorm-adapter/v2"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
)
|
||||
@@ -32,26 +27,31 @@ type Permission struct {
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
|
||||
Users []string `xorm:"mediumtext" json:"users"`
|
||||
Roles []string `xorm:"mediumtext" json:"roles"`
|
||||
Users []string `xorm:"mediumtext" json:"users"`
|
||||
Roles []string `xorm:"mediumtext" json:"roles"`
|
||||
Domains []string `xorm:"mediumtext" json:"domains"`
|
||||
|
||||
Model string `xorm:"varchar(100)" json:"model"`
|
||||
ResourceType string `xorm:"varchar(100)" json:"resourceType"`
|
||||
Resources []string `xorm:"mediumtext" json:"resources"`
|
||||
Actions []string `xorm:"mediumtext" json:"actions"`
|
||||
Effect string `xorm:"varchar(100)" json:"effect"`
|
||||
IsEnabled bool `json:"isEnabled"`
|
||||
|
||||
IsEnabled bool `json:"isEnabled"`
|
||||
Submitter string `xorm:"varchar(100)" json:"submitter"`
|
||||
Approver string `xorm:"varchar(100)" json:"approver"`
|
||||
ApproveTime string `xorm:"varchar(100)" json:"approveTime"`
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
}
|
||||
|
||||
type PermissionRule struct {
|
||||
PType string `xorm:"varchar(100) index not null default ''"`
|
||||
V0 string `xorm:"varchar(100) index not null default ''"`
|
||||
V1 string `xorm:"varchar(100) index not null default ''"`
|
||||
V2 string `xorm:"varchar(100) index not null default ''"`
|
||||
V3 string `xorm:"varchar(100) index not null default ''"`
|
||||
V4 string `xorm:"varchar(100) index not null default ''"`
|
||||
V5 string `xorm:"varchar(100) index not null default ''"`
|
||||
Ptype string `xorm:"varchar(100) index not null default ''" json:"ptype"`
|
||||
V0 string `xorm:"varchar(100) index not null default ''" json:"v0"`
|
||||
V1 string `xorm:"varchar(100) index not null default ''" json:"v1"`
|
||||
V2 string `xorm:"varchar(100) index not null default ''" json:"v2"`
|
||||
V3 string `xorm:"varchar(100) index not null default ''" json:"v3"`
|
||||
V4 string `xorm:"varchar(100) index not null default ''" json:"v4"`
|
||||
V5 string `xorm:"varchar(100) index not null default ''" json:"v5"`
|
||||
}
|
||||
|
||||
func GetPermissionCount(owner, field, value string) int {
|
||||
@@ -158,78 +158,6 @@ func (permission *Permission) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", permission.Owner, permission.Name)
|
||||
}
|
||||
|
||||
func getEnforcer(permission *Permission) *casbin.Enforcer {
|
||||
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||
adapter, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetBeegoConfDataSourceName()+conf.GetConfigString("dbName"), "permission_rule", tableNamePrefix, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
modelText := `
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = permission, sub, obj, act
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act`
|
||||
permissionModel := getModel(permission.Owner, permission.Model)
|
||||
if permissionModel != nil {
|
||||
modelText = permissionModel.ModelText
|
||||
}
|
||||
m, err := model.NewModelFromString(modelText)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
enforcer, err := casbin.NewEnforcer(m, adapter)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = enforcer.LoadFilteredPolicy(xormadapter.Filter{V0: []string{permission.GetId()}})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return enforcer
|
||||
}
|
||||
|
||||
func getPolicies(permission *Permission) [][]string {
|
||||
var policies [][]string
|
||||
for _, user := range permission.Users {
|
||||
for _, resource := range permission.Resources {
|
||||
for _, action := range permission.Actions {
|
||||
policies = append(policies, []string{permission.GetId(), user, resource, strings.ToLower(action)})
|
||||
}
|
||||
}
|
||||
}
|
||||
return policies
|
||||
}
|
||||
|
||||
func addPolicies(permission *Permission) {
|
||||
enforcer := getEnforcer(permission)
|
||||
policies := getPolicies(permission)
|
||||
|
||||
_, err := enforcer.AddPolicies(policies)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func removePolicies(permission *Permission) {
|
||||
enforcer := getEnforcer(permission)
|
||||
|
||||
_, err := enforcer.RemoveFilteredPolicy(0, permission.GetId())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func GetPermissionsByUser(userId string) []*Permission {
|
||||
permissions := []*Permission{}
|
||||
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions)
|
||||
@@ -239,3 +167,13 @@ func GetPermissionsByUser(userId string) []*Permission {
|
||||
|
||||
return permissions
|
||||
}
|
||||
|
||||
func GetPermissionsBySubmitter(owner string, submitter string) []*Permission {
|
||||
permissions := []*Permission{}
|
||||
err := adapter.Engine.Desc("created_time").Find(&permissions, &Permission{Owner: owner, Submitter: submitter})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return permissions
|
||||
}
|
||||
|
191
object/permission_enforcer.go
Normal file
191
object/permission_enforcer.go
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/model"
|
||||
xormadapter "github.com/casbin/xorm-adapter/v2"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
)
|
||||
|
||||
func getEnforcer(permission *Permission) *casbin.Enforcer {
|
||||
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||
adapter, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetBeegoConfDataSourceName()+conf.GetConfigString("dbName"), "permission_rule", tableNamePrefix, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
modelText := `
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = permission, sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
|
||||
permissionModel := getModel(permission.Owner, permission.Model)
|
||||
if permissionModel != nil {
|
||||
modelText = permissionModel.ModelText
|
||||
}
|
||||
m, err := model.NewModelFromString(modelText)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
enforcer, err := casbin.NewEnforcer(m, adapter)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return enforcer
|
||||
}
|
||||
|
||||
func getPolicies(permission *Permission) [][]string {
|
||||
var policies [][]string
|
||||
for _, user := range permission.Users {
|
||||
for _, resource := range permission.Resources {
|
||||
for _, action := range permission.Actions {
|
||||
policies = append(policies, []string{permission.GetId(), user, resource, strings.ToLower(action)})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, role := range permission.Roles {
|
||||
for _, resource := range permission.Resources {
|
||||
for _, action := range permission.Actions {
|
||||
policies = append(policies, []string{permission.GetId(), role, resource, strings.ToLower(action)})
|
||||
}
|
||||
}
|
||||
}
|
||||
return policies
|
||||
}
|
||||
|
||||
func addPolicies(permission *Permission) {
|
||||
enforcer := getEnforcer(permission)
|
||||
policies := getPolicies(permission)
|
||||
|
||||
_, err := enforcer.AddPolicies(policies)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func removePolicies(permission *Permission) {
|
||||
enforcer := getEnforcer(permission)
|
||||
|
||||
_, err := enforcer.RemoveFilteredPolicy(0, permission.GetId())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getGroupingPolicies(role *Role) [][]string {
|
||||
var groupingPolicies [][]string
|
||||
for _, subUser := range role.Users {
|
||||
groupingPolicies = append(groupingPolicies, []string{subUser, role.GetId()})
|
||||
}
|
||||
for _, subRole := range role.Roles {
|
||||
groupingPolicies = append(groupingPolicies, []string{subRole, role.GetId()})
|
||||
}
|
||||
return groupingPolicies
|
||||
}
|
||||
|
||||
func addGroupingPolicies(role *Role) {
|
||||
enforcer := getEnforcer(&Permission{})
|
||||
groupingPolicies := getGroupingPolicies(role)
|
||||
|
||||
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func removeGroupingPolicies(role *Role) {
|
||||
enforcer := getEnforcer(&Permission{})
|
||||
groupingPolicies := getGroupingPolicies(role)
|
||||
|
||||
_, err := enforcer.RemoveGroupingPolicies(groupingPolicies)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Enforce(userId string, permissionRule *PermissionRule) bool {
|
||||
permission := GetPermission(permissionRule.V0)
|
||||
enforcer := getEnforcer(permission)
|
||||
allow, err := enforcer.Enforce(userId, permissionRule.V2, permissionRule.V3)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return allow
|
||||
}
|
||||
|
||||
func BatchEnforce(userId string, permissionRules []PermissionRule) []bool {
|
||||
var requests [][]interface{}
|
||||
for _, permissionRule := range permissionRules {
|
||||
requests = append(requests, []interface{}{userId, permissionRule.V2, permissionRule.V3})
|
||||
}
|
||||
permission := GetPermission(permissionRules[0].V0)
|
||||
enforcer := getEnforcer(permission)
|
||||
allow, err := enforcer.BatchEnforce(requests)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return allow
|
||||
}
|
||||
|
||||
func getAllValues(userId string, sec string, fieldIndex int) []string {
|
||||
permissions := GetPermissionsByUser(userId)
|
||||
var values []string
|
||||
for _, permission := range permissions {
|
||||
enforcer := getEnforcer(permission)
|
||||
enforcer.ClearPolicy()
|
||||
err := enforcer.LoadFilteredPolicy(xormadapter.Filter{V0: []string{permission.GetId()}, V1: []string{userId}})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, value := range enforcer.GetModel().GetValuesForFieldInPolicyAllTypes(sec, fieldIndex) {
|
||||
values = append(values, value)
|
||||
}
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func GetAllObjects(userId string) []string {
|
||||
return getAllValues(userId, "p", 2)
|
||||
}
|
||||
|
||||
func GetAllActions(userId string) []string {
|
||||
return getAllValues(userId, "p", 3)
|
||||
}
|
||||
|
||||
func GetAllRoles(userId string) []string {
|
||||
roles := GetRolesByUser(userId)
|
||||
var res []string
|
||||
for _, role := range roles {
|
||||
res = append(res, role.Name)
|
||||
}
|
||||
return res
|
||||
}
|
@@ -43,10 +43,11 @@ type Provider struct {
|
||||
CustomUserInfoUrl string `xorm:"varchar(200)" json:"customUserInfoUrl"`
|
||||
CustomLogo string `xorm:"varchar(200)" json:"customLogo"`
|
||||
|
||||
Host string `xorm:"varchar(100)" json:"host"`
|
||||
Port int `json:"port"`
|
||||
Title string `xorm:"varchar(100)" json:"title"`
|
||||
Content string `xorm:"varchar(1000)" json:"content"`
|
||||
Host string `xorm:"varchar(100)" json:"host"`
|
||||
Port int `json:"port"`
|
||||
DisableSsl bool `json:"disableSsl"`
|
||||
Title string `xorm:"varchar(100)" json:"title"`
|
||||
Content string `xorm:"varchar(1000)" json:"content"`
|
||||
|
||||
RegionId string `xorm:"varchar(100)" json:"regionId"`
|
||||
SignName string `xorm:"varchar(100)" json:"signName"`
|
||||
|
@@ -29,6 +29,7 @@ type Role struct {
|
||||
|
||||
Users []string `xorm:"mediumtext" json:"users"`
|
||||
Roles []string `xorm:"mediumtext" json:"roles"`
|
||||
Domains []string `xorm:"mediumtext" json:"domains"`
|
||||
IsEnabled bool `json:"isEnabled"`
|
||||
}
|
||||
|
||||
@@ -88,7 +89,8 @@ func GetRole(id string) *Role {
|
||||
|
||||
func UpdateRole(id string, role *Role) bool {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if getRole(owner, name) == nil {
|
||||
oldRole := getRole(owner, name)
|
||||
if oldRole == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -97,6 +99,11 @@ func UpdateRole(id string, role *Role) bool {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if affected != 0 {
|
||||
removeGroupingPolicies(oldRole)
|
||||
addGroupingPolicies(role)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
@@ -106,6 +113,10 @@ func AddRole(role *Role) bool {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if affected != 0 {
|
||||
addGroupingPolicies(role)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
@@ -115,6 +126,10 @@ func DeleteRole(role *Role) bool {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if affected != 0 {
|
||||
removeGroupingPolicies(role)
|
||||
}
|
||||
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
|
@@ -35,6 +35,7 @@ import (
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
// NewSamlResponse
|
||||
// returns a saml2 response
|
||||
func NewSamlResponse(user *User, host string, certificate string, destination string, iss string, requestId string, redirectUri []string) (*etree.Element, error) {
|
||||
samlResponse := &etree.Element{
|
||||
@@ -113,6 +114,7 @@ func (x X509Key) GetKeyPair() (privateKey *rsa.PrivateKey, cert []byte, err erro
|
||||
return privateKey, cert, err
|
||||
}
|
||||
|
||||
// IdpEntityDescriptor
|
||||
// SAML METADATA
|
||||
type IdpEntityDescriptor struct {
|
||||
XMLName xml.Name `xml:"EntityDescriptor"`
|
||||
|
@@ -44,7 +44,7 @@ func ParseSamlResponse(samlResponse string, providerType string) (string, error)
|
||||
func GenerateSamlLoginUrl(id, relayState string) (string, string, error) {
|
||||
provider := GetProvider(id)
|
||||
if provider.Category != "SAML" {
|
||||
return "", "", fmt.Errorf("Provider %s's category is not SAML", provider.Name)
|
||||
return "", "", fmt.Errorf("provider %s's category is not SAML", provider.Name)
|
||||
}
|
||||
sp, err := buildSp(provider, "")
|
||||
if err != nil {
|
||||
|
112
object/token.go
112
object/token.go
@@ -27,14 +27,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
hourSeconds = 3600
|
||||
INVALID_REQUEST = "invalid_request"
|
||||
INVALID_CLIENT = "invalid_client"
|
||||
INVALID_GRANT = "invalid_grant"
|
||||
UNAUTHORIZED_CLIENT = "unauthorized_client"
|
||||
UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type"
|
||||
INVALID_SCOPE = "invalid_scope"
|
||||
ENDPOINT_ERROR = "endpoint_error"
|
||||
hourSeconds = 3600
|
||||
InvalidRequest = "invalid_request"
|
||||
InvalidClient = "invalid_client"
|
||||
InvalidGrant = "invalid_grant"
|
||||
UnauthorizedClient = "unauthorized_client"
|
||||
UnsupportedGrantType = "unsupported_grant_type"
|
||||
InvalidScope = "invalid_scope"
|
||||
EndpointError = "endpoint_error"
|
||||
)
|
||||
|
||||
type Code struct {
|
||||
@@ -200,7 +200,7 @@ func DeleteToken(token *Token) bool {
|
||||
return affected != 0
|
||||
}
|
||||
|
||||
func DeleteTokenByAceessToken(accessToken string) (bool, *Application) {
|
||||
func DeleteTokenByAccessToken(accessToken string) (bool, *Application) {
|
||||
token := Token{AccessToken: accessToken}
|
||||
existed, err := adapter.Engine.Get(&token)
|
||||
if err != nil {
|
||||
@@ -287,7 +287,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
|
||||
}
|
||||
}
|
||||
|
||||
accessToken, refreshToken, err := generateJwtToken(application, user, nonce, scope, host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, nonce, scope, host)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
|
||||
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: util.GenerateId(),
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
@@ -325,7 +325,7 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
|
||||
application := GetApplicationByClientId(clientId)
|
||||
if application == nil {
|
||||
return &TokenError{
|
||||
Error: INVALID_CLIENT,
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_id is invalid",
|
||||
}
|
||||
}
|
||||
@@ -334,7 +334,7 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
|
||||
|
||||
if !IsGrantTypeValid(grantType, application.GrantTypes) && tag == "" {
|
||||
return &TokenError{
|
||||
Error: UNSUPPORTED_GRANT_TYPE,
|
||||
Error: UnsupportedGrantType,
|
||||
ErrorDescription: fmt.Sprintf("grant_type: %s is not supported in this application", grantType),
|
||||
}
|
||||
}
|
||||
@@ -377,20 +377,20 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
||||
// check parameters
|
||||
if grantType != "refresh_token" {
|
||||
return &TokenError{
|
||||
Error: UNSUPPORTED_GRANT_TYPE,
|
||||
Error: UnsupportedGrantType,
|
||||
ErrorDescription: "grant_type should be refresh_token",
|
||||
}
|
||||
}
|
||||
application := GetApplicationByClientId(clientId)
|
||||
if application == nil {
|
||||
return &TokenError{
|
||||
Error: INVALID_CLIENT,
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_id is invalid",
|
||||
}
|
||||
}
|
||||
if clientSecret != "" && application.ClientSecret != clientSecret {
|
||||
return &TokenError{
|
||||
Error: INVALID_CLIENT,
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}
|
||||
}
|
||||
@@ -399,7 +399,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
||||
existed, err := adapter.Engine.Get(&token)
|
||||
if err != nil || !existed {
|
||||
return &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "refresh token is invalid, expired or revoked",
|
||||
}
|
||||
}
|
||||
@@ -408,7 +408,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
||||
_, err = ParseJwtToken(refreshToken, cert)
|
||||
if err != nil {
|
||||
return &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
||||
}
|
||||
}
|
||||
@@ -416,21 +416,22 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
||||
user := getUser(application.Organization, token.User)
|
||||
if user.IsForbidden {
|
||||
return &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the user is forbidden to sign in, please contact the administrator",
|
||||
}
|
||||
}
|
||||
newAccessToken, newRefreshToken, err := generateJwtToken(application, user, "", scope, host)
|
||||
|
||||
newAccessToken, newRefreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
||||
if err != nil {
|
||||
return &TokenError{
|
||||
Error: ENDPOINT_ERROR,
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
newToken := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: util.GenerateId(),
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
@@ -464,6 +465,7 @@ func pkceChallenge(verifier string) string {
|
||||
return challenge
|
||||
}
|
||||
|
||||
// IsGrantTypeValid
|
||||
// Check if grantType is allowed in the current application
|
||||
// authorization_code is allowed by default
|
||||
func IsGrantTypeValid(method string, grantTypes []string) bool {
|
||||
@@ -478,11 +480,12 @@ func IsGrantTypeValid(method string, grantTypes []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetAuthorizationCodeToken
|
||||
// Authorization code flow
|
||||
func GetAuthorizationCodeToken(application *Application, clientSecret string, code string, verifier string) (*Token, *TokenError) {
|
||||
if code == "" {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_REQUEST,
|
||||
Error: InvalidRequest,
|
||||
ErrorDescription: "authorization code should not be empty",
|
||||
}
|
||||
}
|
||||
@@ -490,21 +493,21 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
|
||||
token := getTokenByCode(code)
|
||||
if token == nil {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "authorization code is invalid",
|
||||
}
|
||||
}
|
||||
if token.CodeIsUsed {
|
||||
// anti replay attacks
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "authorization code has been used",
|
||||
}
|
||||
}
|
||||
|
||||
if token.CodeChallenge != "" && pkceChallenge(verifier) != token.CodeChallenge {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "verifier is invalid",
|
||||
}
|
||||
}
|
||||
@@ -514,13 +517,13 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
|
||||
// but if it is provided, it must be accurate.
|
||||
if token.CodeChallenge == "" {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_CLIENT,
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}
|
||||
} else {
|
||||
if clientSecret != "" {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_CLIENT,
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}
|
||||
}
|
||||
@@ -529,7 +532,7 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
|
||||
|
||||
if application.Name != token.Application {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the token is for wrong application (client_id)",
|
||||
}
|
||||
}
|
||||
@@ -537,45 +540,47 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
|
||||
if time.Now().Unix() > token.CodeExpireIn {
|
||||
// code must be used within 5 minutes
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "authorization code has expired",
|
||||
}
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetPasswordToken
|
||||
// Resource Owner Password Credentials flow
|
||||
func GetPasswordToken(application *Application, username string, password string, scope string, host string) (*Token, *TokenError) {
|
||||
user := getUser(application.Organization, username)
|
||||
if user == nil {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the user does not exist",
|
||||
}
|
||||
}
|
||||
msg := CheckPassword(user, password)
|
||||
if msg != "" {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "invalid username or password",
|
||||
}
|
||||
}
|
||||
if user.IsForbidden {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the user is forbidden to sign in, please contact the administrator",
|
||||
}
|
||||
}
|
||||
accessToken, refreshToken, err := generateJwtToken(application, user, "", scope, host)
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: ENDPOINT_ERROR,
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}
|
||||
}
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: util.GenerateId(),
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
@@ -592,11 +597,12 @@ func GetPasswordToken(application *Application, username string, password string
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetClientCredentialsToken
|
||||
// Client Credentials flow
|
||||
func GetClientCredentialsToken(application *Application, clientSecret string, scope string, host string) (*Token, *TokenError) {
|
||||
if application.ClientSecret != clientSecret {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_CLIENT,
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "client_secret is invalid",
|
||||
}
|
||||
}
|
||||
@@ -605,16 +611,17 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
|
||||
Id: application.GetId(),
|
||||
Name: fmt.Sprintf("app/%s", application.Name),
|
||||
}
|
||||
accessToken, _, err := generateJwtToken(application, nullUser, "", scope, host)
|
||||
|
||||
accessToken, _, tokenName, err := generateJwtToken(application, nullUser, "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: ENDPOINT_ERROR,
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}
|
||||
}
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: util.GenerateId(),
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: application.Organization,
|
||||
@@ -630,15 +637,16 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetTokenByUser
|
||||
// Implicit flow
|
||||
func GetTokenByUser(application *Application, user *User, scope string, host string) (*Token, error) {
|
||||
accessToken, refreshToken, err := generateJwtToken(application, user, "", scope, host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: util.GenerateId(),
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
@@ -655,12 +663,13 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetWechatMiniProgramToken
|
||||
// Wechat Mini Program flow
|
||||
func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string) (*Token, *TokenError) {
|
||||
mpProvider := GetWechatMiniProgramProvider(application)
|
||||
if mpProvider == nil {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_CLIENT,
|
||||
Error: InvalidClient,
|
||||
ErrorDescription: "the application does not support wechat mini program",
|
||||
}
|
||||
}
|
||||
@@ -669,14 +678,14 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
|
||||
session, err := mpIdp.GetSessionByCode(code)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("get wechat mini program session error: %s", err.Error()),
|
||||
}
|
||||
}
|
||||
openId, unionId := session.Openid, session.Unionid
|
||||
if openId == "" && unionId == "" {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_REQUEST,
|
||||
Error: InvalidRequest,
|
||||
ErrorDescription: "the wechat mini program session is invalid",
|
||||
}
|
||||
}
|
||||
@@ -684,7 +693,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
|
||||
if user == nil {
|
||||
if !application.EnableSignUp {
|
||||
return nil, &TokenError{
|
||||
Error: INVALID_GRANT,
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "the application does not allow to sign up new account",
|
||||
}
|
||||
}
|
||||
@@ -703,28 +712,31 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
|
||||
Avatar: avatar,
|
||||
SignupApplication: application.Name,
|
||||
WeChat: openId,
|
||||
WeChatUnionId: unionId,
|
||||
Type: "normal-user",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
IsAdmin: false,
|
||||
IsGlobalAdmin: false,
|
||||
IsForbidden: false,
|
||||
IsDeleted: false,
|
||||
Properties: map[string]string{
|
||||
UserPropertiesWechatOpenId: openId,
|
||||
UserPropertiesWechatUnionId: unionId,
|
||||
},
|
||||
}
|
||||
AddUser(user)
|
||||
}
|
||||
|
||||
accessToken, refreshToken, err := generateJwtToken(application, user, "", "", host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: ENDPOINT_ERROR,
|
||||
Error: EndpointError,
|
||||
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
token := &Token{
|
||||
Owner: application.Owner,
|
||||
Name: util.GenerateId(),
|
||||
Name: tokenName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Application: application.Name,
|
||||
Organization: user.Owner,
|
||||
|
@@ -136,6 +136,7 @@ func GenerateId() {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// GetCasTokenByPgt
|
||||
/**
|
||||
@ret1: whether a token is found
|
||||
@ret2: token, nil if not found
|
||||
@@ -150,6 +151,7 @@ func GetCasTokenByPgt(pgt string) (bool, *CasAuthenticationSuccess, string, stri
|
||||
return false, nil, "", ""
|
||||
}
|
||||
|
||||
// GetCasTokenByTicket
|
||||
/**
|
||||
@ret1: whether a token is found
|
||||
@ret2: token, nil if not found
|
||||
@@ -207,6 +209,7 @@ func GenerateCasToken(userId string, service string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetValidationBySaml
|
||||
/**
|
||||
@ret1: saml response
|
||||
@ret2: the service URL who requested to issue this token
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
|
||||
@@ -60,7 +61,7 @@ func getShortClaims(claims Claims) ClaimsShort {
|
||||
return res
|
||||
}
|
||||
|
||||
func generateJwtToken(application *Application, user *User, nonce string, scope string, host string) (string, string, error) {
|
||||
func generateJwtToken(application *Application, user *User, nonce string, scope string, host string) (string, string, string, error) {
|
||||
nowTime := time.Now()
|
||||
expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour)
|
||||
refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours) * time.Hour)
|
||||
@@ -72,6 +73,9 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
||||
originBackend = origin
|
||||
}
|
||||
|
||||
name := util.GenerateId()
|
||||
jti := fmt.Sprintf("%s/%s", application.Owner, name)
|
||||
|
||||
claims := Claims{
|
||||
User: user,
|
||||
Nonce: nonce,
|
||||
@@ -85,7 +89,7 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
||||
ExpiresAt: jwt.NewNumericDate(expireTime),
|
||||
NotBefore: jwt.NewNumericDate(nowTime),
|
||||
IssuedAt: jwt.NewNumericDate(nowTime),
|
||||
ID: "",
|
||||
ID: jti,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -110,17 +114,17 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
||||
// RSA private key
|
||||
key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
token.Header["kid"] = cert.Name
|
||||
tokenString, err := token.SignedString(key)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", "", "", err
|
||||
}
|
||||
refreshTokenString, err := refreshToken.SignedString(key)
|
||||
|
||||
return tokenString, refreshTokenString, err
|
||||
return tokenString, refreshTokenString, name, err
|
||||
}
|
||||
|
||||
func ParseJwtToken(token string, cert *Cert) (*Claims, error) {
|
||||
|
@@ -24,6 +24,11 @@ import (
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
const (
|
||||
UserPropertiesWechatUnionId = "wechatUnionId"
|
||||
UserPropertiesWechatOpenId = "wechatOpenId"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
@@ -73,32 +78,31 @@ type User struct {
|
||||
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
|
||||
LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"`
|
||||
|
||||
GitHub string `xorm:"github varchar(100)" json:"github"`
|
||||
Google string `xorm:"varchar(100)" json:"google"`
|
||||
QQ string `xorm:"qq varchar(100)" json:"qq"`
|
||||
WeChat string `xorm:"wechat varchar(100)" json:"wechat"`
|
||||
WeChatUnionId string `xorm:"varchar(100)" json:"unionId"`
|
||||
Facebook string `xorm:"facebook varchar(100)" json:"facebook"`
|
||||
DingTalk string `xorm:"dingtalk varchar(100)" json:"dingtalk"`
|
||||
Weibo string `xorm:"weibo varchar(100)" json:"weibo"`
|
||||
Gitee string `xorm:"gitee varchar(100)" json:"gitee"`
|
||||
LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"`
|
||||
Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
|
||||
Lark string `xorm:"lark varchar(100)" json:"lark"`
|
||||
Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"`
|
||||
Adfs string `xorm:"adfs varchar(100)" json:"adfs"`
|
||||
Baidu string `xorm:"baidu varchar(100)" json:"baidu"`
|
||||
Alipay string `xorm:"alipay varchar(100)" json:"alipay"`
|
||||
Casdoor string `xorm:"casdoor varchar(100)" json:"casdoor"`
|
||||
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
|
||||
Apple string `xorm:"apple varchar(100)" json:"apple"`
|
||||
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
|
||||
Slack string `xorm:"slack varchar(100)" json:"slack"`
|
||||
Steam string `xorm:"steam varchar(100)" json:"steam"`
|
||||
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
|
||||
Okta string `xorm:"okta varchar(100)" json:"okta"`
|
||||
Douyin string `xorm:"douyin varchar(100)" json:"douyin"`
|
||||
Custom string `xorm:"custom varchar(100)" json:"custom"`
|
||||
GitHub string `xorm:"github varchar(100)" json:"github"`
|
||||
Google string `xorm:"varchar(100)" json:"google"`
|
||||
QQ string `xorm:"qq varchar(100)" json:"qq"`
|
||||
WeChat string `xorm:"wechat varchar(100)" json:"wechat"`
|
||||
Facebook string `xorm:"facebook varchar(100)" json:"facebook"`
|
||||
DingTalk string `xorm:"dingtalk varchar(100)" json:"dingtalk"`
|
||||
Weibo string `xorm:"weibo varchar(100)" json:"weibo"`
|
||||
Gitee string `xorm:"gitee varchar(100)" json:"gitee"`
|
||||
LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"`
|
||||
Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
|
||||
Lark string `xorm:"lark varchar(100)" json:"lark"`
|
||||
Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"`
|
||||
Adfs string `xorm:"adfs varchar(100)" json:"adfs"`
|
||||
Baidu string `xorm:"baidu varchar(100)" json:"baidu"`
|
||||
Alipay string `xorm:"alipay varchar(100)" json:"alipay"`
|
||||
Casdoor string `xorm:"casdoor varchar(100)" json:"casdoor"`
|
||||
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
|
||||
Apple string `xorm:"apple varchar(100)" json:"apple"`
|
||||
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
|
||||
Slack string `xorm:"slack varchar(100)" json:"slack"`
|
||||
Steam string `xorm:"steam varchar(100)" json:"steam"`
|
||||
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
|
||||
Okta string `xorm:"okta varchar(100)" json:"okta"`
|
||||
Douyin string `xorm:"douyin varchar(100)" json:"douyin"`
|
||||
Custom string `xorm:"custom varchar(100)" json:"custom"`
|
||||
|
||||
WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"`
|
||||
|
||||
@@ -107,6 +111,9 @@ type User struct {
|
||||
|
||||
Roles []*Role `json:"roles"`
|
||||
Permissions []*Permission `json:"permissions"`
|
||||
|
||||
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
|
||||
SigninWrongTimes int `json:"signinWrongTimes"`
|
||||
}
|
||||
|
||||
type Userinfo struct {
|
||||
@@ -243,7 +250,7 @@ func getUserByWechatId(wechatOpenId string, wechatUnionId string) *User {
|
||||
wechatUnionId = wechatOpenId
|
||||
}
|
||||
user := &User{}
|
||||
existed, err := adapter.Engine.Where("wechat = ? OR wechat = ? OR unionid = ?", wechatOpenId, wechatUnionId, wechatUnionId).Get(user)
|
||||
existed, err := adapter.Engine.Where("wechat = ? OR wechat = ?", wechatOpenId, wechatUnionId).Get(user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -372,6 +379,7 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
|
||||
"owner", "display_name", "avatar",
|
||||
"location", "address", "region", "language", "affiliation", "title", "homepage", "bio", "score", "tag", "signup_application",
|
||||
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials",
|
||||
"signin_wrong_times", "last_signin_wrong_time",
|
||||
}
|
||||
}
|
||||
if isGlobalAdmin {
|
||||
|
@@ -50,30 +50,31 @@ func GetWebAuthnObject(host string) *webauthn.WebAuthn {
|
||||
return webAuthn
|
||||
}
|
||||
|
||||
// WebAuthnID
|
||||
// implementation of webauthn.User interface
|
||||
func (u *User) WebAuthnID() []byte {
|
||||
return []byte(u.GetId())
|
||||
func (user *User) WebAuthnID() []byte {
|
||||
return []byte(user.GetId())
|
||||
}
|
||||
|
||||
func (u *User) WebAuthnName() string {
|
||||
return u.Name
|
||||
func (user *User) WebAuthnName() string {
|
||||
return user.Name
|
||||
}
|
||||
|
||||
func (u *User) WebAuthnDisplayName() string {
|
||||
return u.DisplayName
|
||||
func (user *User) WebAuthnDisplayName() string {
|
||||
return user.DisplayName
|
||||
}
|
||||
|
||||
func (u *User) WebAuthnCredentials() []webauthn.Credential {
|
||||
return u.WebauthnCredentials
|
||||
func (user *User) WebAuthnCredentials() []webauthn.Credential {
|
||||
return user.WebauthnCredentials
|
||||
}
|
||||
|
||||
func (u *User) WebAuthnIcon() string {
|
||||
return u.Avatar
|
||||
func (user *User) WebAuthnIcon() string {
|
||||
return user.Avatar
|
||||
}
|
||||
|
||||
// CredentialExcludeList returns a CredentialDescriptor array filled with all the user's credentials
|
||||
func (u *User) CredentialExcludeList() []protocol.CredentialDescriptor {
|
||||
credentials := u.WebAuthnCredentials()
|
||||
func (user *User) CredentialExcludeList() []protocol.CredentialDescriptor {
|
||||
credentials := user.WebAuthnCredentials()
|
||||
credentialExcludeList := []protocol.CredentialDescriptor{}
|
||||
for _, cred := range credentials {
|
||||
descriptor := protocol.CredentialDescriptor{
|
||||
@@ -86,16 +87,16 @@ func (u *User) CredentialExcludeList() []protocol.CredentialDescriptor {
|
||||
return credentialExcludeList
|
||||
}
|
||||
|
||||
func (u *User) AddCredentials(credential webauthn.Credential, isGlobalAdmin bool) bool {
|
||||
u.WebauthnCredentials = append(u.WebauthnCredentials, credential)
|
||||
return UpdateUser(u.GetId(), u, []string{"webauthnCredentials"}, isGlobalAdmin)
|
||||
func (user *User) AddCredentials(credential webauthn.Credential, isGlobalAdmin bool) bool {
|
||||
user.WebauthnCredentials = append(user.WebauthnCredentials, credential)
|
||||
return UpdateUser(user.GetId(), user, []string{"webauthnCredentials"}, isGlobalAdmin)
|
||||
}
|
||||
|
||||
func (u *User) DeleteCredentials(credentialIdBase64 string) bool {
|
||||
for i, credential := range u.WebauthnCredentials {
|
||||
func (user *User) DeleteCredentials(credentialIdBase64 string) bool {
|
||||
for i, credential := range user.WebauthnCredentials {
|
||||
if base64.StdEncoding.EncodeToString(credential.ID) == credentialIdBase64 {
|
||||
u.WebauthnCredentials = append(u.WebauthnCredentials[0:i], u.WebauthnCredentials[i+1:]...)
|
||||
return UpdateUserForAllFields(u.GetId(), u)
|
||||
user.WebauthnCredentials = append(user.WebauthnCredentials[0:i], user.WebauthnCredentials[i+1:]...)
|
||||
return UpdateUserForAllFields(user.GetId(), user)
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@@ -42,7 +42,7 @@ type VerificationRecord struct {
|
||||
|
||||
func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
|
||||
if provider == nil {
|
||||
return fmt.Errorf("Please set an Email provider first")
|
||||
return fmt.Errorf("please set an Email provider first")
|
||||
}
|
||||
|
||||
sender := organization.DisplayName
|
||||
@@ -60,7 +60,7 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
|
||||
|
||||
func SendVerificationCodeToPhone(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
|
||||
if provider == nil {
|
||||
return errors.New("Please set a SMS provider first")
|
||||
return errors.New("please set a SMS provider first")
|
||||
}
|
||||
|
||||
code := getRandomCode(5)
|
||||
@@ -85,7 +85,7 @@ func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordT
|
||||
|
||||
now := time.Now().Unix()
|
||||
if has && now-record.Time < 60 {
|
||||
return errors.New("You can only send one code in 60s.")
|
||||
return errors.New("you can only send one code in 60s")
|
||||
}
|
||||
|
||||
record.Owner = provider.Owner
|
||||
|
3
pp/gc.go
3
pp/gc.go
@@ -20,7 +20,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -147,7 +146,7 @@ func (pp *GcPaymentProvider) doPost(postBytes []byte) ([]byte, error) {
|
||||
}
|
||||
}(resp.Body)
|
||||
|
||||
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -78,11 +78,18 @@ func initAPI() {
|
||||
beego.Router("/api/delete-role", &controllers.ApiController{}, "POST:DeleteRole")
|
||||
|
||||
beego.Router("/api/get-permissions", &controllers.ApiController{}, "GET:GetPermissions")
|
||||
beego.Router("/api/get-permissions-by-submitter", &controllers.ApiController{}, "GET:GetPermissionsBySubmitter")
|
||||
beego.Router("/api/get-permission", &controllers.ApiController{}, "GET:GetPermission")
|
||||
beego.Router("/api/update-permission", &controllers.ApiController{}, "POST:UpdatePermission")
|
||||
beego.Router("/api/add-permission", &controllers.ApiController{}, "POST:AddPermission")
|
||||
beego.Router("/api/delete-permission", &controllers.ApiController{}, "POST:DeletePermission")
|
||||
|
||||
beego.Router("/api/enforce", &controllers.ApiController{}, "POST:Enforce")
|
||||
beego.Router("/api/batch-enforce", &controllers.ApiController{}, "POST:BatchEnforce")
|
||||
beego.Router("/api/get-all-objects", &controllers.ApiController{}, "GET:GetAllObjects")
|
||||
beego.Router("/api/get-all-actions", &controllers.ApiController{}, "GET:GetAllActions")
|
||||
beego.Router("/api/get-all-roles", &controllers.ApiController{}, "GET:GetAllRoles")
|
||||
|
||||
beego.Router("/api/get-models", &controllers.ApiController{}, "GET:GetModels")
|
||||
beego.Router("/api/get-model", &controllers.ApiController{}, "GET:GetModel")
|
||||
beego.Router("/api/update-model", &controllers.ApiController{}, "POST:UpdateModel")
|
||||
|
@@ -20,7 +20,7 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -168,7 +168,7 @@ func GetMinLenStr(strs ...string) string {
|
||||
}
|
||||
|
||||
func ReadStringFromPath(path string) string {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -177,7 +177,7 @@ func ReadStringFromPath(path string) string {
|
||||
}
|
||||
|
||||
func WriteStringToPath(s string, path string) {
|
||||
err := ioutil.WriteFile(path, []byte(s), 0o644)
|
||||
err := os.WriteFile(path, []byte(s), 0o644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@@ -20,7 +20,6 @@
|
||||
"plugins": ["unused-imports"],
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended"],
|
||||
"rules": {
|
||||
// "eqeqeq": "error",
|
||||
"semi": ["error", "always"],
|
||||
"indent": ["error", 2],
|
||||
// follow antd's style guide
|
||||
@@ -42,7 +41,13 @@
|
||||
"space-before-function-paren": ["error", "never"],
|
||||
"no-trailing-spaces": ["error", { "ignoreComments": true }],
|
||||
"eol-last": ["error", "always"],
|
||||
// "no-var": ["error"],
|
||||
"no-var": ["error"],
|
||||
"prefer-const": [
|
||||
"error",
|
||||
{
|
||||
"destructuring": "all"
|
||||
}
|
||||
],
|
||||
"curly": ["error", "all"],
|
||||
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
|
||||
"no-mixed-spaces-and-tabs": "error",
|
||||
@@ -81,15 +86,15 @@
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"no-unused-vars": "off",
|
||||
"react/no-deprecated": "error",
|
||||
"react/jsx-key": "error",
|
||||
"no-console": "error",
|
||||
"eqeqeq": "error",
|
||||
|
||||
"react/prop-types": "off",
|
||||
"react/display-name": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
|
||||
// don't use strict mod now, otherwise there are a lot of errors in the codebase
|
||||
"no-unused-vars": "off",
|
||||
"react/no-deprecated": "warn",
|
||||
"no-case-declarations": "warn",
|
||||
"react/jsx-key": "warn"
|
||||
"no-case-declarations": "off"
|
||||
}
|
||||
}
|
||||
|
@@ -63,6 +63,18 @@
|
||||
"devDependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-plugin-react": "^7.30.1"
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
"husky": "^4.3.8",
|
||||
"lint-staged": "^13.0.3"
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{js,jsx,css,sass,ts,tsx}": [
|
||||
"yarn fix"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ class AccountTable extends React.Component {
|
||||
}
|
||||
|
||||
addRow(table) {
|
||||
let row = {name: Setting.getNewRowNameForTable(table, "Please select an account item"), visible: true, viewRule: "Public", modifyRule: "Self"};
|
||||
const row = {name: Setting.getNewRowNameForTable(table, "Please select an account item"), visible: true, viewRule: "Public", modifyRule: "Self"};
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
@@ -141,7 +141,7 @@ class AccountTable extends React.Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
let options = [
|
||||
const options = [
|
||||
{id: "Public", name: "Public"},
|
||||
{id: "Self", name: "Self"},
|
||||
{id: "Admin", name: "Admin"},
|
||||
|
@@ -182,7 +182,7 @@ class App extends Component {
|
||||
}
|
||||
|
||||
setLanguage(account) {
|
||||
let language = account?.language;
|
||||
const language = account?.language;
|
||||
if (language !== "" && language !== i18next.language) {
|
||||
Setting.setLanguage(language);
|
||||
}
|
||||
@@ -242,7 +242,7 @@ class App extends Component {
|
||||
});
|
||||
|
||||
Setting.showMessage("success", "Logged out successfully");
|
||||
let redirectUri = res.data2;
|
||||
const redirectUri = res.data2;
|
||||
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
|
||||
Setting.goToLink(redirectUri);
|
||||
} else if (owner !== "built-in") {
|
||||
@@ -322,7 +322,7 @@ class App extends Component {
|
||||
}
|
||||
|
||||
renderAccount() {
|
||||
let res = [];
|
||||
const res = [];
|
||||
|
||||
if (this.state.account === undefined) {
|
||||
return null;
|
||||
@@ -349,7 +349,7 @@ class App extends Component {
|
||||
}
|
||||
|
||||
renderMenu() {
|
||||
let res = [];
|
||||
const res = [];
|
||||
|
||||
if (this.state.account === null || this.state.account === undefined) {
|
||||
return [];
|
||||
@@ -385,13 +385,17 @@ class App extends Component {
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
res.push(
|
||||
<Menu.Item key="/permissions">
|
||||
<Link to="/permissions">
|
||||
{i18next.t("general:Permissions")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
}
|
||||
|
||||
res.push(
|
||||
<Menu.Item key="/permissions">
|
||||
<Link to="/permissions">
|
||||
{i18next.t("general:Permissions")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
if (Setting.isAdminUser(this.state.account)) {
|
||||
res.push(
|
||||
<Menu.Item key="/models">
|
||||
<Link to="/models">
|
||||
@@ -668,27 +672,29 @@ class App extends Component {
|
||||
renderPage() {
|
||||
if (this.isDoorPages()) {
|
||||
return (
|
||||
<div>
|
||||
<Switch>
|
||||
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />)} />
|
||||
<Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/auto-signup/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signup"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
<Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage account={this.state.account} type={"saml"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout clearAccount={() => this.setState({account: null})} {...props} />)} />
|
||||
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage type={"cas"} mode={"signup"} account={this.state.account} {...props} />);}} />
|
||||
<Route exact path="/callback" component={AuthCallback} />
|
||||
<Route exact path="/callback/saml" component={SamlCallback} />
|
||||
<Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...props} />)} />
|
||||
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...props} />)} />
|
||||
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage account={this.state.account} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} {...props} />)} />
|
||||
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
|
||||
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
|
||||
</Switch>
|
||||
<div style={{position: "relative", minHeight: "100vh"}}>
|
||||
<div id="content-wrap" style={{flexDirection: "column"}}>
|
||||
<Switch>
|
||||
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />)} />
|
||||
<Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/auto-signup/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signup"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
<Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage account={this.state.account} type={"saml"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout clearAccount={() => this.setState({account: null})} {...props} />)} />
|
||||
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage type={"cas"} mode={"signup"} account={this.state.account} {...props} />);}} />
|
||||
<Route exact path="/callback" component={AuthCallback} />
|
||||
<Route exact path="/callback/saml" component={SamlCallback} />
|
||||
<Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...props} />)} />
|
||||
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...props} />)} />
|
||||
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage account={this.state.account} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} {...props} />)} />
|
||||
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
|
||||
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
|
||||
</Switch>
|
||||
</div>
|
||||
{
|
||||
this.renderFooter()
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
@import '~antd/dist/antd.less';
|
||||
|
||||
@StaticBaseUrl:"https://cdn.casbin.org";
|
||||
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -46,7 +48,7 @@
|
||||
}
|
||||
|
||||
.language_box {
|
||||
background: url("https://cdn.casbin.org/img/muti_language.svg");
|
||||
background: url("@{StaticBaseUrl}/img/muti_language.svg");
|
||||
background-size: 25px, 25px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
|
@@ -120,7 +120,7 @@ class ApplicationEditPage extends React.Component {
|
||||
updateApplicationField(key, value) {
|
||||
value = this.parseApplicationField(key, value);
|
||||
|
||||
let application = this.state.application;
|
||||
const application = this.state.application;
|
||||
application[key] = value;
|
||||
this.setState({
|
||||
application: application,
|
||||
@@ -566,8 +566,8 @@ class ApplicationEditPage extends React.Component {
|
||||
|
||||
renderSignupSigninPreview() {
|
||||
let signUpUrl = `/signup/${this.state.application.name}`;
|
||||
let signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${this.state.application.redirectUris[0]}&scope=read&state=casdoor`;
|
||||
let maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
||||
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${this.state.application.redirectUris[0]}&scope=read&state=casdoor`;
|
||||
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
||||
if (!this.state.application.enablePassword) {
|
||||
signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize");
|
||||
}
|
||||
@@ -613,8 +613,8 @@ class ApplicationEditPage extends React.Component {
|
||||
}
|
||||
|
||||
renderPromptPreview() {
|
||||
let promptUrl = `/prompt/${this.state.application.name}`;
|
||||
let maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
||||
const promptUrl = `/prompt/${this.state.application.name}`;
|
||||
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
||||
return (
|
||||
<Col span={11}>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
||||
@@ -634,7 +634,7 @@ class ApplicationEditPage extends React.Component {
|
||||
}
|
||||
|
||||
submitApplicationEdit(willExist) {
|
||||
let application = Setting.deepCopy(this.state.application);
|
||||
const application = Setting.deepCopy(this.state.application);
|
||||
ApplicationBackend.updateApplication(this.state.application.owner, this.state.applicationName, application)
|
||||
.then((res) => {
|
||||
if (res.msg === "") {
|
||||
|
@@ -30,7 +30,7 @@ class ApplicationListPage extends BaseListPage {
|
||||
name: `application_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
displayName: `New Application - ${randomName}`,
|
||||
logo: "https://cdn.casdoor.com/logo/casdoor-logo_1185x256.png",
|
||||
logo: `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`,
|
||||
enablePassword: true,
|
||||
enableSignUp: true,
|
||||
enableSigninSession: false,
|
||||
@@ -250,8 +250,8 @@ class ApplicationListPage extends BaseListPage {
|
||||
}
|
||||
|
||||
fetch = (params = {}) => {
|
||||
let field = params.searchedColumn, value = params.searchText;
|
||||
let sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
const field = params.searchedColumn, value = params.searchText;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
this.setState({loading: true});
|
||||
ApplicationBackend.getApplications("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
.then((res) => {
|
||||
|
@@ -57,7 +57,7 @@ class CertEditPage extends React.Component {
|
||||
updateCertField(key, value) {
|
||||
value = this.parseCertField(key, value);
|
||||
|
||||
let cert = this.state.cert;
|
||||
const cert = this.state.cert;
|
||||
cert[key] = value;
|
||||
this.setState({
|
||||
cert: cert,
|
||||
@@ -214,7 +214,7 @@ class CertEditPage extends React.Component {
|
||||
}
|
||||
|
||||
submitCertEdit(willExist) {
|
||||
let cert = Setting.deepCopy(this.state.cert);
|
||||
const cert = Setting.deepCopy(this.state.cert);
|
||||
CertBackend.updateCert(this.state.cert.owner, this.state.certName, cert)
|
||||
.then((res) => {
|
||||
if (res.msg === "") {
|
||||
|
@@ -198,7 +198,7 @@ class CertListPage extends BaseListPage {
|
||||
|
||||
fetch = (params = {}) => {
|
||||
let field = params.searchedColumn, value = params.searchText;
|
||||
let sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
if (params.category !== undefined && params.category !== null) {
|
||||
field = "category";
|
||||
value = params.category;
|
||||
|
@@ -81,7 +81,6 @@ export const CropperDiv = (props) => {
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
console.log("Clicked cancel button");
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
@@ -103,7 +102,7 @@ export const CropperDiv = (props) => {
|
||||
onCancel={handleCancel}
|
||||
width={600}
|
||||
footer={
|
||||
[<Button block type="primary" onClick={handleOk}>{i18next.t("user:Set new profile picture")}</Button>]
|
||||
[<Button block key="submit" type="primary" onClick={handleOk}>{i18next.t("user:Set new profile picture")}</Button>]
|
||||
}
|
||||
>
|
||||
<Col style={{margin: "0px auto 40px auto", width: 1000, height: 300}}>
|
||||
|
@@ -35,7 +35,7 @@ class LdapSyncPage extends React.Component {
|
||||
}
|
||||
|
||||
syncUsers() {
|
||||
let selectedUsers = this.state.selectedUsers;
|
||||
const selectedUsers = this.state.selectedUsers;
|
||||
if (selectedUsers === null || selectedUsers.length === 0) {
|
||||
Setting.showMessage("error", "Please select al least 1 user first");
|
||||
return;
|
||||
@@ -44,10 +44,10 @@ class LdapSyncPage extends React.Component {
|
||||
LdapBackend.syncUsers(this.state.ldap.owner, this.state.ldap.id, selectedUsers)
|
||||
.then((res => {
|
||||
if (res.status === "ok") {
|
||||
let exist = res.data.exist;
|
||||
let failed = res.data.failed;
|
||||
let existUser = [];
|
||||
let failedUser = [];
|
||||
const exist = res.data.exist;
|
||||
const failed = res.data.failed;
|
||||
const existUser = [];
|
||||
const failedUser = [];
|
||||
|
||||
if ((!exist || exist.length === 0) && (!failed || failed.length === 0)) {
|
||||
Setting.goToLink(`/organizations/${this.state.ldap.owner}/users`);
|
||||
@@ -103,7 +103,7 @@ class LdapSyncPage extends React.Component {
|
||||
}
|
||||
|
||||
getExistUsers(owner, users) {
|
||||
let uuidArray = [];
|
||||
const uuidArray = [];
|
||||
users.forEach(elem => {
|
||||
uuidArray.push(elem.uuid);
|
||||
});
|
||||
@@ -119,11 +119,11 @@ class LdapSyncPage extends React.Component {
|
||||
}
|
||||
|
||||
buildValArray(data, key) {
|
||||
let valTypesArray = [];
|
||||
const valTypesArray = [];
|
||||
|
||||
if (data !== null && data.length > 0) {
|
||||
data.forEach(elem => {
|
||||
let val = elem[key];
|
||||
const val = elem[key];
|
||||
if (!valTypesArray.includes(val)) {
|
||||
valTypesArray.push(val);
|
||||
}
|
||||
@@ -133,10 +133,10 @@ class LdapSyncPage extends React.Component {
|
||||
}
|
||||
|
||||
buildFilter(data, key) {
|
||||
let filterArray = [];
|
||||
const filterArray = [];
|
||||
|
||||
if (data !== null && data.length > 0) {
|
||||
let valArray = this.buildValArray(data, key);
|
||||
const valArray = this.buildValArray(data, key);
|
||||
valArray.forEach(elem => {
|
||||
filterArray.push({
|
||||
text: elem,
|
||||
|
@@ -81,7 +81,7 @@ class ModelEditPage extends React.Component {
|
||||
updateModelField(key, value) {
|
||||
value = this.parseModelField(key, value);
|
||||
|
||||
let model = this.state.model;
|
||||
const model = this.state.model;
|
||||
model[key] = value;
|
||||
this.setState({
|
||||
model: model,
|
||||
@@ -155,7 +155,7 @@ class ModelEditPage extends React.Component {
|
||||
}
|
||||
|
||||
submitModelEdit(willExist) {
|
||||
let model = Setting.deepCopy(this.state.model);
|
||||
const model = Setting.deepCopy(this.state.model);
|
||||
ModelBackend.updateModel(this.state.organizationName, this.state.modelName, model)
|
||||
.then((res) => {
|
||||
if (res.msg === "") {
|
||||
|
@@ -174,7 +174,7 @@ class ModelListPage extends BaseListPage {
|
||||
|
||||
fetch = (params = {}) => {
|
||||
let field = params.searchedColumn, value = params.searchText;
|
||||
let sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
if (params.type !== undefined && params.type !== null) {
|
||||
field = "type";
|
||||
value = params.type;
|
||||
|
@@ -75,7 +75,7 @@ class OrganizationEditPage extends React.Component {
|
||||
updateOrganizationField(key, value) {
|
||||
value = this.parseOrganizationField(key, value);
|
||||
|
||||
let organization = this.state.organization;
|
||||
const organization = this.state.organization;
|
||||
organization[key] = value;
|
||||
this.setState({
|
||||
organization: organization,
|
||||
@@ -283,7 +283,7 @@ class OrganizationEditPage extends React.Component {
|
||||
}
|
||||
|
||||
submitOrganizationEdit(willExist) {
|
||||
let organization = Setting.deepCopy(this.state.organization);
|
||||
const organization = Setting.deepCopy(this.state.organization);
|
||||
OrganizationBackend.updateOrganization(this.state.organization.owner, this.state.organizationName, organization)
|
||||
.then((res) => {
|
||||
if (res.msg === "") {
|
||||
|
@@ -30,11 +30,11 @@ class OrganizationListPage extends BaseListPage {
|
||||
createdTime: moment().format(),
|
||||
displayName: `New Organization - ${randomName}`,
|
||||
websiteUrl: "https://door.casdoor.com",
|
||||
favicon: "https://cdn.casdoor.com/static/favicon.png",
|
||||
favicon: `${Setting.StaticBaseUrl}/img/favicon.png`,
|
||||
passwordType: "plain",
|
||||
PasswordSalt: "",
|
||||
phonePrefix: "86",
|
||||
defaultAvatar: "https://casbin.org/img/casbin.svg",
|
||||
defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`,
|
||||
tags: [],
|
||||
masterPassword: "",
|
||||
enableSoftDeletion: false,
|
||||
@@ -266,7 +266,7 @@ class OrganizationListPage extends BaseListPage {
|
||||
|
||||
fetch = (params = {}) => {
|
||||
let field = params.searchedColumn, value = params.searchText;
|
||||
let sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
if (params.passwordType !== undefined && params.passwordType !== null) {
|
||||
field = "passwordType";
|
||||
value = params.passwordType;
|
||||
|
@@ -54,7 +54,7 @@ export const PasswordModal = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
let hasOldPassword = user.password !== "";
|
||||
const hasOldPassword = user.password !== "";
|
||||
|
||||
return (
|
||||
<Row>
|
||||
|
@@ -60,7 +60,7 @@ class PaymentEditPage extends React.Component {
|
||||
updatePaymentField(key, value) {
|
||||
value = this.parsePaymentField(key, value);
|
||||
|
||||
let payment = this.state.payment;
|
||||
const payment = this.state.payment;
|
||||
payment[key] = value;
|
||||
this.setState({
|
||||
payment: payment,
|
||||
@@ -440,7 +440,7 @@ class PaymentEditPage extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
let payment = Setting.deepCopy(this.state.payment);
|
||||
const payment = Setting.deepCopy(this.state.payment);
|
||||
PaymentBackend.updatePayment(this.state.payment.owner, this.state.paymentName, payment)
|
||||
.then((res) => {
|
||||
if (res.msg === "") {
|
||||
|
@@ -250,7 +250,7 @@ class PaymentListPage extends BaseListPage {
|
||||
|
||||
fetch = (params = {}) => {
|
||||
let field = params.searchedColumn, value = params.searchText;
|
||||
let sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
if (params.type !== undefined && params.type !== null) {
|
||||
field = "type";
|
||||
value = params.type;
|
||||
|
@@ -83,7 +83,7 @@ class PaymentResultPage extends React.Component {
|
||||
title={`${i18next.t("payment:The payment is still under processing")}: ${payment.productDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}, ${i18next.t("payment:please wait for a few seconds...")}`}
|
||||
subTitle={i18next.t("payment:Please click the below button to return to the original website")}
|
||||
extra={[
|
||||
<Spin size="large" tip={i18next.t("payment:Processing...")} />,
|
||||
<Spin key="returnUrl" size="large" tip={i18next.t("payment:Processing...")} />,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
@@ -22,6 +22,7 @@ import i18next from "i18next";
|
||||
import * as RoleBackend from "./backend/RoleBackend";
|
||||
import * as ModelBackend from "./backend/ModelBackend";
|
||||
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||
import moment from "moment/moment";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
@@ -116,7 +117,7 @@ class PermissionEditPage extends React.Component {
|
||||
updatePermissionField(key, value) {
|
||||
value = this.parsePermissionField(key, value);
|
||||
|
||||
let permission = this.state.permission;
|
||||
const permission = this.state.permission;
|
||||
permission[key] = value;
|
||||
this.setState({
|
||||
permission: permission,
|
||||
@@ -210,6 +211,20 @@ class PermissionEditPage extends React.Component {
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("role:Sub domains"), i18next.t("role:Sub domains - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.domains} onChange={(value => {
|
||||
this.updateRoleField("domains", value);
|
||||
})}>
|
||||
{
|
||||
this.state.permission.domains.map((domain, index) => <Option key={index} value={domain}>{domain}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("permission:Resource type"), i18next.t("permission:Resource type - Tooltip"))} :
|
||||
@@ -283,12 +298,69 @@ class PermissionEditPage extends React.Component {
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("permission:Submitter"), i18next.t("permission:Submitter - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={this.state.permission.submitter} onChange={e => {
|
||||
this.updatePermissionField("submitter", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("permission:Approver"), i18next.t("permission:Approver - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={this.state.permission.approver} onChange={e => {
|
||||
this.updatePermissionField("approver", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("permission:Approve time"), i18next.t("permission:Approve time - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={Setting.getFormattedDate(this.state.permission.approveTime)} onChange={e => {
|
||||
this.updatePermissionField("approveTime", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("permission:State"), i18next.t("permission:State - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select disabled={!Setting.isLocalAdminUser(this.props.account)} virtual={false} style={{width: "100%"}} value={this.state.permission.state} onChange={(value => {
|
||||
if (this.state.permission.state !== value) {
|
||||
if (value === "Approved") {
|
||||
this.updatePermissionField("approver", this.props.account.name);
|
||||
this.updatePermissionField("approveTime", moment().format());
|
||||
} else {
|
||||
this.updatePermissionField("approver", "");
|
||||
this.updatePermissionField("approveTime", "");
|
||||
}
|
||||
}
|
||||
|
||||
this.updatePermissionField("state", value);
|
||||
})}>
|
||||
{
|
||||
[
|
||||
{id: "Approved", name: "Approved"},
|
||||
{id: "Pending", name: "Pending"},
|
||||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
submitPermissionEdit(willExist) {
|
||||
let permission = Setting.deepCopy(this.state.permission);
|
||||
const permission = Setting.deepCopy(this.state.permission);
|
||||
PermissionBackend.updatePermission(this.state.organizationName, this.state.permissionName, permission)
|
||||
.then((res) => {
|
||||
if (res.msg === "") {
|
||||
|
@@ -25,17 +25,22 @@ class PermissionListPage extends BaseListPage {
|
||||
newPermission() {
|
||||
const randomName = Setting.getRandomName();
|
||||
return {
|
||||
owner: "built-in",
|
||||
owner: this.props.account.owner,
|
||||
name: `permission_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
displayName: `New Permission - ${randomName}`,
|
||||
users: [],
|
||||
users: [this.props.account.name],
|
||||
roles: [],
|
||||
domains: [],
|
||||
resourceType: "Application",
|
||||
resources: ["app-built-in"],
|
||||
actions: ["Read"],
|
||||
effect: "Allow",
|
||||
isEnabled: true,
|
||||
submitter: this.props.account.name,
|
||||
approver: "",
|
||||
approveTime: "",
|
||||
state: "Pending",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,6 +48,10 @@ class PermissionListPage extends BaseListPage {
|
||||
const newPermission = this.newPermission();
|
||||
PermissionBackend.addPermission(newPermission)
|
||||
.then((res) => {
|
||||
if (res.msg !== "") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.props.history.push({pathname: `/permissions/${newPermission.owner}/${newPermission.name}`, mode: "add"});
|
||||
}
|
||||
)
|
||||
@@ -139,6 +148,16 @@ class PermissionListPage extends BaseListPage {
|
||||
return Setting.getTags(text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("role:Sub domains"),
|
||||
dataIndex: "domains",
|
||||
key: "domains",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("domains"),
|
||||
render: (text, record, index) => {
|
||||
return Setting.getTags(text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("permission:Resource type"),
|
||||
dataIndex: "resourceType",
|
||||
@@ -243,13 +262,15 @@ class PermissionListPage extends BaseListPage {
|
||||
|
||||
fetch = (params = {}) => {
|
||||
let field = params.searchedColumn, value = params.searchText;
|
||||
let sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
if (params.type !== undefined && params.type !== null) {
|
||||
field = "type";
|
||||
value = params.type;
|
||||
}
|
||||
this.setState({loading: true});
|
||||
PermissionBackend.getPermissions("", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
|
||||
const getPermissions = Setting.isAdminUser(this.props.account) ? PermissionBackend.getPermissions : PermissionBackend.getPermissionsBySubmitter;
|
||||
getPermissions("", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
|
@@ -69,7 +69,7 @@ class ProductEditPage extends React.Component {
|
||||
updateProductField(key, value) {
|
||||
value = this.parseProductField(key, value);
|
||||
|
||||
let product = this.state.product;
|
||||
const product = this.state.product;
|
||||
product[key] = value;
|
||||
this.setState({
|
||||
product: product,
|
||||
@@ -252,7 +252,7 @@ class ProductEditPage extends React.Component {
|
||||
}
|
||||
|
||||
renderPreview() {
|
||||
let buyUrl = `/products/${this.state.product.name}/buy`;
|
||||
const buyUrl = `/products/${this.state.product.name}/buy`;
|
||||
return (
|
||||
<Col span={22} style={{display: "flex", flexDirection: "column"}}>
|
||||
<a style={{marginBottom: "10px", display: "flex"}} target="_blank" rel="noreferrer" href={buyUrl}>
|
||||
@@ -268,7 +268,7 @@ class ProductEditPage extends React.Component {
|
||||
}
|
||||
|
||||
submitProductEdit(willExist) {
|
||||
let product = Setting.deepCopy(this.state.product);
|
||||
const product = Setting.deepCopy(this.state.product);
|
||||
ProductBackend.updateProduct(this.state.product.owner, this.state.productName, product)
|
||||
.then((res) => {
|
||||
if (res.msg === "") {
|
||||
|
@@ -30,7 +30,7 @@ class ProductListPage extends BaseListPage {
|
||||
name: `product_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
displayName: `New Product - ${randomName}`,
|
||||
image: "https://cdn.casdoor.com/logo/casdoor-logo_1185x256.png",
|
||||
image: `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`,
|
||||
tag: "Casdoor Summit 2022",
|
||||
currency: "USD",
|
||||
price: 300,
|
||||
@@ -269,7 +269,7 @@ class ProductListPage extends BaseListPage {
|
||||
|
||||
fetch = (params = {}) => {
|
||||
let field = params.searchedColumn, value = params.searchText;
|
||||
let sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
if (params.type !== undefined && params.type !== null) {
|
||||
field = "type";
|
||||
value = params.type;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user