Compare commits

...

64 Commits

Author SHA1 Message Date
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
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
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
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
c92d34e27c Add GetPermissionsBySubmitter() 2022-08-15 14:09:12 +08:00
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
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
ca13247572 chore(style): use eqeqeq (#1013) 2022-08-13 11:23:16 +08:00
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
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
6676cc8ff3 fix: add JTI name to JWT token (#989)
* feat: add jti to jwt

* fix

* fix
2022-08-11 14:32:47 +08:00
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
53ad454962 feat: responsive footer (#1003) 2022-08-10 20:31:42 +08:00
fb203a6f30 feat: delete .env to fix static file path bug (#999) 2022-08-10 12:22:27 +08:00
f716a0985f Add disableSsl to provider. 2022-08-09 23:38:35 +08:00
340fbe135d Fix error in wrapActionResponse() 2022-08-09 23:34:07 +08:00
79119760f2 style: golint (#988) 2022-08-09 16:50:49 +08:00
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
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
802995ed16 refactor: remove WeChat unionId to properties (#985) 2022-08-08 18:43:12 +08:00
b14554a5ba feat(web): check style when commit (#980)
feat(web): check style when commit
2022-08-08 00:10:31 +08:00
4665ffa759 Update i18n data 2022-08-08 00:02:47 +08:00
f914e8e929 Add permission_enforcer.go 2022-08-07 23:57:06 +08:00
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
ee8dd23a56 fix: fixed footer css (#951) 2022-08-07 17:22:52 +08:00
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
8e5cd18c91 fix: Restrict the request permissions of providers and applications (#970) 2022-08-07 16:05:05 +08:00
32b4d98c2a Add ExtendProductWithProviders(). 2022-08-07 15:45:06 +08:00
2ea58cd639 chore(style): use gofumpt to fmt go code (#967) 2022-08-07 12:26:14 +08:00
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
cba338eef2 Merge pull request #973 from qianxi0410/eslint
feat(web): add some eslint rules
2022-08-07 00:41:51 +08:00
c428de6e42 feat: fix some comma dangle 2022-08-07 00:17:27 +08:00
9bca6bb72e feat: no-multi-spacing 2022-08-07 00:06:20 +08:00
cd966116d4 feat: comma dangle 2022-08-06 23:54:56 +08:00
9abf1b9d73 feat: key spacing 2022-08-06 23:47:28 +08:00
6aaba6debd feat: space between infix op 2022-08-06 23:43:09 +08:00
77565712e0 feat: no-multi-empty-lines 2022-08-06 23:38:03 +08:00
d025259db7 feat: indent 2022-08-06 23:36:20 +08:00
aafdc546fa fix: panic when creating a user in a non-existent org (#969) 2022-08-06 22:30:56 +08:00
539ca2d731 chore(web): add fix command (#964) 2022-08-05 23:40:04 +08:00
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
e94ada9ea2 Fix new accountItem. 2022-08-05 15:36:07 +08:00
4ea482223d feat: add geetest captcha (#953) 2022-08-04 20:55:04 +08:00
d55ae7d1d2 Enable some other DBs 2022-08-04 20:28:09 +08:00
d72e00605f fix: updateProviderField when add provider payment (#952) 2022-08-04 19:39:25 +08:00
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
13404d6035 feat: fix binding after registration causes the page to crash (#945) 2022-08-01 21:08:10 +08:00
afa9c530ad fix: panic triggered when user is nil (#940) 2022-07-31 23:23:36 +08:00
1600615aca Support sqlite3 DB 2022-07-31 18:11:18 +08:00
2bb8491499 fix: unable to get user if profile is private (#936) 2022-07-31 10:54:41 +08:00
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
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
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
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
11695220a8 Use user.GetId() 2022-07-30 17:40:30 +08:00
155660b0d7 feat: get user api return roles and permissions (#929) 2022-07-30 17:31:56 +08:00
1c72f5300c feat: fix 'Enable code sign' is not displayed in the login page (#925) 2022-07-28 23:11:33 +08:00
3dd56195d9 fix: fix the problem of link error (#923) 2022-07-28 21:52:10 +08:00
8865244262 fix: add oauth login auto close page (#915) 2022-07-26 23:03:55 +08:00
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
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 - uses: actions/setup-node@v2
with: with:
node-version: '14.17.0' node-version: '14.17.0'
# cache
- uses: c-hive/gha-yarn-cache@v2
with:
directory: ./web
- run: yarn install && CI=false yarn run build - run: yarn install && CI=false yarn run build
working-directory: ./web working-directory: ./web
@ -53,11 +57,30 @@ jobs:
go build -race -ldflags "-extldflags '-static'" go build -race -ldflags "-extldflags '-static'"
working-directory: ./ 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: release-and-push:
name: Release And Push name: Release And Push
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
needs: [ frontend, backend ] needs: [ frontend, backend, linter ]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@ -31,7 +31,7 @@ run:
- api - api
# skip-files: # skip-files:
# - ".*_test\\.go$" # - ".*_test\\.go$"
modules-download-mode: vendor modules-download-mode: mod
# all available settings of specific linters # all available settings of specific linters
linters-settings: linters-settings:
lll: lll:

View File

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

View File

@ -18,7 +18,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"sort" "sort"
@ -31,8 +31,7 @@ import (
const AliyunCaptchaVerifyUrl = "http://afs.aliyuncs.com" const AliyunCaptchaVerifyUrl = "http://afs.aliyuncs.com"
type AliyunCaptchaProvider struct { type AliyunCaptchaProvider struct{}
}
func NewAliyunCaptchaProvider() *AliyunCaptchaProvider { func NewAliyunCaptchaProvider() *AliyunCaptchaProvider {
captcha := &AliyunCaptchaProvider{} captcha := &AliyunCaptchaProvider{}
@ -81,7 +80,7 @@ func (captcha *AliyunCaptchaProvider) VerifyCaptcha(token, clientSecret string)
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -16,8 +16,7 @@ package captcha
import "github.com/casdoor/casdoor/object" import "github.com/casdoor/casdoor/object"
type DefaultCaptchaProvider struct { type DefaultCaptchaProvider struct{}
}
func NewDefaultCaptchaProvider() *DefaultCaptchaProvider { func NewDefaultCaptchaProvider() *DefaultCaptchaProvider {
captcha := &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 ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -25,8 +25,7 @@ import (
const HCaptchaVerifyUrl = "https://hcaptcha.com/siteverify" const HCaptchaVerifyUrl = "https://hcaptcha.com/siteverify"
type HCaptchaProvider struct { type HCaptchaProvider struct{}
}
func NewHCaptchaProvider() *HCaptchaProvider { func NewHCaptchaProvider() *HCaptchaProvider {
captcha := &HCaptchaProvider{} captcha := &HCaptchaProvider{}
@ -44,7 +43,7 @@ func (captcha *HCaptchaProvider) VerifyCaptcha(token, clientSecret string) (bool
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

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

View File

@ -17,7 +17,7 @@ package captcha
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -25,8 +25,7 @@ import (
const ReCaptchaVerifyUrl = "https://recaptcha.net/recaptcha/api/siteverify" const ReCaptchaVerifyUrl = "https://recaptcha.net/recaptcha/api/siteverify"
type ReCaptchaProvider struct { type ReCaptchaProvider struct{}
}
func NewReCaptchaProvider() *ReCaptchaProvider { func NewReCaptchaProvider() *ReCaptchaProvider {
captcha := &ReCaptchaProvider{} captcha := &ReCaptchaProvider{}
@ -44,7 +43,7 @@ func (captcha *ReCaptchaProvider) VerifyCaptcha(token, clientSecret string) (boo
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

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

View File

@ -49,7 +49,7 @@ func GetConfigInt64(key string) (int64, error) {
func init() { func init() {
// this array contains the beego configuration items that may be modified via env // 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 { for _, key := range presetConfigItems {
if value, ok := os.LookupEnv(key); ok { if value, ok := os.LookupEnv(key); ok {
beego.AppConfig.Set(key, value) beego.AppConfig.Set(key, value)

View File

@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package conf package conf
import ( import (

View File

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

View File

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

View File

@ -23,11 +23,13 @@ import (
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
// ApiController
// controller for handlers under /api uri // controller for handlers under /api uri
type ApiController struct { type ApiController struct {
beego.Controller beego.Controller
} }
// RootController
// controller for handlers directly under / (root) // controller for handlers directly under / (root)
type RootController struct { type RootController struct {
ApiController ApiController

View File

@ -31,7 +31,7 @@ const (
InvalidProxyCallback string = "INVALID_PROXY_CALLBACK" InvalidProxyCallback string = "INVALID_PROXY_CALLBACK"
InvalidTicket string = "INVALID_TICKET" InvalidTicket string = "INVALID_TICKET"
InvalidService string = "INVALID_SERVICE" InvalidService string = "INVALID_SERVICE"
InteralError string = "INTERNAL_ERROR" InternalError string = "INTERNAL_ERROR"
UnauthorizedService string = "UNAUTHORIZED_SERVICE" 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))) c.Ctx.Output.Body([]byte(fmt.Sprintf("yes\n%s\n", response.User)))
return return
} }
} }
// token not found // token not found
c.Ctx.Output.Body([]byte("no\n")) c.Ctx.Output.Body([]byte("no\n"))
@ -117,7 +116,7 @@ func (c *RootController) CasP3ServiceAndProxyValidate() {
} }
// make a request to pgturl passing pgt and pgtiou // make a request to pgturl passing pgt and pgtiou
if err != nil { if err != nil {
c.sendCasAuthenticationResponseErr(InteralError, err.Error(), format) c.sendCasAuthenticationResponseErr(InternalError, err.Error(), format)
return return
} }
param := pgtUrlObj.Query() param := pgtUrlObj.Query()
@ -127,7 +126,7 @@ func (c *RootController) CasP3ServiceAndProxyValidate() {
request, err := http.NewRequest("GET", pgtUrlObj.String(), nil) request, err := http.NewRequest("GET", pgtUrlObj.String(), nil)
if err != nil { if err != nil {
c.sendCasAuthenticationResponseErr(InteralError, err.Error(), format) c.sendCasAuthenticationResponseErr(InternalError, err.Error(), format)
return return
} }
@ -184,7 +183,6 @@ func (c *RootController) CasProxy() {
c.Data["xml"] = serviceResponse c.Data["xml"] = serviceResponse
c.ServeXML() c.ServeXML()
} }
} }
func (c *RootController) SamlValidate() { func (c *RootController) SamlValidate() {
@ -216,7 +214,7 @@ func (c *RootController) SamlValidate() {
return return
} }
envelopReponse := struct { envelopResponse := struct {
XMLName xml.Name `xml:"SOAP-ENV:Envelope"` XMLName xml.Name `xml:"SOAP-ENV:Envelope"`
Xmlns string `xml:"xmlns:SOAP-ENV"` Xmlns string `xml:"xmlns:SOAP-ENV"`
Body struct { Body struct {
@ -224,15 +222,15 @@ func (c *RootController) SamlValidate() {
Content string `xml:",innerxml"` Content string `xml:",innerxml"`
} }
}{} }{}
envelopReponse.Xmlns = "http://schemas.xmlsoap.org/soap/envelope/" envelopResponse.Xmlns = "http://schemas.xmlsoap.org/soap/envelope/"
envelopReponse.Body.Content = response envelopResponse.Body.Content = response
data, err := xml.Marshal(envelopReponse) data, err := xml.Marshal(envelopResponse)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
c.Ctx.Output.Body([]byte(data)) c.Ctx.Output.Body(data)
} }
func (c *RootController) sendCasProxyResponseErr(code, msg, format string) { func (c *RootController) sendCasProxyResponseErr(code, msg, format string) {

View File

@ -48,6 +48,7 @@ func (c *ApiController) GetCerts() {
} }
} }
// GetCert
// @Title GetCert // @Title GetCert
// @Tag Cert API // @Tag Cert API
// @Description get cert // @Description get cert
@ -61,6 +62,7 @@ func (c *ApiController) GetCert() {
c.ServeJSON() c.ServeJSON()
} }
// UpdateCert
// @Title UpdateCert // @Title UpdateCert
// @Tag Cert API // @Tag Cert API
// @Description update cert // @Description update cert
@ -81,6 +83,7 @@ func (c *ApiController) UpdateCert() {
c.ServeJSON() c.ServeJSON()
} }
// AddCert
// @Title AddCert // @Title AddCert
// @Tag Cert API // @Tag Cert API
// @Description add cert // @Description add cert
@ -98,6 +101,7 @@ func (c *ApiController) AddCert() {
c.ServeJSON() c.ServeJSON()
} }
// DeleteCert
// @Title DeleteCert // @Title DeleteCert
// @Tag Cert API // @Tag Cert API
// @Description delete cert // @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"` Failed []object.LdapRespUser `json:"failed"`
} }
// GetLdapUser
// @Tag Account API // @Tag Account API
// @Title GetLdapser // @Title GetLdapser
// @router /get-ldap-user [post] // @router /get-ldap-user [post]
@ -100,6 +101,7 @@ func (c *ApiController) GetLdapUser() {
c.ServeJSON() c.ServeJSON()
} }
// GetLdaps
// @Tag Account API // @Tag Account API
// @Title GetLdaps // @Title GetLdaps
// @router /get-ldaps [post] // @router /get-ldaps [post]
@ -110,6 +112,7 @@ func (c *ApiController) GetLdaps() {
c.ServeJSON() c.ServeJSON()
} }
// GetLdap
// @Tag Account API // @Tag Account API
// @Title GetLdap // @Title GetLdap
// @router /get-ldap [post] // @router /get-ldap [post]
@ -125,6 +128,7 @@ func (c *ApiController) GetLdap() {
c.ServeJSON() c.ServeJSON()
} }
// AddLdap
// @Tag Account API // @Tag Account API
// @Title AddLdap // @Title AddLdap
// @router /add-ldap [post] // @router /add-ldap [post]
@ -159,6 +163,7 @@ func (c *ApiController) AddLdap() {
c.ServeJSON() c.ServeJSON()
} }
// UpdateLdap
// @Tag Account API // @Tag Account API
// @Title UpdateLdap // @Title UpdateLdap
// @router /update-ldap [post] // @router /update-ldap [post]
@ -186,6 +191,7 @@ func (c *ApiController) UpdateLdap() {
c.ServeJSON() c.ServeJSON()
} }
// DeleteLdap
// @Tag Account API // @Tag Account API
// @Title DeleteLdap // @Title DeleteLdap
// @router /delete-ldap [post] // @router /delete-ldap [post]
@ -201,6 +207,7 @@ func (c *ApiController) DeleteLdap() {
c.ServeJSON() c.ServeJSON()
} }
// SyncLdapUsers
// @Tag Account API // @Tag Account API
// @Title SyncLdapUsers // @Title SyncLdapUsers
// @router /sync-ldap-users [post] // @router /sync-ldap-users [post]
@ -223,6 +230,7 @@ func (c *ApiController) SyncLdapUsers() {
c.ServeJSON() c.ServeJSON()
} }
// CheckLdapUsersExist
// @Tag Account API // @Tag Account API
// @Title CheckLdapUserExist // @Title CheckLdapUserExist
// @router /check-ldap-users-exist [post] // @router /check-ldap-users-exist [post]

View File

@ -22,6 +22,7 @@ import (
type LinkForm struct { type LinkForm struct {
ProviderType string `json:"providerType"` ProviderType string `json:"providerType"`
User object.User `json:"user"`
} }
// Unlink ... // Unlink ...
@ -40,16 +41,55 @@ func (c *ApiController) Unlink() {
} }
providerType := form.ProviderType providerType := form.ProviderType
// the user will be unlinked from the provider
unlinkedUser := form.User
user := object.GetUser(userId) 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 == "" { if value == "" {
c.ResponseError("Please link first", value) c.ResponseError("Please link first", value)
return return
} }
object.ClearUserOAuthProperties(user, providerType) object.ClearUserOAuthProperties(&unlinkedUser, providerType)
object.LinkUserAccount(user, providerType, "") object.LinkUserAccount(&unlinkedUser, providerType, "")
c.ResponseOk() c.ResponseOk()
} }

View File

@ -16,6 +16,7 @@ package controllers
import "github.com/casdoor/casdoor/object" import "github.com/casdoor/casdoor/object"
// GetOidcDiscovery
// @Title GetOidcDiscovery // @Title GetOidcDiscovery
// @Tag OIDC API // @Tag OIDC API
// @Description Get Oidc Discovery // @Description Get Oidc Discovery
@ -27,6 +28,7 @@ func (c *RootController) GetOidcDiscovery() {
c.ServeJSON() c.ServeJSON()
} }
// GetJwks
// @Title GetJwks // @Title GetJwks
// @Tag OIDC API // @Tag OIDC API
// @Success 200 {object} jose.JSONWebKey // @Success 200 {object} jose.JSONWebKey

View File

@ -67,6 +67,7 @@ func (c *ApiController) GetUserPayments() {
c.ResponseOk(payments) c.ResponseOk(payments)
} }
// GetPayment
// @Title GetPayment // @Title GetPayment
// @Tag Payment API // @Tag Payment API
// @Description get payment // @Description get payment
@ -80,6 +81,7 @@ func (c *ApiController) GetPayment() {
c.ServeJSON() c.ServeJSON()
} }
// UpdatePayment
// @Title UpdatePayment // @Title UpdatePayment
// @Tag Payment API // @Tag Payment API
// @Description update payment // @Description update payment
@ -100,6 +102,7 @@ func (c *ApiController) UpdatePayment() {
c.ServeJSON() c.ServeJSON()
} }
// AddPayment
// @Title AddPayment // @Title AddPayment
// @Tag Payment API // @Tag Payment API
// @Description add payment // @Description add payment
@ -117,6 +120,7 @@ func (c *ApiController) AddPayment() {
c.ServeJSON() c.ServeJSON()
} }
// DeletePayment
// @Title DeletePayment // @Title DeletePayment
// @Tag Payment API // @Tag Payment API
// @Description delete payment // @Description delete payment
@ -134,6 +138,7 @@ func (c *ApiController) DeletePayment() {
c.ServeJSON() c.ServeJSON()
} }
// NotifyPayment
// @Title NotifyPayment // @Title NotifyPayment
// @Tag Payment API // @Tag Payment API
// @Description notify payment // @Description notify payment
@ -159,6 +164,7 @@ func (c *ApiController) NotifyPayment() {
} }
} }
// InvoicePayment
// @Title InvoicePayment // @Title InvoicePayment
// @Tag Payment API // @Tag Payment API
// @Description invoice payment // @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 // @Title GetPermission
// @Tag Permission API // @Tag Permission API
// @Description get permission // @Description get permission
@ -61,6 +80,7 @@ func (c *ApiController) GetPermission() {
c.ServeJSON() c.ServeJSON()
} }
// UpdatePermission
// @Title UpdatePermission // @Title UpdatePermission
// @Tag Permission API // @Tag Permission API
// @Description update permission // @Description update permission
@ -81,6 +101,7 @@ func (c *ApiController) UpdatePermission() {
c.ServeJSON() c.ServeJSON()
} }
// AddPermission
// @Title AddPermission // @Title AddPermission
// @Tag Permission API // @Tag Permission API
// @Description add permission // @Description add permission
@ -98,6 +119,7 @@ func (c *ApiController) AddPermission() {
c.ServeJSON() c.ServeJSON()
} }
// DeletePermission
// @Title DeletePermission // @Title DeletePermission
// @Tag Permission API // @Tag Permission API
// @Description delete permission // @Description delete permission

View File

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

View File

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

View File

@ -27,6 +27,7 @@ import (
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
// GetResources
// @router /get-resources [get] // @router /get-resources [get]
// @Tag Resource API // @Tag Resource API
// @Title GetResources // @Title GetResources
@ -50,6 +51,7 @@ func (c *ApiController) GetResources() {
} }
} }
// GetResource
// @Tag Resource API // @Tag Resource API
// @Title GetResource // @Title GetResource
// @router /get-resource [get] // @router /get-resource [get]
@ -60,6 +62,7 @@ func (c *ApiController) GetResource() {
c.ServeJSON() c.ServeJSON()
} }
// UpdateResource
// @Tag Resource API // @Tag Resource API
// @Title UpdateResource // @Title UpdateResource
// @router /update-resource [post] // @router /update-resource [post]
@ -76,6 +79,7 @@ func (c *ApiController) UpdateResource() {
c.ServeJSON() c.ServeJSON()
} }
// AddResource
// @Tag Resource API // @Tag Resource API
// @Title AddResource // @Title AddResource
// @router /add-resource [post] // @router /add-resource [post]
@ -90,6 +94,7 @@ func (c *ApiController) AddResource() {
c.ServeJSON() c.ServeJSON()
} }
// DeleteResource
// @Tag Resource API // @Tag Resource API
// @Title DeleteResource // @Title DeleteResource
// @router /delete-resource [post] // @router /delete-resource [post]
@ -115,6 +120,7 @@ func (c *ApiController) DeleteResource() {
c.ServeJSON() c.ServeJSON()
} }
// UploadResource
// @Tag Resource API // @Tag Resource API
// @Title UploadResource // @Title UploadResource
// @router /upload-resource [post] // @router /upload-resource [post]

View File

@ -48,6 +48,7 @@ func (c *ApiController) GetRoles() {
} }
} }
// GetRole
// @Title GetRole // @Title GetRole
// @Tag Role API // @Tag Role API
// @Description get role // @Description get role
@ -61,6 +62,7 @@ func (c *ApiController) GetRole() {
c.ServeJSON() c.ServeJSON()
} }
// UpdateRole
// @Title UpdateRole // @Title UpdateRole
// @Tag Role API // @Tag Role API
// @Description update role // @Description update role
@ -81,6 +83,7 @@ func (c *ApiController) UpdateRole() {
c.ServeJSON() c.ServeJSON()
} }
// AddRole
// @Title AddRole // @Title AddRole
// @Tag Role API // @Tag Role API
// @Description add role // @Description add role
@ -98,6 +101,7 @@ func (c *ApiController) AddRole() {
c.ServeJSON() c.ServeJSON()
} }
// DeleteRole
// @Title DeleteRole // @Title DeleteRole
// @Tag Role API // @Tag Role API
// @Description delete role // @Description delete role

