Compare commits

...

64 Commits

Author SHA1 Message Date
Yixiang Zhao
8e9ed1205b feat: support RBAC with domains model and add adapter to specify the table name for policy storage (#1020)
* feat: support RBAC with domains model and add adapter to specify the table name for policy storage

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

* fix some bugs

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

* add i18n

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

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-08-18 11:49:32 +08:00
Yixiang Zhao
a341c65bb1 fix: third-party user may login to the built-in organization (#1024)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-08-17 23:18:38 +08:00
疯魔慕薇
91fa024f0b feat: Mock SMS (#1009)
1. Update go-sms-sender to v0.3.0.
2. Fix: avoid page crash if not found provider info.

Signed-off-by: 疯魔慕薇 <kfanjian@gmail.com>

Signed-off-by: 疯魔慕薇 <kfanjian@gmail.com>
2022-08-17 22:02:45 +08:00
q1anx1
aedef1eea1 feat(login): add login limit (#1023)
* feat(login): add login limit

* chore: rename vars

* chore: use `string`

* fix: clear the signin error times after succeessfull login

* chore: modify code position
2022-08-17 01:39:53 +08:00
Mikey
70f2988f09 feat: revert to the original behavior for wrapActionResponse() (#1021)
Revert: 340fbe135d

see: https://github.com/casdoor/casdoor-go-sdk/pull/36.
2022-08-16 00:20:37 +08:00
疯魔慕薇
2dcdfbe6d3 fix: error login logic of mobile phone login (#1017)
* fix: #1016

1. Limit username cannot be digital.
2. Check avoid repeat register with same phone or email.

Signed-off-by: 疯魔慕薇 <kfanjian@gmail.com>

* Update check.go

Signed-off-by: 疯魔慕薇 <kfanjian@gmail.com>
Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-08-16 00:14:26 +08:00
Gucheng Wang
c92d34e27c Add GetPermissionsBySubmitter() 2022-08-15 14:09:12 +08:00
Yixiang Zhao
dfbf7753c3 feat: support RBAC model in permission (#1006)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-08-15 10:24:26 +08:00
leoshine
ba732b3075 feat: use staticBaseUrl for all static resources (#1015)
* feat: modify system image link

* Update App.less

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-08-15 09:18:21 +08:00
q1anx1
ca13247572 chore(style): use eqeqeq (#1013) 2022-08-13 11:23:16 +08:00
q1anx1
108fdc174f chore(ci): add linter the check go code style (#991)
* feat(ci): auto format go code

* fix: fix #997

* chore(ci): add go code style linter

* fix: fix cmd error

* chore: add `linter` of needs

* chore: modiy commnet style
2022-08-13 10:57:13 +08:00
q1anx1
a741c5179a chore(style): modify eslint rules (#1011)
* chore(style): use strict rules

* chore: modify position

* chore(style): warn about `console.log` and `==`

* fix: fix `console.log` error

* Update CropperDiv.js

* Update HomePage.js

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-08-13 00:04:18 +08:00
Ryao
6676cc8ff3 fix: add JTI name to JWT token (#989)
* feat: add jti to jwt

* fix

* fix
2022-08-11 14:32:47 +08:00
q1anx1
13de019d08 chore(ci): use cache to accelerate ci (#1004)
* chore(ci): use cache to accelerate ci

* chore: comment
2022-08-11 10:20:53 +08:00
Bingchang Chen
53ad454962 feat: responsive footer (#1003) 2022-08-10 20:31:42 +08:00
leoshine
fb203a6f30 feat: delete .env to fix static file path bug (#999) 2022-08-10 12:22:27 +08:00
Gucheng Wang
f716a0985f Add disableSsl to provider. 2022-08-09 23:38:35 +08:00
Gucheng Wang
340fbe135d Fix error in wrapActionResponse() 2022-08-09 23:34:07 +08:00
Mikey
79119760f2 style: golint (#988) 2022-08-09 16:50:49 +08:00
jakiuncle
4dd67a8dcb fix: fix all frontend warnings (#983)
* fix:fix all frontend warnings

* fix:fix all frontend warnings

* fix:fix all frontend warnings

* fix:fix all frontend warnings

* fix:fix all frontend warnings

* fix:fix all frontend warnings
2022-08-09 12:19:56 +08:00
q1anx1
deed857788 chore(style): allow case declarations and ban var (#987)
* chore(style): allow case declarations

* chore(style): ban `var` and prefer `const`
2022-08-08 23:35:24 +08:00
Mikey
802995ed16 refactor: remove WeChat unionId to properties (#985) 2022-08-08 18:43:12 +08:00
q1anx1
b14554a5ba feat(web): check style when commit (#980)
feat(web): check style when commit
2022-08-08 00:10:31 +08:00
Gucheng Wang
4665ffa759 Update i18n data 2022-08-08 00:02:47 +08:00
Gucheng Wang
f914e8e929 Add permission_enforcer.go 2022-08-07 23:57:06 +08:00
Yixiang Zhao
dc33b41107 feat: expose some casbin APIs (#955)
* feat: expose some casbin APIs

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

* feat: add BatchEnforce API

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

* fix: solve requested changes

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-08-07 23:42:45 +08:00
Bingchang Chen
ee8dd23a56 fix: fixed footer css (#951) 2022-08-07 17:22:52 +08:00
github-actions[bot]
08d0269e30 refactor: New Crowdin translations by Github Action (#974)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2022-08-07 16:06:52 +08:00
leoshine
8e5cd18c91 fix: Restrict the request permissions of providers and applications (#970) 2022-08-07 16:05:05 +08:00
Gucheng Wang
32b4d98c2a Add ExtendProductWithProviders(). 2022-08-07 15:45:06 +08:00
q1anx1
2ea58cd639 chore(style): use gofumpt to fmt go code (#967) 2022-08-07 12:26:14 +08:00
q1anx1
45d2745b67 chore(style): add eslint rules: no-unused-imports and no-unused-vars (#976)
* feat(web): no-unused-imports and no-unused-vars

* chore: fix json style
2022-08-07 11:51:53 +08:00
Yang Luo
cba338eef2 Merge pull request #973 from qianxi0410/eslint
feat(web): add some eslint rules
2022-08-07 00:41:51 +08:00
qianxi0410
c428de6e42 feat: fix some comma dangle 2022-08-07 00:17:27 +08:00
qianxi0410
9bca6bb72e feat: no-multi-spacing 2022-08-07 00:06:20 +08:00
qianxi0410
cd966116d4 feat: comma dangle 2022-08-06 23:54:56 +08:00
qianxi0410
9abf1b9d73 feat: key spacing 2022-08-06 23:47:28 +08:00
qianxi0410
6aaba6debd feat: space between infix op 2022-08-06 23:43:09 +08:00
qianxi0410
77565712e0 feat: no-multi-empty-lines 2022-08-06 23:38:03 +08:00
qianxi0410
d025259db7 feat: indent 2022-08-06 23:36:20 +08:00
Artem
aafdc546fa fix: panic when creating a user in a non-existent org (#969) 2022-08-06 22:30:56 +08:00
q1anx1
539ca2d731 chore(web): add fix command (#964) 2022-08-05 23:40:04 +08:00
Ryao
ea326b3513 fix: show social buttons on signup page (#962) 2022-08-05 18:59:56 +08:00
Товарищ программист
98ef766fb4 fix: fix webauthn entry cannot add bug (#960)
* fix: fix webauthn

* Update LoginPage.js

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-08-05 17:43:04 +08:00
Gucheng Wang
e94ada9ea2 Fix new accountItem. 2022-08-05 15:36:07 +08:00
Resulte Lee
4ea482223d feat: add geetest captcha (#953) 2022-08-04 20:55:04 +08:00
Gucheng Wang
d55ae7d1d2 Enable some other DBs 2022-08-04 20:28:09 +08:00
imp2002
d72e00605f fix: updateProviderField when add provider payment (#952) 2022-08-04 19:39:25 +08:00
zzjin
be74cb621f feat: Support sub-directory (#943)
By adding PUBLIC_URL to relative `.`

Signed-off-by: zzjin <tczzjin@gmail.com>
2022-08-02 00:21:15 +08:00
q1anx1
13404d6035 feat: fix binding after registration causes the page to crash (#945) 2022-08-01 21:08:10 +08:00
Mikey
afa9c530ad fix: panic triggered when user is nil (#940) 2022-07-31 23:23:36 +08:00
Yang Luo
1600615aca Support sqlite3 DB 2022-07-31 18:11:18 +08:00
Mikey
2bb8491499 fix: unable to get user if profile is private (#936) 2022-07-31 10:54:41 +08:00
Mikey
293283ed25 feat: add get user by phone (#934)
* fix: check reset phone & email modify rules

* Update verification.go

* Update organization.go

* feat: add get user by phone

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-07-31 01:02:28 +08:00
q1anx1
9cb519d1e9 fix: Admins should not be allowed to add third-party login for their members (#932)
* feat: admin can unlink the other user

* feat: global admin can unlink other user

* fix
2022-07-30 23:11:02 +08:00
Yixiang Zhao
fb9b8f1662 fix: skip the duplicated users when sync users (#928)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-07-30 22:24:23 +08:00
Mikey
2fec3f72ae fix: check reset phone & email modify rules (#927)
* fix: check reset phone & email modify rules

* Update verification.go

* Update organization.go

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-07-30 18:17:13 +08:00
Yang Luo
11695220a8 Use user.GetId() 2022-07-30 17:40:30 +08:00
Resulte Lee
155660b0d7 feat: get user api return roles and permissions (#929) 2022-07-30 17:31:56 +08:00
imp2002
1c72f5300c feat: fix 'Enable code sign' is not displayed in the login page (#925) 2022-07-28 23:11:33 +08:00
q1anx1
3dd56195d9 fix: fix the problem of link error (#923) 2022-07-28 21:52:10 +08:00
Resulte Lee
8865244262 fix: add oauth login auto close page (#915) 2022-07-26 23:03:55 +08:00
Yixiang Zhao
3400fa1e9c feat: support local login for non-built-in users (#911)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-07-26 19:27:24 +08:00
Resulte Lee
bdc5c92ef0 fix: send code missing parameter & show more detail responseError (#910) 2022-07-25 23:46:38 +08:00
206 changed files with 3115 additions and 1422 deletions

View File

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

View File

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

View File

@@ -83,7 +83,7 @@ p, *, *, GET, /api/get-account, *, *
p, *, *, GET, /api/userinfo, *, *
p, *, *, *, /api/login/oauth, *, *
p, *, *, GET, /api/get-application, *, *
p, *, *, GET, /api/get-applications, *, *
p, *, *, GET, /api/get-organization-applications, *, *
p, *, *, GET, /api/get-user, *, *
p, *, *, GET, /api/get-user-application, *, *
p, *, *, GET, /api/get-resources, *, *
@@ -92,7 +92,6 @@ p, *, *, POST, /api/buy-product, *, *
p, *, *, GET, /api/get-payment, *, *
p, *, *, POST, /api/update-payment, *, *
p, *, *, POST, /api/invoice-payment, *, *
p, *, *, GET, /api/get-providers, *, *
p, *, *, POST, /api/notify-payment, *, *
p, *, *, POST, /api/unlink, *, *
p, *, *, POST, /api/set-password, *, *

View File

@@ -18,7 +18,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"sort"
@@ -31,8 +31,7 @@ import (
const AliyunCaptchaVerifyUrl = "http://afs.aliyuncs.com"
type AliyunCaptchaProvider struct {
}
type AliyunCaptchaProvider struct{}
func NewAliyunCaptchaProvider() *AliyunCaptchaProvider {
captcha := &AliyunCaptchaProvider{}
@@ -81,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
}

View File

@@ -16,8 +16,7 @@ package captcha
import "github.com/casdoor/casdoor/object"
type DefaultCaptchaProvider struct {
}
type DefaultCaptchaProvider struct{}
func NewDefaultCaptchaProvider() *DefaultCaptchaProvider {
captcha := &DefaultCaptchaProvider{}

81
captcha/geetest.go Normal file
View File

@@ -0,0 +1,81 @@
// 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 captcha
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"time"
"github.com/casdoor/casdoor/util"
)
const GEETESTCaptchaVerifyUrl = "http://gcaptcha4.geetest.com/validate"
type GEETESTCaptchaProvider struct{}
func NewGEETESTCaptchaProvider() *GEETESTCaptchaProvider {
captcha := &GEETESTCaptchaProvider{}
return captcha
}
func (captcha *GEETESTCaptchaProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
pathData, err := url.ParseQuery(token)
if err != nil {
return false, err
}
signToken := util.GetHmacSha256(clientSecret, pathData["lot_number"][0])
formData := make(url.Values)
formData["lot_number"] = []string{pathData["lot_number"][0]}
formData["captcha_output"] = []string{pathData["captcha_output"][0]}
formData["pass_token"] = []string{pathData["pass_token"][0]}
formData["gen_time"] = []string{pathData["gen_time"][0]}
formData["sign_token"] = []string{signToken}
captchaId := pathData["captcha_id"][0]
cli := http.Client{Timeout: time.Second * 5}
resp, err := cli.PostForm(fmt.Sprintf("%s?captcha_id=%s", GEETESTCaptchaVerifyUrl, captchaId), formData)
if err != nil || resp.StatusCode != 200 {
return false, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return false, err
}
type captchaResponse struct {
Result string `json:"result"`
Reason string `json:"reason"`
}
captchaResp := &captchaResponse{}
err = json.Unmarshal(body, captchaResp)
if err != nil {
return false, err
}
if captchaResp.Result == "success" {
return true, nil
}
return false, errors.New(captchaResp.Reason)
}

View File

@@ -17,7 +17,7 @@ package captcha
import (
"encoding/json"
"errors"
"io/ioutil"
"io"
"net/http"
"net/url"
"strings"
@@ -25,8 +25,7 @@ import (
const HCaptchaVerifyUrl = "https://hcaptcha.com/siteverify"
type HCaptchaProvider struct {
}
type HCaptchaProvider struct{}
func NewHCaptchaProvider() *HCaptchaProvider {
captcha := &HCaptchaProvider{}
@@ -44,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
}

View File

@@ -27,6 +27,8 @@ func GetCaptchaProvider(captchaType string) CaptchaProvider {
return NewHCaptchaProvider()
} else if captchaType == "Aliyun Captcha" {
return NewAliyunCaptchaProvider()
} else if captchaType == "GEETEST" {
return NewGEETESTCaptchaProvider()
}
return nil
}

View File

@@ -17,7 +17,7 @@ package captcha
import (
"encoding/json"
"errors"
"io/ioutil"
"io"
"net/http"
"net/url"
"strings"
@@ -25,8 +25,7 @@ import (
const ReCaptchaVerifyUrl = "https://recaptcha.net/recaptcha/api/siteverify"
type ReCaptchaProvider struct {
}
type ReCaptchaProvider struct{}
func NewReCaptchaProvider() *ReCaptchaProvider {
captcha := &ReCaptchaProvider{}
@@ -44,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
}

View File

@@ -16,3 +16,4 @@ verificationCodeTimeout = 10
initScore = 2000
logPostOnly = true
origin =
staticBaseUrl = "https://cdn.casbin.org"

View File

@@ -49,7 +49,7 @@ func GetConfigInt64(key string) (int64, error) {
func init() {
// this array contains the beego configuration items that may be modified via env
var presetConfigItems = []string{"httpport", "appname"}
presetConfigItems := []string{"httpport", "appname"}
for _, key := range presetConfigItems {
if value, ok := os.LookupEnv(key); ok {
beego.AppConfig.Set(key, value)

View File

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

View File

@@ -217,7 +217,7 @@ func (c *ApiController) Signup() {
record.User = user.Name
util.SafeGoroutine(func() { object.AddRecord(record) })
userId := fmt.Sprintf("%s/%s", user.Owner, user.Name)
userId := user.GetId()
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
c.ResponseOk(userId)
@@ -274,6 +274,7 @@ func (c *ApiController) GetAccount() {
c.ServeJSON()
}
// GetUserinfo
// UserInfo
// @Title UserInfo
// @Tag Account API

View File

@@ -94,6 +94,29 @@ func (c *ApiController) GetUserApplication() {
c.ServeJSON()
}
// GetOrganizationApplications
// @Title GetOrganizationApplications
// @Tag Application API
// @Description get the detail of the organization's application
// @Param organization query string true "The organization name"
// @Success 200 {array} object.Application The Response object
// @router /get-organization-applications [get]
func (c *ApiController) GetOrganizationApplications() {
userId := c.GetSessionUsername()
owner := c.Input().Get("owner")
organization := c.Input().Get("organization")
if organization == "" {
c.ResponseError("Parameter organization is missing")
return
}
var applications []*object.Application
applications = object.GetApplicationsByOrganizationName(owner, organization)
c.Data["json"] = object.GetMaskedApplications(applications, userId)
c.ServeJSON()
}
// UpdateApplication
// @Title UpdateApplication
// @Tag Application API

View File

@@ -44,7 +44,6 @@ func tokenToResponse(token *object.Token) *Response {
return &Response{Status: "error", Msg: "fail to get accessToken", Data: token.AccessToken}
}
return &Response{Status: "ok", Msg: "", Data: token.AccessToken}
}
// HandleLoggedIn ...
@@ -94,7 +93,6 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
token, _ := object.GetTokenByUser(application, user, scope, c.Ctx.Request.Host)
resp = tokenToResponse(token)
}
} else if form.Type == ResponseTypeSaml { // saml flow
res, redirectUrl, err := object.GetSamlResponse(application, user, form.SamlRequest, c.Ctx.Request.Host)
if err != nil {
@@ -120,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

View File

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

View File

@@ -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"
)
@@ -49,7 +49,6 @@ func (c *RootController) CasValidate() {
c.Ctx.Output.Body([]byte(fmt.Sprintf("yes\n%s\n", response.User)))
return
}
}
// token not found
c.Ctx.Output.Body([]byte("no\n"))
@@ -117,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()
@@ -127,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
}
@@ -184,7 +183,6 @@ func (c *RootController) CasProxy() {
c.Data["xml"] = serviceResponse
c.ServeXML()
}
}
func (c *RootController) SamlValidate() {
@@ -216,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 {
@@ -224,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) {

View File

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

View File

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

View File

@@ -22,6 +22,7 @@ import (
type LinkForm struct {
ProviderType string `json:"providerType"`
User object.User `json:"user"`
}
// Unlink ...
@@ -40,16 +41,55 @@ func (c *ApiController) Unlink() {
}
providerType := form.ProviderType
// the user will be unlinked from the provider
unlinkedUser := form.User
user := object.GetUser(userId)
value := object.GetUserField(user, providerType)
if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin {
// if the user is not the same as the one we are unlinking, we need to make sure the user is the global admin.
c.ResponseError("You are not the global admin, you can't unlink other users")
return
}
if user.Id == unlinkedUser.Id && !user.IsGlobalAdmin {
// if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error.
application := object.GetApplicationByUser(user)
if application == nil {
c.ResponseError("You can't unlink yourself, you are not a member of any application")
return
}
if len(application.Providers) == 0 {
c.ResponseError("This application has no providers")
return
}
provider := application.GetProviderItemByType(providerType)
if provider == nil {
c.ResponseError("This application has no providers of type " + providerType)
return
}
if !provider.CanUnlink {
c.ResponseError("This provider can't be unlinked")
return
}
}
// only two situations can happen here
// 1. the user is the global admin
// 2. the user is unlinking themselves and provider can be unlinked
value := object.GetUserField(&unlinkedUser, providerType)
if value == "" {
c.ResponseError("Please link first", value)
return
}
object.ClearUserOAuthProperties(user, providerType)
object.ClearUserOAuthProperties(&unlinkedUser, providerType)
object.LinkUserAccount(user, providerType, "")
object.LinkUserAccount(&unlinkedUser, providerType, "")
c.ResponseOk()
}

View File

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

View File

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

View File

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

View File

@@ -49,6 +49,7 @@ func (c *ApiController) GetProducts() {
}
}
// GetProduct
// @Title GetProduct
// @Tag Product API
// @Description get product
@@ -58,10 +59,14 @@ func (c *ApiController) GetProducts() {
func (c *ApiController) GetProduct() {
id := c.Input().Get("id")
c.Data["json"] = object.GetProduct(id)
product := object.GetProduct(id)
object.ExtendProductWithProviders(product)
c.Data["json"] = product
c.ServeJSON()
}
// UpdateProduct
// @Title UpdateProduct
// @Tag Product API
// @Description update product
@@ -82,6 +87,7 @@ func (c *ApiController) UpdateProduct() {
c.ServeJSON()
}
// AddProduct
// @Title AddProduct
// @Tag Product API
// @Description add product
@@ -99,6 +105,7 @@ func (c *ApiController) AddProduct() {
c.ServeJSON()
}
// DeleteProduct
// @Title DeleteProduct
// @Tag Product API
// @Description delete product
@@ -116,6 +123,7 @@ func (c *ApiController) DeleteProduct() {
c.ServeJSON()
}
// BuyProduct
// @Title BuyProduct
// @Tag Product API
// @Description buy product

View File

@@ -16,6 +16,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
@@ -47,6 +48,7 @@ func (c *ApiController) GetProviders() {
}
}
// GetProvider
// @Title GetProvider
// @Tag Provider API
// @Description get provider
@@ -60,6 +62,7 @@ func (c *ApiController) GetProvider() {
c.ServeJSON()
}
// UpdateProvider
// @Title UpdateProvider
// @Tag Provider API
// @Description update provider
@@ -80,6 +83,7 @@ func (c *ApiController) UpdateProvider() {
c.ServeJSON()
}
// AddProvider
// @Title AddProvider
// @Tag Provider API
// @Description add provider
@@ -97,6 +101,7 @@ func (c *ApiController) AddProvider() {
c.ServeJSON()
}
// DeleteProvider
// @Title DeleteProvider
// @Tag Provider API
// @Description delete provider

View File

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

View File

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

View File

@@ -16,6 +16,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
@@ -47,6 +48,7 @@ func (c *ApiController) GetSyncers() {
}
}
// GetSyncer
// @Title GetSyncer
// @Tag Syncer API
// @Description get syncer
@@ -60,6 +62,7 @@ func (c *ApiController) GetSyncer() {
c.ServeJSON()
}
// UpdateSyncer
// @Title UpdateSyncer
// @Tag Syncer API
// @Description update syncer
@@ -80,6 +83,7 @@ func (c *ApiController) UpdateSyncer() {
c.ServeJSON()
}
// AddSyncer
// @Title AddSyncer
// @Tag Syncer API
// @Description add syncer
@@ -97,6 +101,7 @@ func (c *ApiController) AddSyncer() {
c.ServeJSON()
}
// DeleteSyncer
// @Title DeleteSyncer
// @Tag Syncer API
// @Description delete syncer
@@ -114,6 +119,7 @@ func (c *ApiController) DeleteSyncer() {
c.ServeJSON()
}
// RunSyncer
// @Title RunSyncer
// @Tag Syncer API
// @Description run syncer

View File

@@ -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) {
@@ -273,6 +273,7 @@ func (c *ApiController) TokenLogout() {
// 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

View File

@@ -81,11 +81,15 @@ func (c *ApiController) GetUsers() {
// @Tag User API
// @Description get user
// @Param id query string true "The id of the user"
// @Param owner query string false "The owner of the user"
// @Param email query string false "The email of the user"
// @Param phone query string false "The phone of the user"
// @Success 200 {object} object.User The Response object
// @router /get-user [get]
func (c *ApiController) GetUser() {
id := c.Input().Get("id")
email := c.Input().Get("email")
phone := c.Input().Get("phone")
userId := c.Input().Get("userId")
owner := c.Input().Get("owner")
@@ -96,7 +100,7 @@ func (c *ApiController) GetUser() {
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", owner))
if !organization.IsProfilePublic {
requestUserId := c.GetSessionUsername()
hasPermission, err := object.CheckUserPermission(requestUserId, id, false)
hasPermission, err := object.CheckUserPermission(requestUserId, id, owner, false)
if !hasPermission {
c.ResponseError(err.Error())
return
@@ -104,14 +108,24 @@ func (c *ApiController) GetUser() {
}
var user *object.User
if email != "" {
switch {
case email != "":
user = object.GetUserByEmail(owner, email)
} else if userId != "" {
case phone != "":
user = object.GetUserByPhone(owner, phone)
case userId != "":
user = object.GetUserByUserId(owner, userId)
} else {
default:
user = object.GetUser(id)
}
if user != nil {
roles := object.GetRolesByUser(user.GetId())
user.Roles = roles
permissions := object.GetPermissionsByUser(user.GetId())
user.Permissions = permissions
}
c.Data["json"] = object.GetMaskedUser(user)
c.ServeJSON()
}
@@ -252,7 +266,7 @@ func (c *ApiController) SetPassword() {
requestUserId := c.GetSessionUsername()
userId := fmt.Sprintf("%s/%s", userOwner, userName)
hasPermission, err := object.CheckUserPermission(requestUserId, userId, true)
hasPermission, err := object.CheckUserPermission(requestUserId, userId, userOwner, true)
if !hasPermission {
c.ResponseError(err.Error())
return
@@ -284,6 +298,7 @@ func (c *ApiController) SetPassword() {
c.ServeJSON()
}
// CheckUserPassword
// @Title CheckUserPassword
// @router /check-user-password [post]
// @Tag User API

View File

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

View File

@@ -49,8 +49,24 @@ func (c *ApiController) SendVerificationCode() {
applicationId := c.Ctx.Request.Form.Get("applicationId")
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
if destType == "" || dest == "" || applicationId == "" || !strings.Contains(applicationId, "/") || checkType == "" {
c.ResponseError("Missing parameter.")
if destType == "" {
c.ResponseError("Missing parameter: type.")
return
}
if dest == "" {
c.ResponseError("Missing parameter: dest.")
return
}
if applicationId == "" {
c.ResponseError("Missing parameter: applicationId.")
return
}
if !strings.Contains(applicationId, "/") {
c.ResponseError("Wrong parameter: applicationId.")
return
}
if checkType == "" {
c.ResponseError("Missing parameter: checkType.")
return
}
@@ -82,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
@@ -152,13 +168,35 @@ func (c *ApiController) ResetEmailOrPhone() {
}
checkDest := dest
if destType == "phone" {
org := object.GetOrganizationByUser(user)
if destType == "phone" {
phoneItem := object.GetAccountItemByName("Phone", org)
if phoneItem == nil {
c.ResponseError("Unable to get the phone modify rule.")
return
}
if pass, errMsg := object.CheckAccountItemModifyRule(phoneItem, user); !pass {
c.ResponseError(errMsg)
return
}
phonePrefix := "86"
if org != nil && org.PhonePrefix != "" {
phonePrefix = org.PhonePrefix
}
checkDest = fmt.Sprintf("+%s%s", phonePrefix, dest)
} else if destType == "email" {
emailItem := object.GetAccountItemByName("Email", org)
if emailItem == nil {
c.ResponseError("Unable to get the email modify rule.")
return
}
if pass, errMsg := object.CheckAccountItemModifyRule(emailItem, user); !pass {
c.ResponseError(errMsg)
return
}
}
if ret := object.CheckVerificationCode(checkDest, code); len(ret) != 0 {
c.ResponseError(ret)

View File

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

View File

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

View File

@@ -24,7 +24,6 @@ func NewArgon2idCredManager() *Argon2idCredManager {
}
func (cm *Argon2idCredManager) GetHashedPassword(password string, userSalt string, organizationSalt string) string {
hash, err := argon2id.CreateHash(password, argon2id.DefaultParams)
if err != nil {
return ""

View File

@@ -17,6 +17,7 @@ package cred
import (
"crypto/sha256"
"encoding/base64"
"golang.org/x/crypto/pbkdf2"
)

4
go.mod
View File

@@ -10,10 +10,11 @@ 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
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
github.com/go-ldap/ldap/v3 v3.3.0
@@ -23,6 +24,7 @@ require (
github.com/google/uuid v1.2.0
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/lestrrat-go/jwx v0.9.0
github.com/lib/pq v1.8.0
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/qiangmzsx/string-adapter/v2 v2.1.0
github.com/robfig/cron/v3 v3.0.1

6
go.sum
View File

@@ -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=
@@ -125,6 +125,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b h1:L63RATZFZuFMXy6ixnKmv3eNAXwYQF6HW1vd4IYsQqQ=
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b/go.mod h1:EYSpSkwoEcryMmQGfhol2IiB3IMN9IIIaNd/wcAQMGQ=
@@ -173,6 +174,7 @@ github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptG
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=

View File

@@ -19,7 +19,7 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"time"
@@ -59,12 +59,12 @@ func (idp *AdfsIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *AdfsIdProvider) getConfig(hostUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: fmt.Sprintf("%s/adfs/oauth2/authorize", hostUrl),
TokenURL: fmt.Sprintf("%s/adfs/oauth2/token", hostUrl),
}
var config = &oauth2.Config{
config := &oauth2.Config{
Endpoint: endpoint,
}
@@ -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),

View File

@@ -24,7 +24,6 @@ import (
"encoding/json"
"encoding/pem"
"io"
"io/ioutil"
"net/http"
"net/url"
"sort"
@@ -56,12 +55,12 @@ func (idp *AlipayIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *AlipayIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm",
TokenURL: "https://openapi.alipay.com/gateway.do",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"", ""},
Endpoint: endpoint,
ClientID: clientId,
@@ -205,8 +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
}

View File

@@ -18,7 +18,7 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"golang.org/x/oauth2"
@@ -46,12 +46,12 @@ func (idp *BaiduIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *BaiduIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: "https://openapi.baidu.com/oauth/2.0/authorize",
TokenURL: "https://openapi.baidu.com/oauth/2.0/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"email"},
Endpoint: endpoint,
}
@@ -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
}

View File

@@ -18,7 +18,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
@@ -47,12 +46,12 @@ func (idp *BilibiliIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *BilibiliIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://api.bilibili.com/x/account-oauth2/v1/token",
AuthURL: "http://member.bilibili.com/arcopen/fn/user/account/info",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"", ""},
Endpoint: endpoint,
ClientID: clientId,
@@ -76,6 +75,7 @@ type BilibiliIdProviderTokenResponse struct {
Data BilibiliProviderToken `json:"data"`
}
// GetToken
/*
{
"code": 0,
@@ -104,7 +104,6 @@ func (idp *BilibiliIdProvider) GetToken(code string) (*oauth2.Token, error) {
}
data, err := idp.postWithBody(pTokenParams, idp.Config.Endpoint.TokenURL)
if err != nil {
return nil, err
}
@@ -167,12 +166,11 @@ func (idp *BilibiliIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
userInfoUrl := fmt.Sprintf("%s?%s", idp.Config.Endpoint.AuthURL, params.Encode())
resp, err := idp.Client.Get(userInfoUrl)
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(resp.Body)
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
@@ -206,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
}

View File

@@ -17,7 +17,7 @@ package idp
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"time"
@@ -71,8 +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
}
@@ -91,7 +90,6 @@ func (idp *CasdoorIdProvider) GetToken(code string) (*oauth2.Token, error) {
Expiry: time.Unix(time.Now().Unix()+int64(pToken.ExpiresIn), 0),
}
return token, nil
}
/*
@@ -133,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
}
@@ -155,5 +153,4 @@ func (idp *CasdoorIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error
AvatarUrl: cdUserinfo.AvatarUrl,
}
return userInfo, nil
}

View File

@@ -18,7 +18,7 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
_ "net/url"
_ "time"
@@ -36,7 +36,7 @@ func NewCustomIdProvider(clientId string, clientSecret string, redirectUrl strin
idp := &CustomIdProvider{}
idp.UserInfoUrl = userInfoUrl
var config = &oauth2.Config{
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
RedirectURL: redirectUrl,
@@ -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
}

View File

@@ -18,7 +18,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"time"
@@ -48,12 +47,12 @@ func (idp *DingTalkIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *DingTalkIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: "https://api.dingtalk.com/v1.0/contact/users/me",
TokenURL: "https://api.dingtalk.com/v1.0/oauth2/userAccessToken",
}
var config = &oauth2.Config{
config := &oauth2.Config{
// DingTalk not allow to set scopes,here it is just a placeholder,
// convenient to use later
Scopes: []string{"", ""},
@@ -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
}

View File

@@ -18,7 +18,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"time"
@@ -42,12 +42,12 @@ func (idp *DouyinIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *DouyinIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://open.douyin.com/oauth/access_token",
AuthURL: "https://open.douyin.com/platform/oauth/connect",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"user_info"},
Endpoint: endpoint,
ClientID: clientId,
@@ -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
}

View File

@@ -46,11 +46,11 @@ func (idp *FacebookIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *FacebookIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://graph.facebook.com/oauth/access_token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"email,public_profile"},
Endpoint: endpoint,
ClientID: clientId,
@@ -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"`

View File

@@ -19,7 +19,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
@@ -49,11 +48,11 @@ func (idp *GiteeIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *GiteeIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://gitee.com/oauth/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"user_info emails"},
Endpoint: endpoint,
@@ -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
}

View File

@@ -18,7 +18,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"strconv"
"strings"
@@ -49,12 +48,12 @@ func (idp *GithubIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *GithubIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: "https://github.com/login/oauth/authorize",
TokenURL: "https://github.com/login/oauth/access_token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"user:email", "read:user"},
Endpoint: endpoint,
}
@@ -93,7 +92,6 @@ func (idp *GithubIdProvider) GetToken(code string) (*oauth2.Token, error) {
}
return token, nil
}
//{
@@ -203,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
}
@@ -237,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
}

View File

@@ -17,7 +17,7 @@ package idp
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"strconv"
@@ -46,11 +46,11 @@ func (idp *GitlabIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *GitlabIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://gitlab.com/oauth/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"read_user+profile"},
Endpoint: endpoint,
ClientID: clientId,
@@ -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
}

View File

@@ -19,7 +19,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"golang.org/x/oauth2"
@@ -47,12 +47,12 @@ func (idp *GoogleIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *GoogleIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"profile", "email"},
Endpoint: endpoint,
}
@@ -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
}

View File

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

View File

@@ -17,7 +17,7 @@ package idp
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"golang.org/x/oauth2"
@@ -43,7 +43,7 @@ func (idp *InfoflowInternalIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *InfoflowInternalIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var config = &oauth2.Config{
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
RedirectURL: redirectUrl,
@@ -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
}

View File

@@ -18,7 +18,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"time"
@@ -47,7 +46,7 @@ func (idp *InfoflowIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *InfoflowIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var config = &oauth2.Config{
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
RedirectURL: redirectUrl,
@@ -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
}

View File

@@ -17,7 +17,6 @@ package idp
import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"strings"
"time"
@@ -45,11 +44,11 @@ func (idp *LarkIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *LarkIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{},
Endpoint: endpoint,
ClientID: clientId,
@@ -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
}

View File

@@ -18,7 +18,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
@@ -47,11 +46,11 @@ func (idp *LinkedInIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *LinkedInIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://www.linkedIn.com/oauth/v2/accessToken",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"email,public_profile"},
Endpoint: endpoint,
ClientID: clientId,
@@ -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
}

View File

@@ -17,7 +17,7 @@ package idp
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"time"
@@ -48,12 +48,12 @@ func (idp *OktaIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *OktaIdProvider) getConfig(hostUrl string, clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: fmt.Sprintf("%s/v1/token", hostUrl),
AuthURL: fmt.Sprintf("%s/v1/authorize", hostUrl),
}
var config = &oauth2.Config{
config := &oauth2.Config{
// openid is required for authentication requests
// get more details via: https://developer.okta.com/docs/reference/api/oidc/#reserved-scopes
Scopes: []string{"openid", "profile", "email"},
@@ -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
}

View File

@@ -18,7 +18,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"regexp"
@@ -48,11 +48,11 @@ func (idp *QqIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *QqIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://graph.qq.com/oauth2.0/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"get_user_info"},
Endpoint: endpoint,
}
@@ -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
}

View File

@@ -47,11 +47,11 @@ func (idp *WeChatIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *WeChatIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://graph.qq.com/oauth2.0/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"snsapi_login"},
Endpoint: endpoint,
ClientID: clientId,
@@ -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.
}

View File

@@ -17,7 +17,7 @@ package idp
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"golang.org/x/oauth2"
@@ -42,7 +42,7 @@ func (idp *WeChatMiniProgramIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *WeChatMiniProgramIdProvider) getConfig(clientId string, clientSecret string) *oauth2.Config {
var config = &oauth2.Config{
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
}
@@ -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
}
@@ -78,5 +78,4 @@ func (idp *WeChatMiniProgramIdProvider) GetSessionByCode(code string) (*WeChatMi
return nil, fmt.Errorf("err: %s", session.Errmsg)
}
return &session, nil
}

View File

@@ -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
@@ -44,7 +45,7 @@ func (idp *WeComInternalIdProvider) SetHttpClient(client *http.Client) {
}
func (idp *WeComInternalIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var config = &oauth2.Config{
config := &oauth2.Config{
ClientID: clientId,
ClientSecret: clientSecret,
RedirectURL: redirectUrl,
@@ -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
}

View File

@@ -18,7 +18,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
"time"
@@ -46,11 +45,11 @@ func (idp *WeComIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *WeComIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://graph.qq.com/oauth2.0/token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{"snsapi_login"},
Endpoint: endpoint,
ClientID: clientId,
@@ -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
}

View File

@@ -19,7 +19,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
@@ -48,11 +47,11 @@ func (idp *WeiBoIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *WeiBoIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{
endpoint := oauth2.Endpoint{
TokenURL: "https://api.weibo.com/oauth2/access_token",
}
var config = &oauth2.Config{
config := &oauth2.Config{
Scopes: []string{""},
Endpoint: endpoint,
ClientID: clientId,
@@ -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
}

View File

@@ -21,9 +21,10 @@ import (
"github.com/astaxie/beego"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
//_ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/go-sql-driver/mysql" // db = mysql
//_ "github.com/lib/pq" // db = postgres
_ "github.com/lib/pq" // db = postgres
//_ "github.com/mattn/go-sqlite3" // db = sqlite3
"xorm.io/core"
"xorm.io/xorm"
)

View File

@@ -317,7 +317,7 @@ func (application *Application) GetId() string {
}
func CheckRedirectUriValid(application *Application, redirectUri string) bool {
var validUri = false
validUri := false
for _, tmpUri := range application.RedirectUris {
if strings.Contains(redirectUri, tmpUri) {
validUri = true

View File

@@ -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)
}
@@ -197,16 +246,20 @@ func filterField(field string) bool {
return reFieldWhiteList.MatchString(field)
}
func CheckUserPermission(requestUserId, userId string, strict bool) (bool, error) {
func CheckUserPermission(requestUserId, userId, userOwner string, strict bool) (bool, error) {
if requestUserId == "" {
return false, fmt.Errorf("please login first")
}
if userId != "" {
targetUser := GetUser(userId)
if targetUser == nil {
return false, fmt.Errorf("the user: %s doesn't exist", userId)
}
userOwner = targetUser.Owner
}
hasPermission := false
if strings.HasPrefix(requestUserId, "app/") {
hasPermission = true
@@ -219,7 +272,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool) (bool, error
hasPermission = true
} else if requestUserId == userId {
hasPermission = true
} else if targetUser.Owner == requestUser.Owner {
} else if userOwner == requestUser.Owner {
if strict {
hasPermission = requestUser.IsAdmin
} else {

View File

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

View File

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

View File

@@ -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"},
@@ -71,6 +75,8 @@ func initBuiltInOrganization() bool {
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
@@ -98,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{},
@@ -128,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",
@@ -156,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 "", ""
}

View File

@@ -89,6 +89,8 @@ func initDefinedOrganization(organization *Organization) {
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},

View File

@@ -208,11 +208,15 @@ func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*
func (l *ldapConn) GetLdapUsers(baseDn string) ([]ldapUser, error) {
SearchFilter := "(objectClass=posixAccount)"
SearchAttributes := []string{"uidNumber", "uid", "cn", "gidNumber", "entryUUID", "mail", "email",
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress"}
SearchAttributes := []string{
"uidNumber", "uid", "cn", "gidNumber", "entryUUID", "mail", "email",
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress",
}
SearchFilterMsAD := "(objectClass=user)"
SearchAttributesMsAD := []string{"uidNumber", "sAMAccountName", "cn", "gidNumber", "entryUUID", "mail", "email",
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress"}
SearchAttributesMsAD := []string{
"uidNumber", "sAMAccountName", "cn", "gidNumber", "entryUUID", "mail", "email",
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress",
}
var searchReq *goldap.SearchRequest
if l.IsAD {
searchReq = goldap.NewSearchRequest(baseDn,
@@ -459,7 +463,7 @@ func CheckLdapUuidExist(owner string, uuids []string) []string {
}
}
for uuid, _ := range existUuidSet {
for uuid := range existUuidSet {
existUuids = append(existUuids, uuid)
}
return existUuids

View File

@@ -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()
@@ -93,9 +94,9 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
logs.Info(fmt.Sprintf("ldap autosync success, %d new users, %d existing users", len(users)-len(*existed), len(*existed)))
}
}
}
// LdapAutoSynchronizerStartUpAll
// start all autosync goroutine for existing ldap servers in each organizations
func (l *LdapAutoSynchronizer) LdapAutoSynchronizerStartUpAll() {
organizations := []*Organization{}

View File

@@ -15,6 +15,8 @@
package object
import (
"fmt"
"github.com/casdoor/casdoor/cred"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
@@ -186,3 +188,31 @@ func DeleteOrganization(organization *Organization) bool {
func GetOrganizationByUser(user *User) *Organization {
return getOrganization("admin", user.Owner)
}
func GetAccountItemByName(name string, organization *Organization) *AccountItem {
if organization == nil {
return nil
}
for _, accountItem := range organization.AccountItems {
if accountItem.Name == name {
return accountItem
}
}
return nil
}
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User) (bool, string) {
switch accountItem.ModifyRule {
case "Admin":
if !(user.IsAdmin || user.IsGlobalAdmin) {
return false, fmt.Sprintf("Only admin can modify the %s.", accountItem.Name)
}
case "Immutable":
return false, fmt.Sprintf("The %s is immutable.", accountItem.Name)
case "Self":
break
default:
return false, fmt.Sprintf("Unknown modify rule %s.", accountItem.ModifyRule)
}
return true, ""
}

View File

@@ -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"
)
@@ -34,24 +29,31 @@ type Permission struct {
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"`
Adapter string `xorm:"varchar(100)" json:"adapter"`
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"`
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"`
Id string `xorm:"varchar(100) index not null default ''" json:"id"`
}
func GetPermissionCount(owner, field, value string) int {
@@ -121,7 +123,18 @@ func UpdatePermission(id string, permission *Permission) bool {
}
if affected != 0 {
if oldPermission.Adapter != "" {
removePolicies(oldPermission)
if oldPermission.Adapter != permission.Adapter {
isEmpty, _ := adapter.Engine.IsTableEmpty(oldPermission.Adapter)
if isEmpty {
err = adapter.Engine.DropTables(oldPermission.Adapter)
if err != nil {
panic(err)
}
}
}
}
addPolicies(permission)
}
@@ -134,10 +147,6 @@ func AddPermission(permission *Permission) bool {
panic(err)
}
if affected != 0 {
addPolicies(permission)
}
return affected != 0
}
@@ -147,8 +156,17 @@ func DeletePermission(permission *Permission) bool {
panic(err)
}
if affected != 0 {
if affected != 0 && permission.Adapter != "" {
removePolicies(permission)
if permission.Adapter != "permission_rule" {
isEmpty, _ := adapter.Engine.IsTableEmpty(permission.Adapter)
if isEmpty {
err = adapter.Engine.DropTables(permission.Adapter)
if err != nil {
panic(err)
}
}
}
}
return affected != 0
@@ -158,74 +176,32 @@ 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)
func GetPermissionsByUser(userId string) []*Permission {
permissions := []*Permission{}
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions)
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
return permissions
}
m, err := model.NewModelFromString(modelText)
func GetPermissionsByRole(roleId string) []*Permission {
permissions := []*Permission{}
err := adapter.Engine.Where("roles like ?", "%"+roleId+"%").Find(&permissions)
if err != nil {
panic(err)
}
enforcer, err := casbin.NewEnforcer(m, adapter)
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)
}
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)
}
return permissions
}

View File

@@ -0,0 +1,212 @@
// 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 {
tableName := "permission_rule"
if len(permission.Adapter) != 0 {
tableName = permission.Adapter
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
adapter, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetBeegoConfDataSourceName()+conf.GetConfigString("dbName"), tableName, tableNamePrefix, true)
if err != nil {
panic(err)
}
modelText := `
[request_definition]
r = sub, obj, act
[policy_definition]
p = 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, [][]string) {
var policies [][]string
var groupingPolicies [][]string
domainExist := len(permission.Domains) > 0
for _, user := range permission.Users {
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
if domainExist {
for _, domain := range permission.Domains {
policies = append(policies, []string{user, domain, resource, strings.ToLower(action)})
}
} else {
policies = append(policies, []string{user, resource, strings.ToLower(action)})
}
}
}
}
for _, role := range permission.Roles {
roleObj := GetRole(role)
for _, subUser := range roleObj.Users {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subUser, domain, role})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subUser, role})
}
}
for _, subRole := range roleObj.Roles {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subRole, domain, role})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subRole, role})
}
}
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
if domainExist {
for _, domain := range permission.Domains {
policies = append(policies, []string{role, domain, resource, strings.ToLower(action)})
}
} else {
policies = append(policies, []string{role, resource, strings.ToLower(action)})
}
}
}
}
return policies, groupingPolicies
}
func addPolicies(permission *Permission) {
enforcer := getEnforcer(permission)
policies, groupingPolicies := getPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
_, err := enforcer.AddPolicies(policies)
if err != nil {
panic(err)
}
}
func removePolicies(permission *Permission) {
enforcer := getEnforcer(permission)
policies, groupingPolicies := getPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.RemoveGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
_, err := enforcer.RemovePolicies(policies)
if err != nil {
panic(err)
}
}
func Enforce(userId string, permissionRule *PermissionRule) bool {
permission := GetPermission(permissionRule.Id)
enforcer := getEnforcer(permission)
allow, err := enforcer.Enforce(userId, permissionRule.V1, permissionRule.V2)
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.V1, permissionRule.V2})
}
permission := GetPermission(permissionRules[0].Id)
enforcer := getEnforcer(permission)
allow, err := enforcer.BatchEnforce(requests)
if err != nil {
panic(err)
}
return allow
}
func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) []string {
permissions := GetPermissionsByUser(userId)
for _, role := range GetAllRoles(userId) {
permissions = append(permissions, GetPermissionsByRole(role)...)
}
var values []string
for _, permission := range permissions {
enforcer := getEnforcer(permission)
values = append(values, fn(enforcer)...)
}
return values
}
func GetAllObjects(userId string) []string {
return getAllValues(userId, func(enforcer *casbin.Enforcer) []string {
return enforcer.GetAllObjects()
})
}
func GetAllActions(userId string) []string {
return getAllValues(userId, func(enforcer *casbin.Enforcer) []string {
return enforcer.GetAllActions()
})
}
func GetAllRoles(userId string) []string {
roles := GetRolesByUser(userId)
var res []string
for _, role := range roles {
res = append(res, role.Name)
}
return res
}

View File

@@ -38,6 +38,8 @@ type Product struct {
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
State string `xorm:"varchar(100)" json:"state"`
ProviderObjs []*Provider `xorm:"-" json:"providerObjs"`
}
func GetProductCount(owner, field, value string) int {
@@ -209,3 +211,14 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
return payUrl, err
}
func ExtendProductWithProviders(product *Product) {
product.ProviderObjs = []*Provider{}
m := getProviderMap(product.Owner)
for _, providerItem := range product.Providers {
if provider, ok := m[providerItem]; ok {
product.ProviderObjs = append(product.ProviderObjs, provider)
}
}
}

View File

@@ -45,6 +45,7 @@ type Provider struct {
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"`

View File

@@ -33,6 +33,15 @@ func (application *Application) GetProviderItem(providerName string) *ProviderIt
return nil
}
func (application *Application) GetProviderItemByType(providerType string) *ProviderItem {
for _, item := range application.Providers {
if item.Provider.Type == providerType {
return item
}
}
return nil
}
func (pi *ProviderItem) IsProviderVisible() bool {
if pi.Provider == nil {
return false

View File

@@ -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
}
@@ -121,3 +123,13 @@ func DeleteRole(role *Role) bool {
func (role *Role) GetId() string {
return fmt.Sprintf("%s/%s", role.Owner, role.Name)
}
func GetRolesByUser(userId string) []*Role {
roles := []*Role{}
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&roles)
if err != nil {
panic(err)
}
return roles
}

View File

@@ -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{
@@ -100,7 +101,6 @@ func NewSamlResponse(user *User, host string, certificate string, destination st
displayName.CreateElement("saml:AttributeValue").CreateAttr("xsi:type", "xs:string").Element().SetText(user.DisplayName)
return samlResponse, nil
}
type X509Key struct {
@@ -114,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"`

View File

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

View File

@@ -22,7 +22,7 @@ import (
func (syncer *Syncer) syncUsers() {
fmt.Printf("Running syncUsers()..\n")
users, userMap := syncer.getUserMap()
users, userMap, userNameMap := syncer.getUserMap()
oUsers, oUserMap, err := syncer.getOriginalUserMap()
if err != nil {
fmt.Printf(err.Error())
@@ -44,9 +44,11 @@ func (syncer *Syncer) syncUsers() {
for _, oUser := range oUsers {
id := oUser.Id
if _, ok := userMap[id]; !ok {
if _, ok := userNameMap[oUser.Name]; !ok {
newUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
fmt.Printf("New user: %v\n", newUser)
newUsers = append(newUsers, newUser)
}
} else {
user := userMap[id]
oHash := syncer.calculateHash(oUser)

View File

@@ -19,12 +19,15 @@ func (syncer *Syncer) getUsers() []*User {
return users
}
func (syncer *Syncer) getUserMap() ([]*User, map[string]*User) {
func (syncer *Syncer) getUserMap() ([]*User, map[string]*User, map[string]*User) {
users := syncer.getUsers()
m := map[string]*User{}
m1 := map[string]*User{}
m2 := map[string]*User{}
for _, user := range users {
m[user.Id] = user
m1[user.Id] = user
m2[user.Name] = user
}
return users, m
return users, m1, m2
}

View File

@@ -28,13 +28,13 @@ 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"
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,

View File

@@ -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
@@ -262,7 +265,6 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
return "", "", fmt.Errorf("err: %s", err.Error())
}
return xmlStr, service, nil
}
func (c *CasAuthenticationSuccess) DeepCopy() CasAuthenticationSuccess {
@@ -307,7 +309,6 @@ func (c *CasAttributes) DeepCopy() CasAttributes {
res.ExtraAttributes[i] = &tmp
}
return res
}
func (c *CasUserAttributes) DeepCopy() CasUserAttributes {
@@ -316,11 +317,11 @@ func (c *CasUserAttributes) DeepCopy() CasUserAttributes {
Attributes: make([]*CasNamedAttribute, len(c.Attributes)),
}
for i, a := range c.AnyAttributes {
var tmp = *a
tmp := *a
res.AnyAttributes[i] = &tmp
}
for i, a := range c.Attributes {
var tmp = *a
tmp := *a
res.Attributes[i] = &tmp
}
return res

View File

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

View File

@@ -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,11 +78,10 @@ type User struct {
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"`
Github string `xorm:"varchar(100)" json:"github"`
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"`
@@ -104,6 +108,12 @@ type User struct {
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
Properties map[string]string `json:"properties"`
Roles []*Role `json:"roles"`
Permissions []*Permission `json:"permissions"`
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"`
}
type Userinfo struct {
@@ -240,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)
}
@@ -270,6 +280,24 @@ func GetUserByEmail(owner string, email string) *User {
}
}
func GetUserByPhone(owner string, phone string) *User {
if owner == "" || phone == "" {
return nil
}
user := User{Owner: owner, Phone: phone}
existed, err := adapter.Engine.Get(&user)
if err != nil {
panic(err)
}
if existed {
return &user
} else {
return nil
}
}
func GetUserByUserId(owner string, userId string) *User {
if owner == "" || userId == "" {
return nil
@@ -351,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 {
@@ -396,6 +425,10 @@ func AddUser(user *User) bool {
}
organization := GetOrganizationByUser(user)
if organization == nil {
return false
}
user.UpdateUserPassword(organization)
user.UpdateUserHash()

View File

@@ -37,11 +37,11 @@ func TestSyncAvatarsFromGitHub(t *testing.T) {
users := GetGlobalUsers()
for _, user := range users {
if user.Github == "" {
if user.GitHub == "" {
continue
}
user.Avatar = fmt.Sprintf("https://avatars.githubusercontent.com/%s", user.Github)
user.Avatar = fmt.Sprintf("https://avatars.githubusercontent.com/%s", user.GitHub)
updateUserColumn("avatar", user)
}
}

View File

@@ -106,6 +106,10 @@ func setUserProperty(user *User, field string, value string) {
if value == "" {
delete(user.Properties, field)
} else {
if user.Properties == nil {
user.Properties = make(map[string]string)
}
user.Properties[field] = value
}
}

View File

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

View File

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

View File

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

View File

@@ -25,8 +25,10 @@ import (
"golang.org/x/net/proxy"
)
var DefaultHttpClient *http.Client
var ProxyHttpClient *http.Client
var (
DefaultHttpClient *http.Client
ProxyHttpClient *http.Client
)
func InitHttpClient() {
// not use proxy

View File

@@ -71,5 +71,4 @@ func AutoSigninFilter(ctx *context.Context) {
setSessionUser(ctx, userId)
return
}
}

View File

@@ -30,8 +30,7 @@ func init() {
}
func initAPI() {
ns :=
beego.NewNamespace("/",
ns := beego.NewNamespace("/",
beego.NSNamespace("/api",
beego.NSInclude(
&controllers.ApiController{},
@@ -79,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")
@@ -116,6 +122,7 @@ func initAPI() {
beego.Router("/api/get-applications", &controllers.ApiController{}, "GET:GetApplications")
beego.Router("/api/get-application", &controllers.ApiController{}, "GET:GetApplication")
beego.Router("/api/get-user-application", &controllers.ApiController{}, "GET:GetUserApplication")
beego.Router("/api/get-organization-applications", &controllers.ApiController{}, "GET:GetOrganizationApplications")
beego.Router("/api/update-application", &controllers.ApiController{}, "POST:UpdateApplication")
beego.Router("/api/add-application", &controllers.ApiController{}, "POST:AddApplication")
beego.Router("/api/delete-application", &controllers.ApiController{}, "POST:DeleteApplication")
@@ -195,5 +202,4 @@ func initAPI() {
beego.Router("/api/webauthn/signup/finish", &controllers.ApiController{}, "Post:WebAuthnSignupFinish")
beego.Router("/api/webauthn/signin/begin", &controllers.ApiController{}, "Get:WebAuthnSigninBegin")
beego.Router("/api/webauthn/signin/finish", &controllers.ApiController{}, "Post:WebAuthnSigninFinish")
}

View File

@@ -1291,6 +1291,35 @@
}
}
},
"/api/get-organization-applications": {
"get": {
"tags": [
"Application API"
],
"description": "get the detail of the organization's application",
"operationId": "ApiController.GetOrganizationApplications",
"parameters": [
{
"in": "query",
"name": "organization",
"description": "The organization name",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Application"
}
}
}
}
}
},
"/api/get-organizations": {
"get": {
"tags": [
@@ -1853,6 +1882,24 @@
"description": "The id of the user",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "owner",
"description": "The owner of the user",
"type": "string"
},
{
"in": "query",
"name": "email",
"description": "The email of the user",
"type": "string"
},
{
"in": "query",
"name": "phone",
"description": "The phone of the user",
"type": "string"
}
],
"responses": {
@@ -3220,11 +3267,11 @@
}
},
"definitions": {
"2127.0xc000427560.false": {
"2200.0xc0003f8480.false": {
"title": "false",
"type": "object"
},
"2161.0xc000427590.false": {
"2235.0xc0003f84b0.false": {
"title": "false",
"type": "object"
},
@@ -3342,10 +3389,10 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/2127.0xc000427560.false"
"$ref": "#/definitions/2200.0xc0003f8480.false"
},
"data2": {
"$ref": "#/definitions/2161.0xc000427590.false"
"$ref": "#/definitions/2235.0xc0003f84b0.false"
},
"msg": {
"type": "string"
@@ -3549,6 +3596,9 @@
"type": "integer",
"format": "int64"
},
"certificate": {
"type": "string"
},
"createdTime": {
"type": "string"
},
@@ -3571,9 +3621,6 @@
"privateKey": {
"type": "string"
},
"certificate": {
"type": "string"
},
"scope": {
"type": "string"
},
@@ -4585,6 +4632,12 @@
"permanentAvatar": {
"type": "string"
},
"permissions": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Permission"
}
},
"phone": {
"type": "string"
},
@@ -4606,6 +4659,12 @@
"region": {
"type": "string"
},
"roles": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Role"
}
},
"score": {
"type": "integer",
"format": "int64"

View File

@@ -837,6 +837,25 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/object.Organization'
/api/get-organization-applications:
get:
tags:
- Application API
description: get the detail of the organization's application
operationId: ApiController.GetOrganizationApplications
parameters:
- in: query
name: organization
description: The organization name
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Application'
/api/get-organizations:
get:
tags:
@@ -1209,6 +1228,18 @@ paths:
description: The id of the user
required: true
type: string
- in: query
name: owner
description: The owner of the user
type: string
- in: query
name: email
description: The email of the user
type: string
- in: query
name: phone
description: The phone of the user
type: string
responses:
"200":
description: The Response object
@@ -2108,10 +2139,10 @@ paths:
schema:
$ref: '#/definitions/Response'
definitions:
2127.0xc000427560.false:
2200.0xc0003f8480.false:
title: "false"
type: object
2161.0xc000427590.false:
2235.0xc0003f84b0.false:
title: "false"
type: object
Response:
@@ -2192,9 +2223,9 @@ definitions:
type: object
properties:
data:
$ref: '#/definitions/2127.0xc000427560.false'
$ref: '#/definitions/2200.0xc0003f8480.false'
data2:
$ref: '#/definitions/2161.0xc000427590.false'
$ref: '#/definitions/2235.0xc0003f84b0.false'
msg:
type: string
name:
@@ -2331,6 +2362,8 @@ definitions:
bitSize:
type: integer
format: int64
certificate:
type: string
createdTime:
type: string
cryptoAlgorithm:
@@ -2346,8 +2379,6 @@ definitions:
type: string
privateKey:
type: string
certificate:
type: string
scope:
type: string
type:
@@ -3027,6 +3058,10 @@ definitions:
type: string
permanentAvatar:
type: string
permissions:
type: array
items:
$ref: '#/definitions/object.Permission'
phone:
type: string
preHash:
@@ -3041,6 +3076,10 @@ definitions:
format: int64
region:
type: string
roles:
type: array
items:
$ref: '#/definitions/object.Role'
score:
type: integer
format: int64

View File

@@ -17,7 +17,9 @@ package util
import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
)
func GetHmacSha1(keyStr, value string) string {
@@ -28,3 +30,10 @@ func GetHmacSha1(keyStr, value string) string {
return res
}
func GetHmacSha256(key string, data string) string {
mac := hmac.New(sha256.New, []byte(key))
mac.Write([]byte(data))
return hex.EncodeToString(mac.Sum(nil))
}

View File

@@ -19,8 +19,10 @@ import (
"regexp"
)
var rePhoneCn *regexp.Regexp
var rePhone *regexp.Regexp
var (
rePhoneCn *regexp.Regexp
rePhone *regexp.Regexp
)
func init() {
// https://learnku.com/articles/31543

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