View File

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

View File

@ -255,7 +255,7 @@ func (c *ApiController) RefreshToken() {
// @router /login/oauth/logout [get] // @router /login/oauth/logout [get]
func (c *ApiController) TokenLogout() { func (c *ApiController) TokenLogout() {
token := c.Input().Get("id_token_hint") 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") redirectUri := c.Input().Get("post_logout_redirect_uri")
state := c.Input().Get("state") state := c.Input().Get("state")
if application != nil && object.CheckRedirectUriValid(application, redirectUri) { if application != nil && object.CheckRedirectUriValid(application, redirectUri) {
@ -273,6 +273,7 @@ func (c *ApiController) TokenLogout() {
// representing the meta information surrounding the // representing the meta information surrounding the
// token, including whether this token is currently active. // token, including whether this token is currently active.
// This endpoint only support Basic Authorization. // This endpoint only support Basic Authorization.
//
// @Param token formData string true "access_token's value or refresh_token's value" // @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" // @Param token_type_hint formData string true "the token type access_token or refresh_token"
// @Success 200 {object} object.IntrospectionResponse The Response object // @Success 200 {object} object.IntrospectionResponse The Response object
@ -288,7 +289,7 @@ func (c *ApiController) IntrospectToken() {
if clientId == "" || clientSecret == "" { if clientId == "" || clientSecret == "" {
c.ResponseError("empty clientId or clientSecret") c.ResponseError("empty clientId or clientSecret")
c.Data["json"] = &object.TokenError{ c.Data["json"] = &object.TokenError{
Error: object.INVALID_REQUEST, Error: object.InvalidRequest,
} }
c.SetTokenErrorHttpStatus() c.SetTokenErrorHttpStatus()
c.ServeJSON() c.ServeJSON()
@ -299,7 +300,7 @@ func (c *ApiController) IntrospectToken() {
if application == nil || application.ClientSecret != clientSecret { if application == nil || application.ClientSecret != clientSecret {
c.ResponseError("invalid application or wrong clientSecret") c.ResponseError("invalid application or wrong clientSecret")
c.Data["json"] = &object.TokenError{ c.Data["json"] = &object.TokenError{
Error: object.INVALID_CLIENT, Error: object.InvalidClient,
} }
c.SetTokenErrorHttpStatus() c.SetTokenErrorHttpStatus()
return return

View File

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

View File

@ -55,7 +55,7 @@ func (c *ApiController) ResponseError(error string, data ...interface{}) {
func (c *ApiController) SetTokenErrorHttpStatus() { func (c *ApiController) SetTokenErrorHttpStatus() {
_, ok := c.Data["json"].(*object.TokenError) _, ok := c.Data["json"].(*object.TokenError)
if ok { 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.SetStatus(401)
c.Ctx.Output.Header("WWW-Authenticate", "Basic realm=\"OAuth2\"") c.Ctx.Output.Header("WWW-Authenticate", "Basic realm=\"OAuth2\"")
} else { } else {

View File

@ -49,8 +49,24 @@ func (c *ApiController) SendVerificationCode() {
applicationId := c.Ctx.Request.Form.Get("applicationId") applicationId := c.Ctx.Request.Form.Get("applicationId")
remoteAddr := util.GetIPFromRequest(c.Ctx.Request) remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
if destType == "" || dest == "" || applicationId == "" || !strings.Contains(applicationId, "/") || checkType == "" { if destType == "" {
c.ResponseError("Missing parameter.") 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 return
} }
@ -82,7 +98,7 @@ func (c *ApiController) SendVerificationCode() {
return return
} }
sendResp := errors.New("Invalid dest type") sendResp := errors.New("invalid dest type")
if user == nil && checkUser != "" && checkUser != "true" { if user == nil && checkUser != "" && checkUser != "true" {
name := application.Organization name := application.Organization
@ -152,13 +168,35 @@ func (c *ApiController) ResetEmailOrPhone() {
} }
checkDest := dest checkDest := dest
if destType == "phone" {
org := object.GetOrganizationByUser(user) 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" phonePrefix := "86"
if org != nil && org.PhonePrefix != "" { if org != nil && org.PhonePrefix != "" {
phonePrefix = org.PhonePrefix phonePrefix = org.PhonePrefix
} }
checkDest = fmt.Sprintf("+%s%s", phonePrefix, dest) 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 { if ret := object.CheckVerificationCode(checkDest, code); len(ret) != 0 {
c.ResponseError(ret) c.ResponseError(ret)

View File

@ -16,7 +16,7 @@ package controllers
import ( import (
"bytes" "bytes"
"io/ioutil" "io"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
@ -24,6 +24,7 @@ import (
"github.com/duo-labs/webauthn/webauthn" "github.com/duo-labs/webauthn/webauthn"
) )
// WebAuthnSignupBegin
// @Title WebAuthnSignupBegin // @Title WebAuthnSignupBegin
// @Tag User API // @Tag User API
// @Description WebAuthn Registration Flow 1st stage // @Description WebAuthn Registration Flow 1st stage
@ -53,6 +54,7 @@ func (c *ApiController) WebAuthnSignupBegin() {
c.ServeJSON() c.ServeJSON()
} }
// WebAuthnSignupFinish
// @Title WebAuthnSignupFinish // @Title WebAuthnSignupFinish
// @Tag User API // @Tag User API
// @Description WebAuthn Registration Flow 2nd stage // @Description WebAuthn Registration Flow 2nd stage
@ -72,7 +74,7 @@ func (c *ApiController) WebAuthnSignupFinish() {
c.ResponseError("Please call WebAuthnSignupBegin first") c.ResponseError("Please call WebAuthnSignupBegin first")
return 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) credential, err := webauthnObj.FinishRegistration(user, sessionData, c.Ctx.Request)
if err != nil { if err != nil {
@ -84,6 +86,7 @@ func (c *ApiController) WebAuthnSignupFinish() {
c.ResponseOk() c.ResponseOk()
} }
// WebAuthnSigninBegin
// @Title WebAuthnSigninBegin // @Title WebAuthnSigninBegin
// @Tag Login API // @Tag Login API
// @Description WebAuthn Login Flow 1st stage // @Description WebAuthn Login Flow 1st stage
@ -110,6 +113,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
c.ServeJSON() c.ServeJSON()
} }
// WebAuthnSigninFinish
// @Title WebAuthnSigninBegin // @Title WebAuthnSigninBegin
// @Tag Login API // @Tag Login API
// @Description WebAuthn Login Flow 2nd stage // @Description WebAuthn Login Flow 2nd stage
@ -124,7 +128,7 @@ func (c *ApiController) WebAuthnSigninFinish() {
c.ResponseError("Please call WebAuthnSigninBegin first") c.ResponseError("Please call WebAuthnSigninBegin first")
return 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) userId := string(sessionData.UserID)
user := object.GetUser(userId) user := object.GetUser(userId)
_, err := webauthnObj.FinishLogin(user, sessionData, c.Ctx.Request) _, err := webauthnObj.FinishLogin(user, sessionData, c.Ctx.Request)

View File

@ -48,6 +48,7 @@ func (c *ApiController) GetWebhooks() {
} }
} }
// GetWebhook
// @Title GetWebhook // @Title GetWebhook
// @Tag Webhook API // @Tag Webhook API
// @Description get webhook // @Description get webhook
@ -61,6 +62,7 @@ func (c *ApiController) GetWebhook() {
c.ServeJSON() c.ServeJSON()
} }
// UpdateWebhook
// @Title UpdateWebhook // @Title UpdateWebhook
// @Tag Webhook API // @Tag Webhook API
// @Description update webhook // @Description update webhook
@ -81,6 +83,7 @@ func (c *ApiController) UpdateWebhook() {
c.ServeJSON() c.ServeJSON()
} }
// AddWebhook
// @Title AddWebhook // @Title AddWebhook
// @Tag Webhook API // @Tag Webhook API
// @Description add webhook // @Description add webhook
@ -98,6 +101,7 @@ func (c *ApiController) AddWebhook() {
c.ServeJSON() c.ServeJSON()
} }
// DeleteWebhook
// @Title DeleteWebhook // @Title DeleteWebhook
// @Tag Webhook API // @Tag Webhook API
// @Description delete webhook // @Description delete webhook

View File

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

View File

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

4
go.mod
View File

@ -10,10 +10,11 @@ require (
github.com/beevik/etree v1.1.0 github.com/beevik/etree v1.1.0
github.com/casbin/casbin/v2 v2.30.1 github.com/casbin/casbin/v2 v2.30.1
github.com/casbin/xorm-adapter/v2 v2.5.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/goth v1.69.0-FIX2
github.com/casdoor/oss v1.2.0 github.com/casdoor/oss v1.2.0
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f 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/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
github.com/go-ldap/ldap/v3 v3.3.0 github.com/go-ldap/ldap/v3 v3.3.0
@ -23,6 +24,7 @@ require (
github.com/google/uuid v1.2.0 github.com/google/uuid v1.2.0
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/lestrrat-go/jwx v0.9.0 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/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/qiangmzsx/string-adapter/v2 v2.1.0 github.com/qiangmzsx/string-adapter/v2 v2.1.0
github.com/robfig/cron/v3 v3.0.1 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/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 h1:BkpIxRHKa0s3bSMx173PpuU7oTs+Zw7XmD0BIta0HGM=
github.com/casbin/xorm-adapter/v2 v2.5.1/go.mod h1:AeH4dBKHC9/zYxzdPVHhPDzF8LYLqjDdb767CWJoV54= 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.3.0 h1:c4bWVcKZhO2L3Xu1oy7aeVkCK6HRJkW/b5K1xU9mV60=
github.com/casdoor/go-sms-sender v0.2.0/go.mod h1:fsZsNnALvFIo+HFcE1U/oCQv4ZT42FdglXKMsEm3WSk= 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 h1:RgfIMkL9kekylgxHHK2ZY8ASAwOGns2HVlaBwLu7Bcs=
github.com/casdoor/goth v1.69.0-FIX2/go.mod h1:Om55nRo8CkeDkPSNBbzXW4G5uI28ZUkSk5S69dPek3s= github.com/casdoor/goth v1.69.0-FIX2/go.mod h1:Om55nRo8CkeDkPSNBbzXW4G5uI28ZUkSk5S69dPek3s=
github.com/casdoor/oss v1.2.0 h1:ozLAE+nnNdFQBWbzH8U9spzaO8h8NrB57lBcdyMUUQ8= 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/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 h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY= 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/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 h1:L63RATZFZuFMXy6ixnKmv3eNAXwYQF6HW1vd4IYsQqQ=
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b/go.mod h1:EYSpSkwoEcryMmQGfhol2IiB3IMN9IIIaNd/wcAQMGQ= 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.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 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= 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-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/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= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=

View File

@ -19,7 +19,7 @@ import (
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"time" "time"
@ -59,12 +59,12 @@ func (idp *AdfsIdProvider) SetHttpClient(client *http.Client) {
} }
func (idp *AdfsIdProvider) getConfig(hostUrl string) *oauth2.Config { func (idp *AdfsIdProvider) getConfig(hostUrl string) *oauth2.Config {
var endpoint = oauth2.Endpoint{ endpoint := oauth2.Endpoint{
AuthURL: fmt.Sprintf("%s/adfs/oauth2/authorize", hostUrl), AuthURL: fmt.Sprintf("%s/adfs/oauth2/authorize", hostUrl),
TokenURL: fmt.Sprintf("%s/adfs/oauth2/token", hostUrl), TokenURL: fmt.Sprintf("%s/adfs/oauth2/token", hostUrl),
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Endpoint: endpoint, Endpoint: endpoint,
} }
@ -77,6 +77,7 @@ type AdfsToken struct {
ErrMsg string `json:"error_description"` 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 // 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) { func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
payload := url.Values{} payload := url.Values{}
@ -88,7 +89,7 @@ func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -109,6 +110,7 @@ func (idp *AdfsIdProvider) GetToken(code string) (*oauth2.Token, error) {
return token, nil return token, nil
} }
// GetUserInfo
// Since the userinfo endpoint of ADFS only returns sub, // Since the userinfo endpoint of ADFS only returns sub,
// the id_token is used to resolve the userinfo // the id_token is used to resolve the userinfo
func (idp *AdfsIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) { 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) tokenSrc := []byte(token.AccessToken)
publicKey, _ := keyset.Keys[0].Materialize() publicKey, _ := keyset.Keys[0].Materialize()
id_token, _ := jwt.Parse(bytes.NewReader(tokenSrc), jwt.WithVerify(jwa.RS256, publicKey)) idToken, _ := jwt.Parse(bytes.NewReader(tokenSrc), jwt.WithVerify(jwa.RS256, publicKey))
sid, _ := id_token.Get("sid") sid, _ := idToken.Get("sid")
upn, _ := id_token.Get("upn") upn, _ := idToken.Get("upn")
name, _ := id_token.Get("unique_name") name, _ := idToken.Get("unique_name")
userinfo := &UserInfo{ userinfo := &UserInfo{
Id: sid.(string), Id: sid.(string),
Username: name.(string), Username: name.(string),

View File

@ -24,7 +24,6 @@ import (
"encoding/json" "encoding/json"
"encoding/pem" "encoding/pem"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"sort" "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 // 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 { 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", AuthURL: "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm",
TokenURL: "https://openapi.alipay.com/gateway.do", TokenURL: "https://openapi.alipay.com/gateway.do",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"", ""}, Scopes: []string{"", ""},
Endpoint: endpoint, Endpoint: endpoint,
ClientID: clientId, ClientID: clientId,
@ -205,8 +204,7 @@ func (idp *AlipayIdProvider) postWithBody(body interface{}, targetUrl string) ([
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -18,7 +18,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"golang.org/x/oauth2" "golang.org/x/oauth2"
@ -46,12 +46,12 @@ func (idp *BaiduIdProvider) SetHttpClient(client *http.Client) {
} }
func (idp *BaiduIdProvider) getConfig() *oauth2.Config { func (idp *BaiduIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{ endpoint := oauth2.Endpoint{
AuthURL: "https://openapi.baidu.com/oauth/2.0/authorize", AuthURL: "https://openapi.baidu.com/oauth/2.0/authorize",
TokenURL: "https://openapi.baidu.com/oauth/2.0/token", TokenURL: "https://openapi.baidu.com/oauth/2.0/token",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"email"}, Scopes: []string{"email"},
Endpoint: endpoint, Endpoint: endpoint,
} }
@ -97,7 +97,7 @@ func (idp *BaiduIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -18,7 +18,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strings" "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 // 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 { 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", TokenURL: "https://api.bilibili.com/x/account-oauth2/v1/token",
AuthURL: "http://member.bilibili.com/arcopen/fn/user/account/info", AuthURL: "http://member.bilibili.com/arcopen/fn/user/account/info",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"", ""}, Scopes: []string{"", ""},
Endpoint: endpoint, Endpoint: endpoint,
ClientID: clientId, ClientID: clientId,
@ -76,6 +75,7 @@ type BilibiliIdProviderTokenResponse struct {
Data BilibiliProviderToken `json:"data"` Data BilibiliProviderToken `json:"data"`
} }
// GetToken
/* /*
{ {
"code": 0, "code": 0,
@ -104,7 +104,6 @@ func (idp *BilibiliIdProvider) GetToken(code string) (*oauth2.Token, error) {
} }
data, err := idp.postWithBody(pTokenParams, idp.Config.Endpoint.TokenURL) data, err := idp.postWithBody(pTokenParams, idp.Config.Endpoint.TokenURL)
if err != nil { if err != nil {
return nil, err 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()) userInfoUrl := fmt.Sprintf("%s?%s", idp.Config.Endpoint.AuthURL, params.Encode())
resp, err := idp.Client.Get(userInfoUrl) resp, err := idp.Client.Get(userInfoUrl)
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -206,7 +204,7 @@ func (idp *BilibiliIdProvider) postWithBody(body interface{}, url string) ([]byt
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

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

View File

@ -18,7 +18,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
_ "net/url" _ "net/url"
_ "time" _ "time"
@ -36,7 +36,7 @@ func NewCustomIdProvider(clientId string, clientSecret string, redirectUrl strin
idp := &CustomIdProvider{} idp := &CustomIdProvider{}
idp.UserInfoUrl = userInfoUrl idp.UserInfoUrl = userInfoUrl
var config = &oauth2.Config{ config := &oauth2.Config{
ClientID: clientId, ClientID: clientId,
ClientSecret: clientSecret, ClientSecret: clientSecret,
RedirectURL: redirectUrl, RedirectURL: redirectUrl,
@ -84,7 +84,7 @@ func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -18,7 +18,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"strings" "strings"
"time" "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 // 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 { 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", AuthURL: "https://api.dingtalk.com/v1.0/contact/users/me",
TokenURL: "https://api.dingtalk.com/v1.0/oauth2/userAccessToken", 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, // DingTalk not allow to set scopes,here it is just a placeholder,
// convenient to use later // convenient to use later
Scopes: []string{"", ""}, Scopes: []string{"", ""},
@ -101,7 +100,7 @@ func (idp *DingTalkIdProvider) GetToken(code string) (*oauth2.Token, error) {
token := &oauth2.Token{ token := &oauth2.Token{
AccessToken: pToken.AccessToken, 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 return token, nil
} }
@ -145,7 +144,7 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -180,7 +179,7 @@ func (idp *DingTalkIdProvider) postWithBody(body interface{}, url string) ([]byt
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -18,7 +18,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"time" "time"
@ -42,12 +42,12 @@ func (idp *DouyinIdProvider) SetHttpClient(client *http.Client) {
} }
func (idp *DouyinIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config { 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", TokenURL: "https://open.douyin.com/oauth/access_token",
AuthURL: "https://open.douyin.com/platform/oauth/connect", AuthURL: "https://open.douyin.com/platform/oauth/connect",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"user_info"}, Scopes: []string{"user_info"},
Endpoint: endpoint, Endpoint: endpoint,
ClientID: clientId, ClientID: clientId,
@ -98,7 +98,7 @@ func (idp *DouyinIdProvider) GetToken(code string) (*oauth2.Token, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -177,7 +177,7 @@ func (idp *DouyinIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
defer resp.Body.Close() defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body) respBody, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err 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 // 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 { 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", TokenURL: "https://graph.facebook.com/oauth/access_token",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"email,public_profile"}, Scopes: []string{"email,public_profile"},
Endpoint: endpoint, Endpoint: endpoint,
ClientID: clientId, ClientID: clientId,
@ -71,6 +71,7 @@ type FacebookCheckToken struct {
Data string `json:"data"` Data string `json:"data"`
} }
// FacebookCheckTokenData
// Get more detail via: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken // Get more detail via: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken
type FacebookCheckTokenData struct { type FacebookCheckTokenData struct {
UserId string `json:"user_id"` UserId string `json:"user_id"`

View File

@ -19,7 +19,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "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 // 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 { 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", TokenURL: "https://gitee.com/oauth/token",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"user_info emails"}, Scopes: []string{"user_info emails"},
Endpoint: endpoint, Endpoint: endpoint,
@ -93,7 +92,7 @@ func (idp *GiteeIdProvider) GetToken(code string) (*oauth2.Token, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
rbs, err := ioutil.ReadAll(resp.Body) rbs, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -18,7 +18,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -49,12 +48,12 @@ func (idp *GithubIdProvider) SetHttpClient(client *http.Client) {
} }
func (idp *GithubIdProvider) getConfig() *oauth2.Config { func (idp *GithubIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{ endpoint := oauth2.Endpoint{
AuthURL: "https://github.com/login/oauth/authorize", AuthURL: "https://github.com/login/oauth/authorize",
TokenURL: "https://github.com/login/oauth/access_token", TokenURL: "https://github.com/login/oauth/access_token",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"user:email", "read:user"}, Scopes: []string{"user:email", "read:user"},
Endpoint: endpoint, Endpoint: endpoint,
} }
@ -93,7 +92,6 @@ func (idp *GithubIdProvider) GetToken(code string) (*oauth2.Token, error) {
} }
return token, nil return token, nil
} }
//{ //{
@ -203,7 +201,7 @@ func (idp *GithubIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -237,7 +235,7 @@ func (idp *GithubIdProvider) postWithBody(body interface{}, url string) ([]byte,
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,7 +17,7 @@ package idp
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "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 // 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 { 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", TokenURL: "https://gitlab.com/oauth/token",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"read_user+profile"}, Scopes: []string{"read_user+profile"},
Endpoint: endpoint, Endpoint: endpoint,
ClientID: clientId, ClientID: clientId,
@ -85,7 +85,7 @@ func (idp *GitlabIdProvider) GetToken(code string) (*oauth2.Token, error) {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -209,7 +209,7 @@ func (idp *GitlabIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -19,7 +19,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"golang.org/x/oauth2" "golang.org/x/oauth2"
@ -47,12 +47,12 @@ func (idp *GoogleIdProvider) SetHttpClient(client *http.Client) {
} }
func (idp *GoogleIdProvider) getConfig() *oauth2.Config { func (idp *GoogleIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{ endpoint := oauth2.Endpoint{
AuthURL: "https://accounts.google.com/o/oauth2/auth", AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/token", TokenURL: "https://accounts.google.com/o/oauth2/token",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"profile", "email"}, Scopes: []string{"profile", "email"},
Endpoint: endpoint, Endpoint: endpoint,
} }
@ -95,7 +95,7 @@ func (idp *GoogleIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -207,6 +207,7 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
return &idp 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 // 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) { func (idp *GothIdProvider) SetHttpClient(client *http.Client) {
idpClient := reflect.ValueOf(idp.Provider).Elem().FieldByName("HTTPClient") idpClient := reflect.ValueOf(idp.Provider).Elem().FieldByName("HTTPClient")

View File

@ -17,7 +17,7 @@ package idp
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"golang.org/x/oauth2" "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 { func (idp *InfoflowInternalIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var config = &oauth2.Config{ config := &oauth2.Config{
ClientID: clientId, ClientID: clientId,
ClientSecret: clientSecret, ClientSecret: clientSecret,
RedirectURL: redirectUrl, RedirectURL: redirectUrl,
@ -58,6 +58,7 @@ type InfoflowInterToken struct {
AccessToken string `json:"access_token"` 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 // 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) { func (idp *InfoflowInternalIdProvider) GetToken(code string) (*oauth2.Token, error) {
pTokenParams := &struct { pTokenParams := &struct {
@ -69,7 +70,7 @@ func (idp *InfoflowInternalIdProvider) GetToken(code string) (*oauth2.Token, err
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -137,6 +138,7 @@ type InfoflowInternalUserInfo struct {
Email string `json:"email"` 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 // 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) { func (idp *InfoflowInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
// Get userid first // Get userid first
@ -147,7 +149,7 @@ func (idp *InfoflowInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserIn
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -165,7 +167,7 @@ func (idp *InfoflowInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserIn
return nil, err return nil, err
} }
data, err = ioutil.ReadAll(resp.Body) data, err = io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -18,7 +18,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@ -47,7 +46,7 @@ func (idp *InfoflowIdProvider) SetHttpClient(client *http.Client) {
} }
func (idp *InfoflowIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config { func (idp *InfoflowIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var config = &oauth2.Config{ config := &oauth2.Config{
ClientID: clientId, ClientID: clientId,
ClientSecret: clientSecret, ClientSecret: clientSecret,
RedirectURL: redirectUrl, RedirectURL: redirectUrl,
@ -63,6 +62,7 @@ type InfoflowToken struct {
ExpiresIn int `json:"expires_in"` ExpiresIn int `json:"expires_in"`
} }
// GetToken
// get more detail via: https://qy.baidu.com/doc/index.html#/third_serverapi/authority // get more detail via: https://qy.baidu.com/doc/index.html#/third_serverapi/authority
func (idp *InfoflowIdProvider) GetToken(code string) (*oauth2.Token, error) { func (idp *InfoflowIdProvider) GetToken(code string) (*oauth2.Token, error) {
pTokenParams := &struct { pTokenParams := &struct {
@ -134,6 +134,7 @@ type InfoflowUserInfo struct {
Email string `json:"email"` 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 // 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) { func (idp *InfoflowIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
// Get userid first // Get userid first
@ -144,7 +145,7 @@ func (idp *InfoflowIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -162,7 +163,7 @@ func (idp *InfoflowIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
return nil, err return nil, err
} }
data, err = ioutil.ReadAll(resp.Body) data, err = io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -197,7 +198,7 @@ func (idp *InfoflowIdProvider) postWithBody(body interface{}, url string) ([]byt
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,7 +17,6 @@ package idp
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"strings" "strings"
"time" "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 // 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 { 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", TokenURL: "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{}, Scopes: []string{},
Endpoint: endpoint, Endpoint: endpoint,
ClientID: clientId, ClientID: clientId,
@ -173,7 +172,7 @@ func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err = ioutil.ReadAll(resp.Body) data, err = io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -204,7 +203,7 @@ func (idp *LarkIdProvider) postWithBody(body interface{}, url string) ([]byte, e
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -18,7 +18,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strings" "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 // 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 { 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", TokenURL: "https://www.linkedIn.com/oauth/v2/accessToken",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"email,public_profile"}, Scopes: []string{"email,public_profile"},
Endpoint: endpoint, Endpoint: endpoint,
ClientID: clientId, ClientID: clientId,
@ -85,7 +84,7 @@ func (idp *LinkedInIdProvider) GetToken(code string) (*oauth2.Token, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
rbs, err := ioutil.ReadAll(resp.Body) rbs, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -323,7 +322,7 @@ func (idp *LinkedInIdProvider) GetUrlRespWithAuthorization(url, token string) ([
} }
}(resp.Body) }(resp.Body)
bs, err := ioutil.ReadAll(resp.Body) bs, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -17,7 +17,7 @@ package idp
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"time" "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 { 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), TokenURL: fmt.Sprintf("%s/v1/token", hostUrl),
AuthURL: fmt.Sprintf("%s/v1/authorize", hostUrl), AuthURL: fmt.Sprintf("%s/v1/authorize", hostUrl),
} }
var config = &oauth2.Config{ config := &oauth2.Config{
// openid is required for authentication requests // openid is required for authentication requests
// get more details via: https://developer.okta.com/docs/reference/api/oidc/#reserved-scopes // get more details via: https://developer.okta.com/docs/reference/api/oidc/#reserved-scopes
Scopes: []string{"openid", "profile", "email"}, Scopes: []string{"openid", "profile", "email"},
@ -114,7 +114,7 @@ func (idp *OktaIdProvider) GetToken(code string) (*oauth2.Token, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -178,7 +178,7 @@ func (idp *OktaIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -18,7 +18,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"regexp" "regexp"
@ -48,11 +48,11 @@ func (idp *QqIdProvider) SetHttpClient(client *http.Client) {
} }
func (idp *QqIdProvider) getConfig() *oauth2.Config { func (idp *QqIdProvider) getConfig() *oauth2.Config {
var endpoint = oauth2.Endpoint{ endpoint := oauth2.Endpoint{
TokenURL: "https://graph.qq.com/oauth2.0/token", TokenURL: "https://graph.qq.com/oauth2.0/token",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"get_user_info"}, Scopes: []string{"get_user_info"},
Endpoint: endpoint, Endpoint: endpoint,
} }
@ -75,7 +75,7 @@ func (idp *QqIdProvider) GetToken(code string) (*oauth2.Token, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
tokenContent, err := ioutil.ReadAll(resp.Body) tokenContent, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -148,7 +148,7 @@ func (idp *QqIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
openIdBody, err := ioutil.ReadAll(resp.Body) openIdBody, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -167,7 +167,7 @@ func (idp *QqIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
userInfoBody, err := ioutil.ReadAll(resp.Body) userInfoBody, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err 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 // 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 { 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", TokenURL: "https://graph.qq.com/oauth2.0/token",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"snsapi_login"}, Scopes: []string{"snsapi_login"},
Endpoint: endpoint, Endpoint: endpoint,
ClientID: clientId, ClientID: clientId,
@ -144,7 +144,7 @@ type WechatUserInfo struct {
City string `json:"city"` // City filled in by general user's personal data 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 Province string `json:"province"` // Province filled in by ordinary user's personal information
Country string `json:"country"` // Country, such as China is CN 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) 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. 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 ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"golang.org/x/oauth2" "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 { func (idp *WeChatMiniProgramIdProvider) getConfig(clientId string, clientSecret string) *oauth2.Config {
var config = &oauth2.Config{ config := &oauth2.Config{
ClientID: clientId, ClientID: clientId,
ClientSecret: clientSecret, ClientSecret: clientSecret,
} }
@ -65,7 +65,7 @@ func (idp *WeChatMiniProgramIdProvider) GetSessionByCode(code string) (*WeChatMi
return nil, err return nil, err
} }
defer sessionResponse.Body.Close() defer sessionResponse.Body.Close()
data, err := ioutil.ReadAll(sessionResponse.Body) data, err := io.ReadAll(sessionResponse.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -78,5 +78,4 @@ func (idp *WeChatMiniProgramIdProvider) GetSessionByCode(code string) (*WeChatMi
return nil, fmt.Errorf("err: %s", session.Errmsg) return nil, fmt.Errorf("err: %s", session.Errmsg)
} }
return &session, nil return &session, nil
} }

View File

@ -17,13 +17,14 @@ package idp
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"time" "time"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
// WeComInternalIdProvider
// This idp is using wecom internal application api as idp // This idp is using wecom internal application api as idp
type WeComInternalIdProvider struct { type WeComInternalIdProvider struct {
Client *http.Client 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 { func (idp *WeComInternalIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
var config = &oauth2.Config{ config := &oauth2.Config{
ClientID: clientId, ClientID: clientId,
ClientSecret: clientSecret, ClientSecret: clientSecret,
RedirectURL: redirectUrl, RedirectURL: redirectUrl,
@ -72,7 +73,7 @@ func (idp *WeComInternalIdProvider) GetToken(code string) (*oauth2.Token, error)
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -123,7 +124,7 @@ func (idp *WeComInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo,
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -144,7 +145,7 @@ func (idp *WeComInternalIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo,
return nil, err return nil, err
} }
data, err = ioutil.ReadAll(resp.Body) data, err = io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -18,7 +18,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"strings" "strings"
"time" "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 // 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 { 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", TokenURL: "https://graph.qq.com/oauth2.0/token",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{"snsapi_login"}, Scopes: []string{"snsapi_login"},
Endpoint: endpoint, Endpoint: endpoint,
ClientID: clientId, ClientID: clientId,
@ -195,7 +194,7 @@ func (idp *WeComIdProvider) postWithBody(body interface{}, url string) ([]byte,
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err := ioutil.ReadAll(resp.Body) data, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -19,7 +19,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "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 // 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 { 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", TokenURL: "https://api.weibo.com/oauth2/access_token",
} }
var config = &oauth2.Config{ config := &oauth2.Config{
Scopes: []string{""}, Scopes: []string{""},
Endpoint: endpoint, Endpoint: endpoint,
ClientID: clientId, ClientID: clientId,
@ -92,7 +91,7 @@ func (idp *WeiBoIdProvider) GetToken(code string) (*oauth2.Token, error) {
return return
} }
}(resp.Body) }(resp.Body)
bs, err := ioutil.ReadAll(resp.Body) bs, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -21,9 +21,10 @@ import (
"github.com/astaxie/beego" "github.com/astaxie/beego"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util" "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/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/core"
"xorm.io/xorm" "xorm.io/xorm"
) )

View File

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

View File

@ -18,6 +18,8 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"strings" "strings"
"time"
"unicode"
"github.com/casdoor/casdoor/cred" "github.com/casdoor/casdoor/cred"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
@ -29,6 +31,11 @@ var (
reFieldWhiteList *regexp.Regexp reFieldWhiteList *regexp.Regexp
) )
const (
SigninWrongTimesLimit = 5
LastSignWrongTimeDuration = time.Minute * 15
)
func init() { func init() {
reWhiteSpace, _ = regexp.Compile(`\s`) reWhiteSpace, _ = regexp.Compile(`\s`)
reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`) reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
@ -42,11 +49,25 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if application.IsSignupItemVisible("Username") { if application.IsSignupItemVisible("Username") {
if len(username) <= 1 { if len(username) <= 1 {
return "username must have at least 2 characters" 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" return "username cannot contain white spaces"
} else if HasUserByField(organization.Name, "name", username) { }
if HasUserByField(organization.Name, "name", username) {
return "username already exists" 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 { if len(password) <= 5 {
@ -112,7 +133,32 @@ func CheckUserSignup(application *Application, organization *Organization, usern
return "" 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 { func CheckPassword(user *User, password string) string {
// check the login error times
if msg := checkSigninErrorTimes(user); msg != "" {
return msg
}
organization := GetOrganizationByUser(user) organization := GetOrganizationByUser(user)
if organization == nil { if organization == nil {
return "organization does not exist" return "organization does not exist"
@ -122,14 +168,17 @@ func CheckPassword(user *User, password string) string {
if credManager != nil { if credManager != nil {
if organization.MasterPassword != "" { if organization.MasterPassword != "" {
if credManager.IsPasswordCorrect(password, organization.MasterPassword, "", organization.PasswordSalt) { if credManager.IsPasswordCorrect(password, organization.MasterPassword, "", organization.PasswordSalt) {
resetUserSigninErrorTimes(user)
return "" return ""
} }
} }
if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) { if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) {
resetUserSigninErrorTimes(user)
return "" return ""
} }
return "password incorrect"
return recordSigninErrorInfo(user)
} else { } else {
return fmt.Sprintf("unsupported password type: %s", organization.PasswordType) return fmt.Sprintf("unsupported password type: %s", organization.PasswordType)
} }
@ -197,16 +246,20 @@ func filterField(field string) bool {
return reFieldWhiteList.MatchString(field) 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 == "" { if requestUserId == "" {
return false, fmt.Errorf("please login first") return false, fmt.Errorf("please login first")
} }
if userId != "" {
targetUser := GetUser(userId) targetUser := GetUser(userId)
if targetUser == nil { if targetUser == nil {
return false, fmt.Errorf("the user: %s doesn't exist", userId) return false, fmt.Errorf("the user: %s doesn't exist", userId)
} }
userOwner = targetUser.Owner
}
hasPermission := false hasPermission := false
if strings.HasPrefix(requestUserId, "app/") { if strings.HasPrefix(requestUserId, "app/") {
hasPermission = true hasPermission = true
@ -219,7 +272,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool) (bool, error
hasPermission = true hasPermission = true
} else if requestUserId == userId { } else if requestUserId == userId {
hasPermission = true hasPermission = true
} else if targetUser.Owner == requestUser.Owner { } else if userOwner == requestUser.Owner {
if strict { if strict {
hasPermission = requestUser.IsAdmin hasPermission = requestUser.IsAdmin
} else { } else {

View File

@ -14,7 +14,11 @@
package object package object
import "regexp" import (
"fmt"
"regexp"
"time"
)
var reRealName *regexp.Regexp var reRealName *regexp.Regexp
@ -29,3 +33,32 @@ func init() {
func isValidRealName(s string) bool { func isValidRealName(s string) bool {
return reRealName.MatchString(s) 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 // DailSmtpServer Dail Smtp server
func DailSmtpServer(provider *Provider) error { func DailSmtpServer(provider *Provider) error {
dialer := gomail.NewDialer(provider.Host, provider.Port, provider.ClientId, provider.ClientSecret) dialer := gomail.NewDialer(provider.Host, provider.Port, provider.ClientId, provider.ClientSecret)
dialer.SSL = !provider.DisableSsl
sender, err := dialer.Dial() sender, err := dialer.Dial()
if err != nil { if err != nil {

View File

@ -16,8 +16,10 @@ package object
import ( import (
"encoding/gob" "encoding/gob"
"io/ioutil" "fmt"
"os"
"github.com/astaxie/beego"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/duo-labs/webauthn/webauthn" "github.com/duo-labs/webauthn/webauthn"
) )
@ -36,6 +38,8 @@ func InitDb() {
initWebAuthn() initWebAuthn()
} }
var staticBaseUrl = beego.AppConfig.String("staticBaseUrl")
func initBuiltInOrganization() bool { func initBuiltInOrganization() bool {
organization := getOrganization("admin", "built-in") organization := getOrganization("admin", "built-in")
if organization != nil { if organization != nil {
@ -48,10 +52,10 @@ func initBuiltInOrganization() bool {
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
DisplayName: "Built-in Organization", DisplayName: "Built-in Organization",
WebsiteUrl: "https://example.com", WebsiteUrl: "https://example.com",
Favicon: "https://cdn.casbin.com/static/favicon.ico", Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", staticBaseUrl),
PasswordType: "plain", PasswordType: "plain",
PhonePrefix: "86", PhonePrefix: "86",
DefaultAvatar: "https://casbin.org/img/casbin.svg", DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", staticBaseUrl),
Tags: []string{}, Tags: []string{},
AccountItems: []*AccountItem{ AccountItems: []*AccountItem{
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"}, {Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
@ -71,6 +75,8 @@ func initBuiltInOrganization() bool {
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"}, {Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"}, {Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", 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: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
@ -98,7 +104,7 @@ func initBuiltInUser() {
Type: "normal-user", Type: "normal-user",
Password: "123", Password: "123",
DisplayName: "Admin", DisplayName: "Admin",
Avatar: "https://casbin.org/img/casbin.svg", Avatar: fmt.Sprintf("%s/img/casbin.svg", staticBaseUrl),
Email: "admin@example.com", Email: "admin@example.com",
Phone: "12345678910", Phone: "12345678910",
Address: []string{}, Address: []string{},
@ -128,7 +134,7 @@ func initBuiltInApplication() {
Name: "app-built-in", Name: "app-built-in",
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
DisplayName: "Casdoor", 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", HomepageUrl: "https://casdoor.org",
Organization: "built-in", Organization: "built-in",
Cert: "cert-built-in", Cert: "cert-built-in",
@ -156,11 +162,11 @@ func initBuiltInApplication() {
func readTokenFromFile() (string, string) { func readTokenFromFile() (string, string) {
pemPath := "./object/token_jwt_key.pem" pemPath := "./object/token_jwt_key.pem"
keyPath := "./object/token_jwt_key.key" keyPath := "./object/token_jwt_key.key"
pem, err := ioutil.ReadFile(pemPath) pem, err := os.ReadFile(pemPath)
if err != nil { if err != nil {
return "", "" return "", ""
} }
key, err := ioutil.ReadFile(keyPath) key, err := os.ReadFile(keyPath)
if err != nil { if err != nil {
return "", "" return "", ""
} }

View File

@ -89,6 +89,8 @@ func initDefinedOrganization(organization *Organization) {
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"}, {Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"}, {Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", 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: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, 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) { func (l *ldapConn) GetLdapUsers(baseDn string) ([]ldapUser, error) {
SearchFilter := "(objectClass=posixAccount)" SearchFilter := "(objectClass=posixAccount)"
SearchAttributes := []string{"uidNumber", "uid", "cn", "gidNumber", "entryUUID", "mail", "email", SearchAttributes := []string{
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress"} "uidNumber", "uid", "cn", "gidNumber", "entryUUID", "mail", "email",
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress",
}
SearchFilterMsAD := "(objectClass=user)" SearchFilterMsAD := "(objectClass=user)"
SearchAttributesMsAD := []string{"uidNumber", "sAMAccountName", "cn", "gidNumber", "entryUUID", "mail", "email", SearchAttributesMsAD := []string{
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress"} "uidNumber", "sAMAccountName", "cn", "gidNumber", "entryUUID", "mail", "email",
"emailAddress", "telephoneNumber", "mobile", "mobileTelephoneNumber", "registeredAddress", "postalAddress",
}
var searchReq *goldap.SearchRequest var searchReq *goldap.SearchRequest
if l.IsAD { if l.IsAD {
searchReq = goldap.NewSearchRequest(baseDn, 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) existUuids = append(existUuids, uuid)
} }
return existUuids return existUuids

View File

@ -31,6 +31,7 @@ func GetLdapAutoSynchronizer() *LdapAutoSynchronizer {
return globalLdapAutoSynchronizer return globalLdapAutoSynchronizer
} }
// StartAutoSync
// start autosync for specified ldap, old existing autosync goroutine will be ceased // start autosync for specified ldap, old existing autosync goroutine will be ceased
func (l *LdapAutoSynchronizer) StartAutoSync(ldapId string) error { func (l *LdapAutoSynchronizer) StartAutoSync(ldapId string) error {
l.Lock() 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))) 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 // start all autosync goroutine for existing ldap servers in each organizations
func (l *LdapAutoSynchronizer) LdapAutoSynchronizerStartUpAll() { func (l *LdapAutoSynchronizer) LdapAutoSynchronizerStartUpAll() {
organizations := []*Organization{} organizations := []*Organization{}

View File

@ -15,6 +15,8 @@
package object package object
import ( import (
"fmt"
"github.com/casdoor/casdoor/cred" "github.com/casdoor/casdoor/cred"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "xorm.io/core"
@ -186,3 +188,31 @@ func DeleteOrganization(organization *Organization) bool {
func GetOrganizationByUser(user *User) *Organization { func GetOrganizationByUser(user *User) *Organization {
return getOrganization("admin", user.Owner) 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 ( import (
"fmt" "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" "github.com/casdoor/casdoor/util"
"xorm.io/core" "xorm.io/core"
) )
@ -34,24 +29,31 @@ type Permission struct {
Users []string `xorm:"mediumtext" json:"users"` Users []string `xorm:"mediumtext" json:"users"`
Roles []string `xorm:"mediumtext" json:"roles"` Roles []string `xorm:"mediumtext" json:"roles"`
Domains []string `xorm:"mediumtext" json:"domains"`
Model string `xorm:"varchar(100)" json:"model"` Model string `xorm:"varchar(100)" json:"model"`
Adapter string `xorm:"varchar(100)" json:"adapter"`
ResourceType string `xorm:"varchar(100)" json:"resourceType"` ResourceType string `xorm:"varchar(100)" json:"resourceType"`
Resources []string `xorm:"mediumtext" json:"resources"` Resources []string `xorm:"mediumtext" json:"resources"`
Actions []string `xorm:"mediumtext" json:"actions"` Actions []string `xorm:"mediumtext" json:"actions"`
Effect string `xorm:"varchar(100)" json:"effect"` Effect string `xorm:"varchar(100)" json:"effect"`
IsEnabled bool `json:"isEnabled"` IsEnabled bool `json:"isEnabled"`
Submitter string `xorm:"varchar(100)" json:"submitter"`
Approver string `xorm:"varchar(100)" json:"approver"`
ApproveTime string `xorm:"varchar(100)" json:"approveTime"`
State string `xorm:"varchar(100)" json:"state"`
} }
type PermissionRule struct { type PermissionRule struct {
PType 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 ''"` V0 string `xorm:"varchar(100) index not null default ''" json:"v0"`
V1 string `xorm:"varchar(100) index not null default ''"` V1 string `xorm:"varchar(100) index not null default ''" json:"v1"`
V2 string `xorm:"varchar(100) index not null default ''"` V2 string `xorm:"varchar(100) index not null default ''" json:"v2"`
V3 string `xorm:"varchar(100) index not null default ''"` V3 string `xorm:"varchar(100) index not null default ''" json:"v3"`
V4 string `xorm:"varchar(100) index not null default ''"` V4 string `xorm:"varchar(100) index not null default ''" json:"v4"`
V5 string `xorm:"varchar(100) index not null default ''"` 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 { func GetPermissionCount(owner, field, value string) int {
@ -121,7 +123,18 @@ func UpdatePermission(id string, permission *Permission) bool {
} }
if affected != 0 { if affected != 0 {
if oldPermission.Adapter != "" {
removePolicies(oldPermission) 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) addPolicies(permission)
} }
@ -134,10 +147,6 @@ func AddPermission(permission *Permission) bool {
panic(err) panic(err)
} }
if affected != 0 {
addPolicies(permission)
}
return affected != 0 return affected != 0
} }
@ -147,8 +156,17 @@ func DeletePermission(permission *Permission) bool {
panic(err) panic(err)
} }
if affected != 0 { if affected != 0 && permission.Adapter != "" {
removePolicies(permission) 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 return affected != 0
@ -158,74 +176,32 @@ func (permission *Permission) GetId() string {
return fmt.Sprintf("%s/%s", permission.Owner, permission.Name) return fmt.Sprintf("%s/%s", permission.Owner, permission.Name)
} }
func getEnforcer(permission *Permission) *casbin.Enforcer { func GetPermissionsByUser(userId string) []*Permission {
tableNamePrefix := conf.GetConfigString("tableNamePrefix") permissions := []*Permission{}
adapter, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetBeegoConfDataSourceName()+conf.GetConfigString("dbName"), "permission_rule", tableNamePrefix, true) err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions)
if err != nil { if err != nil {
panic(err) panic(err)
} }
modelText := ` return permissions
[request_definition]
r = sub, obj, act
[policy_definition]
p = permission, sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act`
permissionModel := getModel(permission.Owner, permission.Model)
if permissionModel != nil {
modelText = permissionModel.ModelText
} }
m, err := model.NewModelFromString(modelText)
func GetPermissionsByRole(roleId string) []*Permission {
permissions := []*Permission{}
err := adapter.Engine.Where("roles like ?", "%"+roleId+"%").Find(&permissions)
if err != nil { if err != nil {
panic(err) 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 { if err != nil {
panic(err) panic(err)
} }
err = enforcer.LoadFilteredPolicy(xormadapter.Filter{V0: []string{permission.GetId()}}) return permissions
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)
}
} }

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"` ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
State string `xorm:"varchar(100)" json:"state"` State string `xorm:"varchar(100)" json:"state"`
ProviderObjs []*Provider `xorm:"-" json:"providerObjs"`
} }
func GetProductCount(owner, field, value string) int { 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 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"` Host string `xorm:"varchar(100)" json:"host"`
Port int `json:"port"` Port int `json:"port"`
DisableSsl bool `json:"disableSsl"`
Title string `xorm:"varchar(100)" json:"title"` Title string `xorm:"varchar(100)" json:"title"`
Content string `xorm:"varchar(1000)" json:"content"` Content string `xorm:"varchar(1000)" json:"content"`

View File

@ -33,6 +33,15 @@ func (application *Application) GetProviderItem(providerName string) *ProviderIt
return nil 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 { func (pi *ProviderItem) IsProviderVisible() bool {
if pi.Provider == nil { if pi.Provider == nil {
return false return false

View File

@ -29,6 +29,7 @@ type Role struct {
Users []string `xorm:"mediumtext" json:"users"` Users []string `xorm:"mediumtext" json:"users"`
Roles []string `xorm:"mediumtext" json:"roles"` Roles []string `xorm:"mediumtext" json:"roles"`
Domains []string `xorm:"mediumtext" json:"domains"`
IsEnabled bool `json:"isEnabled"` IsEnabled bool `json:"isEnabled"`
} }
@ -88,7 +89,8 @@ func GetRole(id string) *Role {
func UpdateRole(id string, role *Role) bool { func UpdateRole(id string, role *Role) bool {
owner, name := util.GetOwnerAndNameFromId(id) owner, name := util.GetOwnerAndNameFromId(id)
if getRole(owner, name) == nil { oldRole := getRole(owner, name)
if oldRole == nil {
return false return false
} }
@ -121,3 +123,13 @@ func DeleteRole(role *Role) bool {
func (role *Role) GetId() string { func (role *Role) GetId() string {
return fmt.Sprintf("%s/%s", role.Owner, role.Name) 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" uuid "github.com/satori/go.uuid"
) )
// NewSamlResponse
// returns a saml2 response // returns a saml2 response
func NewSamlResponse(user *User, host string, certificate string, destination string, iss string, requestId string, redirectUri []string) (*etree.Element, error) { func NewSamlResponse(user *User, host string, certificate string, destination string, iss string, requestId string, redirectUri []string) (*etree.Element, error) {
samlResponse := &etree.Element{ 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) displayName.CreateElement("saml:AttributeValue").CreateAttr("xsi:type", "xs:string").Element().SetText(user.DisplayName)
return samlResponse, nil return samlResponse, nil
} }
type X509Key struct { type X509Key struct {
@ -114,6 +114,7 @@ func (x X509Key) GetKeyPair() (privateKey *rsa.PrivateKey, cert []byte, err erro
return privateKey, cert, err return privateKey, cert, err
} }
// IdpEntityDescriptor
// SAML METADATA // SAML METADATA
type IdpEntityDescriptor struct { type IdpEntityDescriptor struct {
XMLName xml.Name `xml:"EntityDescriptor"` 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) { func GenerateSamlLoginUrl(id, relayState string) (string, string, error) {
provider := GetProvider(id) provider := GetProvider(id)
if provider.Category != "SAML" { 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, "") sp, err := buildSp(provider, "")
if err != nil { if err != nil {

View File

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

View File

@ -19,12 +19,15 @@ func (syncer *Syncer) getUsers() []*User {
return users return users
} }
func (syncer *Syncer) getUserMap() ([]*User, map[string]*User) { func (syncer *Syncer) getUserMap() ([]*User, map[string]*User, map[string]*User) {
users := syncer.getUsers() users := syncer.getUsers()
m := map[string]*User{} m1 := map[string]*User{}
m2 := map[string]*User{}
for _, user := range users { 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 ( const (
hourSeconds = 3600 hourSeconds = 3600
INVALID_REQUEST = "invalid_request" InvalidRequest = "invalid_request"
INVALID_CLIENT = "invalid_client" InvalidClient = "invalid_client"
INVALID_GRANT = "invalid_grant" InvalidGrant = "invalid_grant"
UNAUTHORIZED_CLIENT = "unauthorized_client" UnauthorizedClient = "unauthorized_client"
UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type" UnsupportedGrantType = "unsupported_grant_type"
INVALID_SCOPE = "invalid_scope" InvalidScope = "invalid_scope"
ENDPOINT_ERROR = "endpoint_error" EndpointError = "endpoint_error"
) )
type Code struct { type Code struct {
@ -200,7 +200,7 @@ func DeleteToken(token *Token) bool {
return affected != 0 return affected != 0
} }
func DeleteTokenByAceessToken(accessToken string) (bool, *Application) { func DeleteTokenByAccessToken(accessToken string) (bool, *Application) {
token := Token{AccessToken: accessToken} token := Token{AccessToken: accessToken}
existed, err := adapter.Engine.Get(&token) existed, err := adapter.Engine.Get(&token)
if err != nil { 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 { if err != nil {
panic(err) panic(err)
} }
@ -298,7 +298,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
token := &Token{ token := &Token{
Owner: application.Owner, Owner: application.Owner,
Name: util.GenerateId(), Name: tokenName,
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
Application: application.Name, Application: application.Name,
Organization: user.Owner, Organization: user.Owner,
@ -325,7 +325,7 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
application := GetApplicationByClientId(clientId) application := GetApplicationByClientId(clientId)
if application == nil { if application == nil {
return &TokenError{ return &TokenError{
Error: INVALID_CLIENT, Error: InvalidClient,
ErrorDescription: "client_id is invalid", ErrorDescription: "client_id is invalid",
} }
} }
@ -334,7 +334,7 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
if !IsGrantTypeValid(grantType, application.GrantTypes) && tag == "" { if !IsGrantTypeValid(grantType, application.GrantTypes) && tag == "" {
return &TokenError{ return &TokenError{
Error: UNSUPPORTED_GRANT_TYPE, Error: UnsupportedGrantType,
ErrorDescription: fmt.Sprintf("grant_type: %s is not supported in this application", grantType), 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 // check parameters
if grantType != "refresh_token" { if grantType != "refresh_token" {
return &TokenError{ return &TokenError{
Error: UNSUPPORTED_GRANT_TYPE, Error: UnsupportedGrantType,
ErrorDescription: "grant_type should be refresh_token", ErrorDescription: "grant_type should be refresh_token",
} }
} }
application := GetApplicationByClientId(clientId) application := GetApplicationByClientId(clientId)
if application == nil { if application == nil {
return &TokenError{ return &TokenError{
Error: INVALID_CLIENT, Error: InvalidClient,
ErrorDescription: "client_id is invalid", ErrorDescription: "client_id is invalid",
} }
} }
if clientSecret != "" && application.ClientSecret != clientSecret { if clientSecret != "" && application.ClientSecret != clientSecret {
return &TokenError{ return &TokenError{
Error: INVALID_CLIENT, Error: InvalidClient,
ErrorDescription: "client_secret is invalid", ErrorDescription: "client_secret is invalid",
} }
} }
@ -399,7 +399,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
existed, err := adapter.Engine.Get(&token) existed, err := adapter.Engine.Get(&token)
if err != nil || !existed { if err != nil || !existed {
return &TokenError{ return &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "refresh token is invalid, expired or revoked", 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) _, err = ParseJwtToken(refreshToken, cert)
if err != nil { if err != nil {
return &TokenError{ return &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()), 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) user := getUser(application.Organization, token.User)
if user.IsForbidden { if user.IsForbidden {
return &TokenError{ return &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "the user is forbidden to sign in, please contact the administrator", 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 { if err != nil {
return &TokenError{ return &TokenError{
Error: ENDPOINT_ERROR, Error: EndpointError,
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()), ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
} }
} }
newToken := &Token{ newToken := &Token{
Owner: application.Owner, Owner: application.Owner,
Name: util.GenerateId(), Name: tokenName,
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
Application: application.Name, Application: application.Name,
Organization: user.Owner, Organization: user.Owner,
@ -464,6 +465,7 @@ func pkceChallenge(verifier string) string {
return challenge return challenge
} }
// IsGrantTypeValid
// Check if grantType is allowed in the current application // Check if grantType is allowed in the current application
// authorization_code is allowed by default // authorization_code is allowed by default
func IsGrantTypeValid(method string, grantTypes []string) bool { func IsGrantTypeValid(method string, grantTypes []string) bool {
@ -478,11 +480,12 @@ func IsGrantTypeValid(method string, grantTypes []string) bool {
return false return false
} }
// GetAuthorizationCodeToken
// Authorization code flow // Authorization code flow
func GetAuthorizationCodeToken(application *Application, clientSecret string, code string, verifier string) (*Token, *TokenError) { func GetAuthorizationCodeToken(application *Application, clientSecret string, code string, verifier string) (*Token, *TokenError) {
if code == "" { if code == "" {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_REQUEST, Error: InvalidRequest,
ErrorDescription: "authorization code should not be empty", ErrorDescription: "authorization code should not be empty",
} }
} }
@ -490,21 +493,21 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
token := getTokenByCode(code) token := getTokenByCode(code)
if token == nil { if token == nil {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "authorization code is invalid", ErrorDescription: "authorization code is invalid",
} }
} }
if token.CodeIsUsed { if token.CodeIsUsed {
// anti replay attacks // anti replay attacks
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "authorization code has been used", ErrorDescription: "authorization code has been used",
} }
} }
if token.CodeChallenge != "" && pkceChallenge(verifier) != token.CodeChallenge { if token.CodeChallenge != "" && pkceChallenge(verifier) != token.CodeChallenge {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "verifier is invalid", ErrorDescription: "verifier is invalid",
} }
} }
@ -514,13 +517,13 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
// but if it is provided, it must be accurate. // but if it is provided, it must be accurate.
if token.CodeChallenge == "" { if token.CodeChallenge == "" {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_CLIENT, Error: InvalidClient,
ErrorDescription: "client_secret is invalid", ErrorDescription: "client_secret is invalid",
} }
} else { } else {
if clientSecret != "" { if clientSecret != "" {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_CLIENT, Error: InvalidClient,
ErrorDescription: "client_secret is invalid", ErrorDescription: "client_secret is invalid",
} }
} }
@ -529,7 +532,7 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
if application.Name != token.Application { if application.Name != token.Application {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "the token is for wrong application (client_id)", 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 { if time.Now().Unix() > token.CodeExpireIn {
// code must be used within 5 minutes // code must be used within 5 minutes
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "authorization code has expired", ErrorDescription: "authorization code has expired",
} }
} }
return token, nil return token, nil
} }
// GetPasswordToken
// Resource Owner Password Credentials flow // Resource Owner Password Credentials flow
func GetPasswordToken(application *Application, username string, password string, scope string, host string) (*Token, *TokenError) { func GetPasswordToken(application *Application, username string, password string, scope string, host string) (*Token, *TokenError) {
user := getUser(application.Organization, username) user := getUser(application.Organization, username)
if user == nil { if user == nil {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "the user does not exist", ErrorDescription: "the user does not exist",
} }
} }
msg := CheckPassword(user, password) msg := CheckPassword(user, password)
if msg != "" { if msg != "" {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "invalid username or password", ErrorDescription: "invalid username or password",
} }
} }
if user.IsForbidden { if user.IsForbidden {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "the user is forbidden to sign in, please contact the administrator", 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 { if err != nil {
return nil, &TokenError{ return nil, &TokenError{
Error: ENDPOINT_ERROR, Error: EndpointError,
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()), ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
} }
} }
token := &Token{ token := &Token{
Owner: application.Owner, Owner: application.Owner,
Name: util.GenerateId(), Name: tokenName,
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
Application: application.Name, Application: application.Name,
Organization: user.Owner, Organization: user.Owner,
@ -592,11 +597,12 @@ func GetPasswordToken(application *Application, username string, password string
return token, nil return token, nil
} }
// GetClientCredentialsToken
// Client Credentials flow // Client Credentials flow
func GetClientCredentialsToken(application *Application, clientSecret string, scope string, host string) (*Token, *TokenError) { func GetClientCredentialsToken(application *Application, clientSecret string, scope string, host string) (*Token, *TokenError) {
if application.ClientSecret != clientSecret { if application.ClientSecret != clientSecret {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_CLIENT, Error: InvalidClient,
ErrorDescription: "client_secret is invalid", ErrorDescription: "client_secret is invalid",
} }
} }
@ -605,16 +611,17 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
Id: application.GetId(), Id: application.GetId(),
Name: fmt.Sprintf("app/%s", application.Name), 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 { if err != nil {
return nil, &TokenError{ return nil, &TokenError{
Error: ENDPOINT_ERROR, Error: EndpointError,
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()), ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
} }
} }
token := &Token{ token := &Token{
Owner: application.Owner, Owner: application.Owner,
Name: util.GenerateId(), Name: tokenName,
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
Application: application.Name, Application: application.Name,
Organization: application.Organization, Organization: application.Organization,
@ -630,15 +637,16 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
return token, nil return token, nil
} }
// GetTokenByUser
// Implicit flow // Implicit flow
func GetTokenByUser(application *Application, user *User, scope string, host string) (*Token, error) { 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 { if err != nil {
return nil, err return nil, err
} }
token := &Token{ token := &Token{
Owner: application.Owner, Owner: application.Owner,
Name: util.GenerateId(), Name: tokenName,
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
Application: application.Name, Application: application.Name,
Organization: user.Owner, Organization: user.Owner,
@ -655,12 +663,13 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
return token, nil return token, nil
} }
// GetWechatMiniProgramToken
// Wechat Mini Program flow // Wechat Mini Program flow
func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string) (*Token, *TokenError) { func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string) (*Token, *TokenError) {
mpProvider := GetWechatMiniProgramProvider(application) mpProvider := GetWechatMiniProgramProvider(application)
if mpProvider == nil { if mpProvider == nil {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_CLIENT, Error: InvalidClient,
ErrorDescription: "the application does not support wechat mini program", 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) session, err := mpIdp.GetSessionByCode(code)
if err != nil { if err != nil {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("get wechat mini program session error: %s", err.Error()), ErrorDescription: fmt.Sprintf("get wechat mini program session error: %s", err.Error()),
} }
} }
openId, unionId := session.Openid, session.Unionid openId, unionId := session.Openid, session.Unionid
if openId == "" && unionId == "" { if openId == "" && unionId == "" {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_REQUEST, Error: InvalidRequest,
ErrorDescription: "the wechat mini program session is invalid", ErrorDescription: "the wechat mini program session is invalid",
} }
} }
@ -684,7 +693,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
if user == nil { if user == nil {
if !application.EnableSignUp { if !application.EnableSignUp {
return nil, &TokenError{ return nil, &TokenError{
Error: INVALID_GRANT, Error: InvalidGrant,
ErrorDescription: "the application does not allow to sign up new account", 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, Avatar: avatar,
SignupApplication: application.Name, SignupApplication: application.Name,
WeChat: openId, WeChat: openId,
WeChatUnionId: unionId,
Type: "normal-user", Type: "normal-user",
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
IsAdmin: false, IsAdmin: false,
IsGlobalAdmin: false, IsGlobalAdmin: false,
IsForbidden: false, IsForbidden: false,
IsDeleted: false, IsDeleted: false,
Properties: map[string]string{
UserPropertiesWechatOpenId: openId,
UserPropertiesWechatUnionId: unionId,
},
} }
AddUser(user) AddUser(user)
} }
accessToken, refreshToken, err := generateJwtToken(application, user, "", "", host) accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", host)
if err != nil { if err != nil {
return nil, &TokenError{ return nil, &TokenError{
Error: ENDPOINT_ERROR, Error: EndpointError,
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()), ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
} }
} }
token := &Token{ token := &Token{
Owner: application.Owner, Owner: application.Owner,
Name: util.GenerateId(), Name: tokenName,
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
Application: application.Name, Application: application.Name,
Organization: user.Owner, Organization: user.Owner,

View File

@ -136,6 +136,7 @@ func GenerateId() {
panic("unimplemented") panic("unimplemented")
} }
// GetCasTokenByPgt
/** /**
@ret1: whether a token is found @ret1: whether a token is found
@ret2: token, nil if not found @ret2: token, nil if not found
@ -150,6 +151,7 @@ func GetCasTokenByPgt(pgt string) (bool, *CasAuthenticationSuccess, string, stri
return false, nil, "", "" return false, nil, "", ""
} }
// GetCasTokenByTicket
/** /**
@ret1: whether a token is found @ret1: whether a token is found
@ret2: token, nil if not found @ret2: token, nil if not found
@ -207,6 +209,7 @@ func GenerateCasToken(userId string, service string) (string, error) {
} }
} }
// GetValidationBySaml
/** /**
@ret1: saml response @ret1: saml response
@ret2: the service URL who requested to issue this token @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 "", "", fmt.Errorf("err: %s", err.Error())
} }
return xmlStr, service, nil return xmlStr, service, nil
} }
func (c *CasAuthenticationSuccess) DeepCopy() CasAuthenticationSuccess { func (c *CasAuthenticationSuccess) DeepCopy() CasAuthenticationSuccess {
@ -307,7 +309,6 @@ func (c *CasAttributes) DeepCopy() CasAttributes {
res.ExtraAttributes[i] = &tmp res.ExtraAttributes[i] = &tmp
} }
return res return res
} }
func (c *CasUserAttributes) DeepCopy() CasUserAttributes { func (c *CasUserAttributes) DeepCopy() CasUserAttributes {
@ -316,11 +317,11 @@ func (c *CasUserAttributes) DeepCopy() CasUserAttributes {
Attributes: make([]*CasNamedAttribute, len(c.Attributes)), Attributes: make([]*CasNamedAttribute, len(c.Attributes)),
} }
for i, a := range c.AnyAttributes { for i, a := range c.AnyAttributes {
var tmp = *a tmp := *a
res.AnyAttributes[i] = &tmp res.AnyAttributes[i] = &tmp
} }
for i, a := range c.Attributes { for i, a := range c.Attributes {
var tmp = *a tmp := *a
res.Attributes[i] = &tmp res.Attributes[i] = &tmp
} }
return res return res

View File

@ -19,6 +19,7 @@ import (
"time" "time"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
) )
@ -60,7 +61,7 @@ func getShortClaims(claims Claims) ClaimsShort {
return res 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() nowTime := time.Now()
expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour) expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour)
refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours) * 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 originBackend = origin
} }
name := util.GenerateId()
jti := fmt.Sprintf("%s/%s", application.Owner, name)
claims := Claims{ claims := Claims{
User: user, User: user,
Nonce: nonce, Nonce: nonce,
@ -85,7 +89,7 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
ExpiresAt: jwt.NewNumericDate(expireTime), ExpiresAt: jwt.NewNumericDate(expireTime),
NotBefore: jwt.NewNumericDate(nowTime), NotBefore: jwt.NewNumericDate(nowTime),
IssuedAt: 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 // RSA private key
key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey)) key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
if err != nil { if err != nil {
return "", "", err return "", "", "", err
} }
token.Header["kid"] = cert.Name token.Header["kid"] = cert.Name
tokenString, err := token.SignedString(key) tokenString, err := token.SignedString(key)
if err != nil { if err != nil {
return "", "", err return "", "", "", err
} }
refreshTokenString, err := refreshToken.SignedString(key) refreshTokenString, err := refreshToken.SignedString(key)
return tokenString, refreshTokenString, err return tokenString, refreshTokenString, name, err
} }
func ParseJwtToken(token string, cert *Cert) (*Claims, error) { func ParseJwtToken(token string, cert *Cert) (*Claims, error) {

View File

@ -24,6 +24,11 @@ import (
"xorm.io/core" "xorm.io/core"
) )
const (
UserPropertiesWechatUnionId = "wechatUnionId"
UserPropertiesWechatOpenId = "wechatOpenId"
)
type User struct { type User struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"` Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"` Name string `xorm:"varchar(100) notnull pk" json:"name"`
@ -73,11 +78,10 @@ type User struct {
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"` LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"` 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"` Google string `xorm:"varchar(100)" json:"google"`
QQ string `xorm:"qq varchar(100)" json:"qq"` QQ string `xorm:"qq varchar(100)" json:"qq"`
WeChat string `xorm:"wechat varchar(100)" json:"wechat"` WeChat string `xorm:"wechat varchar(100)" json:"wechat"`
WeChatUnionId string `xorm:"varchar(100)" json:"unionId"`
Facebook string `xorm:"facebook varchar(100)" json:"facebook"` Facebook string `xorm:"facebook varchar(100)" json:"facebook"`
DingTalk string `xorm:"dingtalk varchar(100)" json:"dingtalk"` DingTalk string `xorm:"dingtalk varchar(100)" json:"dingtalk"`
Weibo string `xorm:"weibo varchar(100)" json:"weibo"` Weibo string `xorm:"weibo varchar(100)" json:"weibo"`
@ -104,6 +108,12 @@ type User struct {
Ldap string `xorm:"ldap varchar(100)" json:"ldap"` Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
Properties map[string]string `json:"properties"` 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 { type Userinfo struct {
@ -240,7 +250,7 @@ func getUserByWechatId(wechatOpenId string, wechatUnionId string) *User {
wechatUnionId = wechatOpenId wechatUnionId = wechatOpenId
} }
user := &User{} 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 { if err != nil {
panic(err) 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 { func GetUserByUserId(owner string, userId string) *User {
if owner == "" || userId == "" { if owner == "" || userId == "" {
return nil return nil
@ -351,6 +379,7 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
"owner", "display_name", "avatar", "owner", "display_name", "avatar",
"location", "address", "region", "language", "affiliation", "title", "homepage", "bio", "score", "tag", "signup_application", "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", "is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials",
"signin_wrong_times", "last_signin_wrong_time",
} }
} }
if isGlobalAdmin { if isGlobalAdmin {
@ -396,6 +425,10 @@ func AddUser(user *User) bool {
} }
organization := GetOrganizationByUser(user) organization := GetOrganizationByUser(user)
if organization == nil {
return false
}
user.UpdateUserPassword(organization) user.UpdateUserPassword(organization)
user.UpdateUserHash() user.UpdateUserHash()

View File

@ -37,11 +37,11 @@ func TestSyncAvatarsFromGitHub(t *testing.T) {
users := GetGlobalUsers() users := GetGlobalUsers()
for _, user := range users { for _, user := range users {
if user.Github == "" { if user.GitHub == "" {
continue 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) updateUserColumn("avatar", user)
} }
} }

View File

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

View File

@ -50,30 +50,31 @@ func GetWebAuthnObject(host string) *webauthn.WebAuthn {
return webAuthn return webAuthn
} }
// WebAuthnID
// implementation of webauthn.User interface // implementation of webauthn.User interface
func (u *User) WebAuthnID() []byte { func (user *User) WebAuthnID() []byte {
return []byte(u.GetId()) return []byte(user.GetId())
} }
func (u *User) WebAuthnName() string { func (user *User) WebAuthnName() string {
return u.Name return user.Name
} }
func (u *User) WebAuthnDisplayName() string { func (user *User) WebAuthnDisplayName() string {
return u.DisplayName return user.DisplayName
} }
func (u *User) WebAuthnCredentials() []webauthn.Credential { func (user *User) WebAuthnCredentials() []webauthn.Credential {
return u.WebauthnCredentials return user.WebauthnCredentials
} }
func (u *User) WebAuthnIcon() string { func (user *User) WebAuthnIcon() string {
return u.Avatar return user.Avatar
} }
// CredentialExcludeList returns a CredentialDescriptor array filled with all the user's credentials // CredentialExcludeList returns a CredentialDescriptor array filled with all the user's credentials
func (u *User) CredentialExcludeList() []protocol.CredentialDescriptor { func (user *User) CredentialExcludeList() []protocol.CredentialDescriptor {
credentials := u.WebAuthnCredentials() credentials := user.WebAuthnCredentials()
credentialExcludeList := []protocol.CredentialDescriptor{} credentialExcludeList := []protocol.CredentialDescriptor{}
for _, cred := range credentials { for _, cred := range credentials {
descriptor := protocol.CredentialDescriptor{ descriptor := protocol.CredentialDescriptor{
@ -86,16 +87,16 @@ func (u *User) CredentialExcludeList() []protocol.CredentialDescriptor {
return credentialExcludeList return credentialExcludeList
} }
func (u *User) AddCredentials(credential webauthn.Credential, isGlobalAdmin bool) bool { func (user *User) AddCredentials(credential webauthn.Credential, isGlobalAdmin bool) bool {
u.WebauthnCredentials = append(u.WebauthnCredentials, credential) user.WebauthnCredentials = append(user.WebauthnCredentials, credential)
return UpdateUser(u.GetId(), u, []string{"webauthnCredentials"}, isGlobalAdmin) return UpdateUser(user.GetId(), user, []string{"webauthnCredentials"}, isGlobalAdmin)
} }
func (u *User) DeleteCredentials(credentialIdBase64 string) bool { func (user *User) DeleteCredentials(credentialIdBase64 string) bool {
for i, credential := range u.WebauthnCredentials { for i, credential := range user.WebauthnCredentials {
if base64.StdEncoding.EncodeToString(credential.ID) == credentialIdBase64 { if base64.StdEncoding.EncodeToString(credential.ID) == credentialIdBase64 {
u.WebauthnCredentials = append(u.WebauthnCredentials[0:i], u.WebauthnCredentials[i+1:]...) user.WebauthnCredentials = append(user.WebauthnCredentials[0:i], user.WebauthnCredentials[i+1:]...)
return UpdateUserForAllFields(u.GetId(), u) return UpdateUserForAllFields(user.GetId(), user)
} }
} }
return false return false

View File

@ -42,7 +42,7 @@ type VerificationRecord struct {
func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error { func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
if provider == nil { if provider == nil {
return fmt.Errorf("Please set an Email provider first") return fmt.Errorf("please set an Email provider first")
} }
sender := organization.DisplayName 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 { func SendVerificationCodeToPhone(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
if provider == nil { if provider == nil {
return errors.New("Please set a SMS provider first") return errors.New("please set a SMS provider first")
} }
code := getRandomCode(5) code := getRandomCode(5)
@ -85,7 +85,7 @@ func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordT
now := time.Now().Unix() now := time.Now().Unix()
if has && now-record.Time < 60 { 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 record.Owner = provider.Owner

View File

@ -20,7 +20,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -147,7 +146,7 @@ func (pp *GcPaymentProvider) doPost(postBytes []byte) ([]byte, error) {
} }
}(resp.Body) }(resp.Body)
respBytes, err := ioutil.ReadAll(resp.Body) respBytes, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

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

View File

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

View File

@ -30,8 +30,7 @@ func init() {
} }
func initAPI() { func initAPI() {
ns := ns := beego.NewNamespace("/",
beego.NewNamespace("/",
beego.NSNamespace("/api", beego.NSNamespace("/api",
beego.NSInclude( beego.NSInclude(
&controllers.ApiController{}, &controllers.ApiController{},
@ -79,11 +78,18 @@ func initAPI() {
beego.Router("/api/delete-role", &controllers.ApiController{}, "POST:DeleteRole") beego.Router("/api/delete-role", &controllers.ApiController{}, "POST:DeleteRole")
beego.Router("/api/get-permissions", &controllers.ApiController{}, "GET:GetPermissions") 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/get-permission", &controllers.ApiController{}, "GET:GetPermission")
beego.Router("/api/update-permission", &controllers.ApiController{}, "POST:UpdatePermission") beego.Router("/api/update-permission", &controllers.ApiController{}, "POST:UpdatePermission")
beego.Router("/api/add-permission", &controllers.ApiController{}, "POST:AddPermission") beego.Router("/api/add-permission", &controllers.ApiController{}, "POST:AddPermission")
beego.Router("/api/delete-permission", &controllers.ApiController{}, "POST:DeletePermission") 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-models", &controllers.ApiController{}, "GET:GetModels")
beego.Router("/api/get-model", &controllers.ApiController{}, "GET:GetModel") beego.Router("/api/get-model", &controllers.ApiController{}, "GET:GetModel")
beego.Router("/api/update-model", &controllers.ApiController{}, "POST:UpdateModel") 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-applications", &controllers.ApiController{}, "GET:GetApplications")
beego.Router("/api/get-application", &controllers.ApiController{}, "GET:GetApplication") beego.Router("/api/get-application", &controllers.ApiController{}, "GET:GetApplication")
beego.Router("/api/get-user-application", &controllers.ApiController{}, "GET:GetUserApplication") 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/update-application", &controllers.ApiController{}, "POST:UpdateApplication")
beego.Router("/api/add-application", &controllers.ApiController{}, "POST:AddApplication") beego.Router("/api/add-application", &controllers.ApiController{}, "POST:AddApplication")
beego.Router("/api/delete-application", &controllers.ApiController{}, "POST:DeleteApplication") 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/signup/finish", &controllers.ApiController{}, "Post:WebAuthnSignupFinish")
beego.Router("/api/webauthn/signin/begin", &controllers.ApiController{}, "Get:WebAuthnSigninBegin") beego.Router("/api/webauthn/signin/begin", &controllers.ApiController{}, "Get:WebAuthnSigninBegin")
beego.Router("/api/webauthn/signin/finish", &controllers.ApiController{}, "Post:WebAuthnSigninFinish") 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": { "/api/get-organizations": {
"get": { "get": {
"tags": [ "tags": [
@ -1853,6 +1882,24 @@
"description": "The id of the user", "description": "The id of the user",
"required": true, "required": true,
"type": "string" "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": { "responses": {
@ -3220,11 +3267,11 @@
} }
}, },
"definitions": { "definitions": {
"2127.0xc000427560.false": { "2200.0xc0003f8480.false": {
"title": "false", "title": "false",
"type": "object" "type": "object"
}, },
"2161.0xc000427590.false": { "2235.0xc0003f84b0.false": {
"title": "false", "title": "false",
"type": "object" "type": "object"
}, },
@ -3342,10 +3389,10 @@
"type": "object", "type": "object",
"properties": { "properties": {
"data": { "data": {
"$ref": "#/definitions/2127.0xc000427560.false" "$ref": "#/definitions/2200.0xc0003f8480.false"
}, },
"data2": { "data2": {
"$ref": "#/definitions/2161.0xc000427590.false" "$ref": "#/definitions/2235.0xc0003f84b0.false"
}, },
"msg": { "msg": {
"type": "string" "type": "string"
@ -3549,6 +3596,9 @@
"type": "integer", "type": "integer",
"format": "int64" "format": "int64"
}, },
"certificate": {
"type": "string"
},
"createdTime": { "createdTime": {
"type": "string" "type": "string"
}, },
@ -3571,9 +3621,6 @@
"privateKey": { "privateKey": {
"type": "string" "type": "string"
}, },
"certificate": {
"type": "string"
},
"scope": { "scope": {
"type": "string" "type": "string"
}, },
@ -4585,6 +4632,12 @@
"permanentAvatar": { "permanentAvatar": {
"type": "string" "type": "string"
}, },
"permissions": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Permission"
}
},
"phone": { "phone": {
"type": "string" "type": "string"
}, },
@ -4606,6 +4659,12 @@
"region": { "region": {
"type": "string" "type": "string"
}, },
"roles": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Role"
}
},
"score": { "score": {
"type": "integer", "type": "integer",
"format": "int64" "format": "int64"

View File

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

View File

@ -17,7 +17,9 @@ package util
import ( import (
"crypto/hmac" "crypto/hmac"
"crypto/sha1" "crypto/sha1"
"crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/hex"
) )
func GetHmacSha1(keyStr, value string) string { func GetHmacSha1(keyStr, value string) string {
@ -28,3 +30,10 @@ func GetHmacSha1(keyStr, value string) string {
return res 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" "regexp"
) )
var rePhoneCn *regexp.Regexp var (
var rePhone *regexp.Regexp rePhoneCn *regexp.Regexp
rePhone *regexp.Regexp
)
func init() { func init() {
// https://learnku.com/articles/31543 // https://learnku.com/articles/31543

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