mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-18 13:44:53 +08:00
Compare commits
39 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
482eb61168 | ||
![]() |
8819a8697b | ||
![]() |
85cb68eb66 | ||
![]() |
b25b5f0249 | ||
![]() |
947dcf6e75 | ||
![]() |
113c27db73 | ||
![]() |
badfe34755 | ||
![]() |
a5f9f61381 | ||
![]() |
2ce8c93ead | ||
![]() |
da41ac7275 | ||
![]() |
fd0c70a827 | ||
![]() |
c4a6f07672 | ||
![]() |
a67f541171 | ||
![]() |
192968bac8 | ||
![]() |
23d4488b64 | ||
![]() |
23f4684e1d | ||
![]() |
1a91e7b0f9 | ||
![]() |
811999b6cc | ||
![]() |
7786018051 | ||
![]() |
6c72f86d03 | ||
![]() |
5b151f4ec4 | ||
![]() |
e9b7d1266f | ||
![]() |
2d4998228c | ||
![]() |
d3ed6c348b | ||
![]() |
a22e05dcc1 | ||
![]() |
0ac2b69f5a | ||
![]() |
d090e9c860 | ||
![]() |
8ebb158765 | ||
![]() |
ea2f053630 | ||
![]() |
988b14c6b5 | ||
![]() |
a9e72ac3cb | ||
![]() |
498cd02d49 | ||
![]() |
a389842f59 | ||
![]() |
6c69daa666 | ||
![]() |
53c89bbe89 | ||
![]() |
9442aa9f7a | ||
![]() |
8a195715d0 | ||
![]() |
b985bab3f3 | ||
![]() |
477a090aa0 |
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -127,7 +127,7 @@ jobs:
|
|||||||
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 == 'casbin/casdoor' && github.event_name == 'push'
|
||||||
needs: [ frontend, backend, linter, e2e ]
|
needs: [ frontend, backend, linter, e2e ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -184,14 +184,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v1
|
||||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
if: github.repository == 'casbin/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||||
|
|
||||||
- name: Push to Docker Hub
|
- name: Push to Docker Hub
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
if: github.repository == 'casbin/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
target: STANDARD
|
target: STANDARD
|
||||||
@@ -201,7 +201,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Push All In One Version to Docker Hub
|
- name: Push All In One Version to Docker Hub
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
if: github.repository == 'casbin/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
target: ALLINONE
|
target: ALLINONE
|
||||||
|
2
.github/workflows/sync.yml
vendored
2
.github/workflows/sync.yml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
synchronize-with-crowdin:
|
synchronize-with-crowdin:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
|
if: github.repository == 'casbin/casdoor' && github.event_name == 'push'
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<h1 align="center" style="border-bottom: none;">📦⚡️ Casdoor</h1>
|
<h1 align="center" style="border-bottom: none;">📦⚡️ Casdoor</h1>
|
||||||
<h3 align="center">A UI-first centralized authentication / Single-Sign-On (SSO) platform based on OAuth 2.0 / OIDC.</h3>
|
<h3 align="center">An open-source UI-first Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML, CAS, LDAP, SCIM, WebAuthn, TOTP, MFA and RADIUS</h3>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="#badge">
|
<a href="#badge">
|
||||||
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
|
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
|
||||||
|
@@ -96,7 +96,7 @@ p, *, *, GET, /api/get-organization-names, *, *
|
|||||||
|
|
||||||
sa := stringadapter.NewAdapter(ruleText)
|
sa := stringadapter.NewAdapter(ruleText)
|
||||||
// load all rules from string adapter to enforcer's memory
|
// load all rules from string adapter to enforcer's memory
|
||||||
err := sa.LoadPolicy(Enforcer.GetModel())
|
err = sa.LoadPolicy(Enforcer.GetModel())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -151,7 +151,7 @@ func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath
|
|||||||
return true
|
return true
|
||||||
} else if urlPath == "/api/update-user" {
|
} else if urlPath == "/api/update-user" {
|
||||||
// Allow ordinary users to update their own information
|
// Allow ordinary users to update their own information
|
||||||
if subOwner == objOwner && subName == objName && !(subOwner == "built-in" && subName == "admin") {
|
if (subOwner == objOwner && subName == objName || subOwner == "app") && !(subOwner == "built-in" && subName == "admin") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@@ -282,17 +282,15 @@ func (c *ApiController) Logout() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, application, token, err := object.ExpireTokenByAccessToken(accessToken)
|
_, application, token, err := object.ExpireTokenByAccessToken(accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if token == nil {
|
||||||
if !affected {
|
|
||||||
c.ResponseError(c.T("token:Token not found, invalid accessToken"))
|
c.ResponseError(c.T("token:Token not found, invalid accessToken"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if application == nil {
|
if application == nil {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist")), token.Application)
|
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist")), token.Application)
|
||||||
return
|
return
|
||||||
@@ -319,7 +317,15 @@ func (c *ApiController) Logout() {
|
|||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
if application.IsRedirectUriValid(redirectUri) {
|
if application.IsRedirectUriValid(redirectUri) {
|
||||||
c.Ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?state=%s", strings.TrimRight(redirectUri, "/"), state))
|
redirectUrl := redirectUri
|
||||||
|
if state != "" {
|
||||||
|
if strings.Contains(redirectUri, "?") {
|
||||||
|
redirectUrl = fmt.Sprintf("%s&state=%s", strings.TrimSuffix(redirectUri, "/"), state)
|
||||||
|
} else {
|
||||||
|
redirectUrl = fmt.Sprintf("%s?state=%s", strings.TrimSuffix(redirectUri, "/"), state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Ctx.Redirect(http.StatusFound, redirectUrl)
|
||||||
} else {
|
} else {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri))
|
c.ResponseError(fmt.Sprintf(c.T("token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri))
|
||||||
return
|
return
|
||||||
|
@@ -173,6 +173,12 @@ func (c *ApiController) GetOrganizationApplications() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applications, err = object.GetAllowedApplications(applications, userId)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetMaskedApplications(applications, userId))
|
c.ResponseOk(object.GetMaskedApplications(applications, userId))
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
|
@@ -34,6 +34,7 @@ import (
|
|||||||
"github.com/casdoor/casdoor/proxy"
|
"github.com/casdoor/casdoor/proxy"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -154,7 +155,8 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
|||||||
resp = &Response{Status: "error", Msg: fmt.Sprintf("error: grant_type: %s is not supported in this application", form.Type), Data: ""}
|
resp = &Response{Status: "error", Msg: fmt.Sprintf("error: grant_type: %s is not supported in this application", form.Type), Data: ""}
|
||||||
} else {
|
} else {
|
||||||
scope := c.Input().Get("scope")
|
scope := c.Input().Get("scope")
|
||||||
token, _ := object.GetTokenByUser(application, user, scope, c.Ctx.Request.Host)
|
nonce := c.Input().Get("nonce")
|
||||||
|
token, _ := object.GetTokenByUser(application, user, scope, nonce, c.Ctx.Request.Host)
|
||||||
resp = tokenToResponse(token)
|
resp = tokenToResponse(token)
|
||||||
}
|
}
|
||||||
} else if form.Type == ResponseTypeSaml { // saml flow
|
} else if form.Type == ResponseTypeSaml { // saml flow
|
||||||
@@ -331,8 +333,6 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var user *object.User
|
var user *object.User
|
||||||
var msg string
|
|
||||||
|
|
||||||
if authForm.Password == "" {
|
if authForm.Password == "" {
|
||||||
if user, err = object.GetUserByFields(authForm.Organization, authForm.Username); err != nil {
|
if user, err = object.GetUserByFields(authForm.Organization, authForm.Username); err != nil {
|
||||||
c.ResponseError(err.Error(), nil)
|
c.ResponseError(err.Error(), nil)
|
||||||
@@ -354,20 +354,21 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check result through Email or Phone
|
// check result through Email or Phone
|
||||||
checkResult := object.CheckSigninCode(user, checkDest, authForm.Code, c.GetAcceptLanguage())
|
err = object.CheckSigninCode(user, checkDest, authForm.Code, c.GetAcceptLanguage())
|
||||||
if len(checkResult) != 0 {
|
if err != nil {
|
||||||
c.ResponseError(fmt.Sprintf("%s - %s", verificationCodeType, checkResult))
|
c.ResponseError(fmt.Sprintf("%s - %s", verificationCodeType, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable the verification code
|
// disable the verification code
|
||||||
err := object.DisableVerificationCode(checkDest)
|
err = object.DisableVerificationCode(checkDest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error(), nil)
|
c.ResponseError(err.Error(), nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
var application *object.Application
|
||||||
|
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error(), nil)
|
c.ResponseError(err.Error(), nil)
|
||||||
return
|
return
|
||||||
@@ -386,7 +387,8 @@ func (c *ApiController) Login() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
} else if enableCaptcha {
|
} else if enableCaptcha {
|
||||||
isHuman, err := captcha.VerifyCaptchaByCaptchaType(authForm.CaptchaType, authForm.CaptchaToken, authForm.ClientSecret)
|
var isHuman bool
|
||||||
|
isHuman, err = captcha.VerifyCaptchaByCaptchaType(authForm.CaptchaType, authForm.CaptchaToken, authForm.ClientSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -399,13 +401,15 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
password := authForm.Password
|
password := authForm.Password
|
||||||
user, msg = object.CheckUserPassword(authForm.Organization, authForm.Username, password, c.GetAcceptLanguage(), enableCaptcha)
|
user, err = object.CheckUserPassword(authForm.Organization, authForm.Username, password, c.GetAcceptLanguage(), enableCaptcha)
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg != "" {
|
if err != nil {
|
||||||
resp = &Response{Status: "error", Msg: msg}
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
var application *object.Application
|
||||||
|
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -416,7 +420,8 @@ func (c *ApiController) Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
organization, err := object.GetOrganizationByUser(user)
|
var organization *object.Organization
|
||||||
|
organization, err = object.GetOrganizationByUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
}
|
}
|
||||||
@@ -461,12 +466,15 @@ func (c *ApiController) Login() {
|
|||||||
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
organization, err := object.GetOrganization(util.GetId("admin", application.Organization))
|
|
||||||
|
var organization *object.Organization
|
||||||
|
organization, err = object.GetOrganization(util.GetId("admin", application.Organization))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(c.T(err.Error()))
|
c.ResponseError(c.T(err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, err := object.GetProvider(util.GetId("admin", authForm.Provider))
|
var provider *object.Provider
|
||||||
|
provider, err = object.GetProvider(util.GetId("admin", authForm.Provider))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -488,7 +496,8 @@ func (c *ApiController) Login() {
|
|||||||
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
||||||
// OAuth
|
// OAuth
|
||||||
idpInfo := object.FromProviderToIdpInfo(c.Ctx, provider)
|
idpInfo := object.FromProviderToIdpInfo(c.Ctx, provider)
|
||||||
idProvider, err := idp.GetIdProvider(idpInfo, authForm.RedirectUri)
|
var idProvider idp.IdProvider
|
||||||
|
idProvider, err = idp.GetIdProvider(idpInfo, authForm.RedirectUri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -506,7 +515,8 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/golang/oauth2/issues/123#issuecomment-103715338
|
// https://github.com/golang/oauth2/issues/123#issuecomment-103715338
|
||||||
token, err := idProvider.GetToken(authForm.Code)
|
var token *oauth2.Token
|
||||||
|
token, err = idProvider.GetToken(authForm.Code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -547,7 +557,12 @@ func (c *ApiController) Login() {
|
|||||||
if user.IsForbidden {
|
if user.IsForbidden {
|
||||||
c.ResponseError(c.T("check:The user is forbidden to sign in, please contact the administrator"))
|
c.ResponseError(c.T("check:The user is forbidden to sign in, please contact the administrator"))
|
||||||
}
|
}
|
||||||
|
// sync info from 3rd-party if possible
|
||||||
|
_, err = object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||||
|
|
||||||
record := object.NewRecord(c.Ctx)
|
record := object.NewRecord(c.Ctx)
|
||||||
@@ -588,14 +603,16 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle username conflicts
|
// Handle username conflicts
|
||||||
tmpUser, err := object.GetUser(util.GetId(application.Organization, userInfo.Username))
|
var tmpUser *object.User
|
||||||
|
tmpUser, err = object.GetUser(util.GetId(application.Organization, userInfo.Username))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if tmpUser != nil {
|
if tmpUser != nil {
|
||||||
uid, err := uuid.NewRandom()
|
var uid uuid.UUID
|
||||||
|
uid, err = uuid.NewRandom()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -606,14 +623,16 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
properties := map[string]string{}
|
properties := map[string]string{}
|
||||||
count, err := object.GetUserCount(application.Organization, "", "", "")
|
var count int64
|
||||||
|
count, err = object.GetUserCount(application.Organization, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
properties["no"] = strconv.Itoa(int(count + 2))
|
properties["no"] = strconv.Itoa(int(count + 2))
|
||||||
initScore, err := organization.GetInitScore()
|
var initScore int
|
||||||
|
initScore, err = organization.GetInitScore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
||||||
return
|
return
|
||||||
@@ -645,7 +664,8 @@ func (c *ApiController) Login() {
|
|||||||
Properties: properties,
|
Properties: properties,
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := object.AddUser(user)
|
var affected bool
|
||||||
|
affected, err = object.AddUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -667,7 +687,7 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sync info from 3rd-party if possible
|
// sync info from 3rd-party if possible
|
||||||
_, err := object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
|
_, err = object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -703,7 +723,8 @@ func (c *ApiController) Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
oldUser, err := object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
|
var oldUser *object.User
|
||||||
|
oldUser, err = object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -714,7 +735,8 @@ func (c *ApiController) Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := object.GetUser(userId)
|
var user *object.User
|
||||||
|
user, err = object.GetUser(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -727,7 +749,8 @@ func (c *ApiController) Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
isLinked, err := object.LinkUserAccount(user, provider.Type, userInfo.Id)
|
var isLinked bool
|
||||||
|
isLinked, err = object.LinkUserAccount(user, provider.Type, userInfo.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -740,7 +763,8 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if c.getMfaUserSession() != "" {
|
} else if c.getMfaUserSession() != "" {
|
||||||
user, err := object.GetUser(c.getMfaUserSession())
|
var user *object.User
|
||||||
|
user, err = object.GetUser(c.getMfaUserSession())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -773,7 +797,8 @@ func (c *ApiController) Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
var application *object.Application
|
||||||
|
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -794,7 +819,8 @@ func (c *ApiController) Login() {
|
|||||||
} else {
|
} else {
|
||||||
if c.GetSessionUsername() != "" {
|
if c.GetSessionUsername() != "" {
|
||||||
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
|
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
|
||||||
application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
var application *object.Application
|
||||||
|
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
|
@@ -243,7 +243,13 @@ func (c *ApiController) GetAllObjects() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetAllObjects(userId))
|
objects, err := object.GetAllObjects(userId)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(objects)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) GetAllActions() {
|
func (c *ApiController) GetAllActions() {
|
||||||
@@ -253,7 +259,13 @@ func (c *ApiController) GetAllActions() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetAllActions(userId))
|
actions, err := object.GetAllActions(userId)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) GetAllRoles() {
|
func (c *ApiController) GetAllRoles() {
|
||||||
@@ -263,5 +275,11 @@ func (c *ApiController) GetAllRoles() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(object.GetAllRoles(userId))
|
roles, err := object.GetAllRoles(userId)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(roles)
|
||||||
}
|
}
|
||||||
|
@@ -65,13 +65,13 @@ func (c *ApiController) GetCerts() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGlobleCerts
|
// GetGlobalCerts
|
||||||
// @Title GetGlobleCerts
|
// @Title GetGlobalCerts
|
||||||
// @Tag Cert API
|
// @Tag Cert API
|
||||||
// @Description get globle certs
|
// @Description get globle certs
|
||||||
// @Success 200 {array} object.Cert The Response object
|
// @Success 200 {array} object.Cert The Response object
|
||||||
// @router /get-globle-certs [get]
|
// @router /get-global-certs [get]
|
||||||
func (c *ApiController) GetGlobleCerts() {
|
func (c *ApiController) GetGlobalCerts() {
|
||||||
limit := c.Input().Get("pageSize")
|
limit := c.Input().Get("pageSize")
|
||||||
page := c.Input().Get("p")
|
page := c.Input().Get("p")
|
||||||
field := c.Input().Get("field")
|
field := c.Input().Get("field")
|
||||||
@@ -80,7 +80,7 @@ func (c *ApiController) GetGlobleCerts() {
|
|||||||
sortOrder := c.Input().Get("sortOrder")
|
sortOrder := c.Input().Get("sortOrder")
|
||||||
|
|
||||||
if limit == "" || page == "" {
|
if limit == "" || page == "" {
|
||||||
maskedCerts, err := object.GetMaskedCerts(object.GetGlobleCerts())
|
maskedCerts, err := object.GetMaskedCerts(object.GetGlobalCerts())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
|
@@ -59,6 +59,7 @@ func (c *ApiController) GetLdapUsers() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
//groupsMap, err := conn.GetLdapGroups(ldapServer.BaseDn)
|
//groupsMap, err := conn.GetLdapGroups(ldapServer.BaseDn)
|
||||||
//if err != nil {
|
//if err != nil {
|
||||||
|
@@ -163,6 +163,8 @@ func (c *ApiController) BuyProduct() {
|
|||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
host := c.Ctx.Request.Host
|
host := c.Ctx.Request.Host
|
||||||
providerName := c.Input().Get("providerName")
|
providerName := c.Input().Get("providerName")
|
||||||
|
paymentEnv := c.Input().Get("paymentEnv")
|
||||||
|
|
||||||
// buy `pricingName/planName` for `paidUserName`
|
// buy `pricingName/planName` for `paidUserName`
|
||||||
pricingName := c.Input().Get("pricingName")
|
pricingName := c.Input().Get("pricingName")
|
||||||
planName := c.Input().Get("planName")
|
planName := c.Input().Get("planName")
|
||||||
@@ -187,11 +189,11 @@ func (c *ApiController) BuyProduct() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
payment, err := object.BuyProduct(id, user, providerName, pricingName, planName, host)
|
payment, attachInfo, err := object.BuyProduct(id, user, providerName, pricingName, planName, host, paymentEnv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(payment)
|
c.ResponseOk(payment, attachInfo)
|
||||||
}
|
}
|
||||||
|
@@ -158,10 +158,9 @@ func (c *ApiController) DeleteToken() {
|
|||||||
// @Success 401 {object} object.TokenError The Response object
|
// @Success 401 {object} object.TokenError The Response object
|
||||||
// @router api/login/oauth/access_token [post]
|
// @router api/login/oauth/access_token [post]
|
||||||
func (c *ApiController) GetOAuthToken() {
|
func (c *ApiController) GetOAuthToken() {
|
||||||
grantType := c.Input().Get("grant_type")
|
|
||||||
refreshToken := c.Input().Get("refresh_token")
|
|
||||||
clientId := c.Input().Get("client_id")
|
clientId := c.Input().Get("client_id")
|
||||||
clientSecret := c.Input().Get("client_secret")
|
clientSecret := c.Input().Get("client_secret")
|
||||||
|
grantType := c.Input().Get("grant_type")
|
||||||
code := c.Input().Get("code")
|
code := c.Input().Get("code")
|
||||||
verifier := c.Input().Get("code_verifier")
|
verifier := c.Input().Get("code_verifier")
|
||||||
scope := c.Input().Get("scope")
|
scope := c.Input().Get("scope")
|
||||||
@@ -169,35 +168,61 @@ func (c *ApiController) GetOAuthToken() {
|
|||||||
password := c.Input().Get("password")
|
password := c.Input().Get("password")
|
||||||
tag := c.Input().Get("tag")
|
tag := c.Input().Get("tag")
|
||||||
avatar := c.Input().Get("avatar")
|
avatar := c.Input().Get("avatar")
|
||||||
|
refreshToken := c.Input().Get("refresh_token")
|
||||||
|
|
||||||
if clientId == "" && clientSecret == "" {
|
if clientId == "" && clientSecret == "" {
|
||||||
clientId, clientSecret, _ = c.Ctx.Request.BasicAuth()
|
clientId, clientSecret, _ = c.Ctx.Request.BasicAuth()
|
||||||
}
|
}
|
||||||
if clientId == "" {
|
|
||||||
// If clientID is empty, try to read data from RequestBody
|
if len(c.Ctx.Input.RequestBody) != 0 {
|
||||||
|
// If clientId is empty, try to read data from RequestBody
|
||||||
var tokenRequest TokenRequest
|
var tokenRequest TokenRequest
|
||||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &tokenRequest); err == nil {
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &tokenRequest)
|
||||||
clientId = tokenRequest.ClientId
|
if err == nil {
|
||||||
clientSecret = tokenRequest.ClientSecret
|
if clientId == "" {
|
||||||
grantType = tokenRequest.GrantType
|
clientId = tokenRequest.ClientId
|
||||||
refreshToken = tokenRequest.RefreshToken
|
}
|
||||||
code = tokenRequest.Code
|
if clientSecret == "" {
|
||||||
verifier = tokenRequest.Verifier
|
clientSecret = tokenRequest.ClientSecret
|
||||||
scope = tokenRequest.Scope
|
}
|
||||||
username = tokenRequest.Username
|
if grantType == "" {
|
||||||
password = tokenRequest.Password
|
grantType = tokenRequest.GrantType
|
||||||
tag = tokenRequest.Tag
|
}
|
||||||
avatar = tokenRequest.Avatar
|
if code == "" {
|
||||||
|
code = tokenRequest.Code
|
||||||
|
}
|
||||||
|
if verifier == "" {
|
||||||
|
verifier = tokenRequest.Verifier
|
||||||
|
}
|
||||||
|
if scope == "" {
|
||||||
|
scope = tokenRequest.Scope
|
||||||
|
}
|
||||||
|
if username == "" {
|
||||||
|
username = tokenRequest.Username
|
||||||
|
}
|
||||||
|
if password == "" {
|
||||||
|
password = tokenRequest.Password
|
||||||
|
}
|
||||||
|
if tag == "" {
|
||||||
|
tag = tokenRequest.Tag
|
||||||
|
}
|
||||||
|
if avatar == "" {
|
||||||
|
avatar = tokenRequest.Avatar
|
||||||
|
}
|
||||||
|
if refreshToken == "" {
|
||||||
|
refreshToken = tokenRequest.RefreshToken
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
host := c.Ctx.Request.Host
|
host := c.Ctx.Request.Host
|
||||||
oAuthtoken, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
|
token, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = oAuthtoken
|
c.Data["json"] = token
|
||||||
c.SetTokenErrorHttpStatus()
|
c.SetTokenErrorHttpStatus()
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
@@ -15,10 +15,10 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
type TokenRequest struct {
|
type TokenRequest struct {
|
||||||
GrantType string `json:"grant_type"`
|
|
||||||
Code string `json:"code"`
|
|
||||||
ClientId string `json:"client_id"`
|
ClientId string `json:"client_id"`
|
||||||
ClientSecret string `json:"client_secret"`
|
ClientSecret string `json:"client_secret"`
|
||||||
|
GrantType string `json:"grant_type"`
|
||||||
|
Code string `json:"code"`
|
||||||
Verifier string `json:"code_verifier"`
|
Verifier string `json:"code_verifier"`
|
||||||
Scope string `json:"scope"`
|
Scope string `json:"scope"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
@@ -161,7 +161,6 @@ func (c *ApiController) GetUser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var user *object.User
|
var user *object.User
|
||||||
|
|
||||||
if id == "" && owner == "" {
|
if id == "" && owner == "" {
|
||||||
switch {
|
switch {
|
||||||
case email != "":
|
case email != "":
|
||||||
@@ -176,7 +175,8 @@ func (c *ApiController) GetUser() {
|
|||||||
owner = util.GetOwnerFromId(id)
|
owner = util.GetOwnerFromId(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
organization, err := object.GetOrganization(util.GetId("admin", owner))
|
var organization *object.Organization
|
||||||
|
organization, err = object.GetOrganization(util.GetId("admin", owner))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -476,16 +476,16 @@ func (c *ApiController) SetPassword() {
|
|||||||
isAdmin := c.IsAdmin()
|
isAdmin := c.IsAdmin()
|
||||||
if isAdmin {
|
if isAdmin {
|
||||||
if oldPassword != "" {
|
if oldPassword != "" {
|
||||||
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
err = object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||||
if msg != "" {
|
if err != nil {
|
||||||
c.ResponseError(msg)
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if code == "" {
|
} else if code == "" {
|
||||||
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
err = object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||||
if msg != "" {
|
if err != nil {
|
||||||
c.ResponseError(msg)
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -518,11 +518,11 @@ func (c *ApiController) CheckUserPassword() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, msg := object.CheckUserPassword(user.Owner, user.Name, user.Password, c.GetAcceptLanguage())
|
_, err = object.CheckUserPassword(user.Owner, user.Name, user.Password, c.GetAcceptLanguage())
|
||||||
if msg == "" {
|
if err != nil {
|
||||||
c.ResponseOk()
|
c.ResponseError(err.Error())
|
||||||
} else {
|
} else {
|
||||||
c.ResponseError(msg)
|
c.ResponseOk()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -154,6 +154,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
|
|||||||
// @router /webauthn/signin/finish [post]
|
// @router /webauthn/signin/finish [post]
|
||||||
func (c *ApiController) WebAuthnSigninFinish() {
|
func (c *ApiController) WebAuthnSigninFinish() {
|
||||||
responseType := c.Input().Get("responseType")
|
responseType := c.Input().Get("responseType")
|
||||||
|
clientId := c.Input().Get("clientId")
|
||||||
webauthnObj, err := object.GetWebAuthnObject(c.Ctx.Request.Host)
|
webauthnObj, err := object.GetWebAuthnObject(c.Ctx.Request.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
@@ -182,7 +183,13 @@ func (c *ApiController) WebAuthnSigninFinish() {
|
|||||||
c.SetSessionUsername(userId)
|
c.SetSessionUsername(userId)
|
||||||
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
|
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
|
||||||
|
|
||||||
application, err := object.GetApplicationByUser(user)
|
var application *object.Application
|
||||||
|
|
||||||
|
if clientId != "" && (responseType == ResponseTypeCode) {
|
||||||
|
application, err = object.GetApplicationByClientId(clientId)
|
||||||
|
} else {
|
||||||
|
application, err = object.GetApplicationByUser(user)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
|
7
go.mod
7
go.mod
@@ -15,7 +15,7 @@ require (
|
|||||||
github.com/casdoor/gomail/v2 v2.0.1
|
github.com/casdoor/gomail/v2 v2.0.1
|
||||||
github.com/casdoor/notify v0.45.0
|
github.com/casdoor/notify v0.45.0
|
||||||
github.com/casdoor/oss v1.3.0
|
github.com/casdoor/oss v1.3.0
|
||||||
github.com/casdoor/xorm-adapter/v3 v3.0.4
|
github.com/casdoor/xorm-adapter/v3 v3.1.0
|
||||||
github.com/casvisor/casvisor-go-sdk v1.0.3
|
github.com/casvisor/casvisor-go-sdk v1.0.3
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
github.com/denisenkom/go-mssqldb v0.9.0
|
github.com/denisenkom/go-mssqldb v0.9.0
|
||||||
@@ -24,7 +24,7 @@ require (
|
|||||||
github.com/fogleman/gg v1.3.0
|
github.com/fogleman/gg v1.3.0
|
||||||
github.com/forestmgy/ldapserver v1.1.0
|
github.com/forestmgy/ldapserver v1.1.0
|
||||||
github.com/go-git/go-git/v5 v5.6.0
|
github.com/go-git/go-git/v5 v5.6.0
|
||||||
github.com/go-ldap/ldap/v3 v3.3.0
|
github.com/go-ldap/ldap/v3 v3.4.6
|
||||||
github.com/go-mysql-org/go-mysql v1.7.0
|
github.com/go-mysql-org/go-mysql v1.7.0
|
||||||
github.com/go-pay/gopay v1.5.72
|
github.com/go-pay/gopay v1.5.72
|
||||||
github.com/go-sql-driver/mysql v1.6.0
|
github.com/go-sql-driver/mysql v1.6.0
|
||||||
@@ -32,6 +32,7 @@ require (
|
|||||||
github.com/go-webauthn/webauthn v0.6.0
|
github.com/go-webauthn/webauthn v0.6.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.3.1
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
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 v1.2.21
|
github.com/lestrrat-go/jwx v1.2.21
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
@@ -61,7 +62,7 @@ require (
|
|||||||
github.com/xorm-io/core v0.7.4
|
github.com/xorm-io/core v0.7.4
|
||||||
github.com/xorm-io/xorm v1.1.6
|
github.com/xorm-io/xorm v1.1.6
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
golang.org/x/crypto v0.12.0
|
golang.org/x/crypto v0.13.0
|
||||||
golang.org/x/net v0.14.0
|
golang.org/x/net v0.14.0
|
||||||
golang.org/x/oauth2 v0.11.0
|
golang.org/x/oauth2 v0.11.0
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
|
18
go.sum
18
go.sum
@@ -780,6 +780,8 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM
|
|||||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||||
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
@@ -823,6 +825,7 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
|||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
|
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||||
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387 h1:loy0fjI90vF44BPW4ZYOkE3tDkGTy7yHURusOJimt+I=
|
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387 h1:loy0fjI90vF44BPW4ZYOkE3tDkGTy7yHURusOJimt+I=
|
||||||
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387/go.mod h1:GuR5j/NW7AU7tDAQUDGCtpiPxWIOy/c3kiRDnlwiCHc=
|
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387/go.mod h1:GuR5j/NW7AU7tDAQUDGCtpiPxWIOy/c3kiRDnlwiCHc=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||||
@@ -930,8 +933,8 @@ github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk
|
|||||||
github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ=
|
github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ=
|
||||||
github.com/casdoor/oss v1.3.0 h1:D5pcz65tJRqJrWY11Ks7D9LUsmlhqqMHugjDhSxWTvk=
|
github.com/casdoor/oss v1.3.0 h1:D5pcz65tJRqJrWY11Ks7D9LUsmlhqqMHugjDhSxWTvk=
|
||||||
github.com/casdoor/oss v1.3.0/go.mod h1:YOi6KpG1pZHTkiy9AYaqI0UaPfE7YkaA07d89f1idqY=
|
github.com/casdoor/oss v1.3.0/go.mod h1:YOi6KpG1pZHTkiy9AYaqI0UaPfE7YkaA07d89f1idqY=
|
||||||
github.com/casdoor/xorm-adapter/v3 v3.0.4 h1:vB04Ao8n2jA7aFBI9F+gGXo9+Aa1IQP6mTdo50913DM=
|
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
|
||||||
github.com/casdoor/xorm-adapter/v3 v3.0.4/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
|
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
|
||||||
github.com/casvisor/casvisor-go-sdk v1.0.3 h1:TKJQWKnhtznEBhzLPEdNsp7nJK2GgdD8JsB0lFPMW7U=
|
github.com/casvisor/casvisor-go-sdk v1.0.3 h1:TKJQWKnhtznEBhzLPEdNsp7nJK2GgdD8JsB0lFPMW7U=
|
||||||
github.com/casvisor/casvisor-go-sdk v1.0.3/go.mod h1:frnNtH5GA0wxzAQLyZxxfL0RSsSub9GQPi2Ybe86ocE=
|
github.com/casvisor/casvisor-go-sdk v1.0.3/go.mod h1:frnNtH5GA0wxzAQLyZxxfL0RSsSub9GQPi2Ybe86ocE=
|
||||||
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||||
@@ -1083,6 +1086,8 @@ github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
|||||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA=
|
||||||
|
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
|
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
|
||||||
github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
|
github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
|
||||||
@@ -1112,6 +1117,8 @@ github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2C
|
|||||||
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
|
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
|
||||||
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
|
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
|
||||||
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
|
||||||
|
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
@@ -1246,6 +1253,7 @@ github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=
|
|||||||
github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4=
|
github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4=
|
||||||
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
|
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
|
||||||
github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
|
github.com/google/go-tpm-tools v0.2.0/go.mod h1:npUd03rQ60lxN7tzeBJreG38RvWwme2N1reF/eeiBk4=
|
||||||
|
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
@@ -1949,6 +1957,8 @@ golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45
|
|||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
|
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||||
|
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@@ -2273,6 +2283,8 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -2289,6 +2301,8 @@ golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
|||||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||||
|
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
||||||
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@@ -31,6 +31,7 @@ type UserInfo struct {
|
|||||||
Phone string
|
Phone string
|
||||||
CountryCode string
|
CountryCode string
|
||||||
AvatarUrl string
|
AvatarUrl string
|
||||||
|
Extra map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProviderInfo struct {
|
type ProviderInfo struct {
|
||||||
|
@@ -186,15 +186,24 @@ func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
|||||||
id = wechatUserInfo.Openid
|
id = wechatUserInfo.Openid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extra := make(map[string]string)
|
||||||
|
extra["wechat_unionid"] = wechatUserInfo.Openid
|
||||||
|
// For WeChat, different appId corresponds to different openId
|
||||||
|
extra[BuildWechatOpenIdKey(idp.Config.ClientID)] = wechatUserInfo.Openid
|
||||||
userInfo := UserInfo{
|
userInfo := UserInfo{
|
||||||
Id: id,
|
Id: id,
|
||||||
Username: wechatUserInfo.Nickname,
|
Username: wechatUserInfo.Nickname,
|
||||||
DisplayName: wechatUserInfo.Nickname,
|
DisplayName: wechatUserInfo.Nickname,
|
||||||
AvatarUrl: wechatUserInfo.Headimgurl,
|
AvatarUrl: wechatUserInfo.Headimgurl,
|
||||||
|
Extra: extra,
|
||||||
}
|
}
|
||||||
return &userInfo, nil
|
return &userInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BuildWechatOpenIdKey(appId string) string {
|
||||||
|
return fmt.Sprintf("wechat_openid_%s", appId)
|
||||||
|
}
|
||||||
|
|
||||||
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
|
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
|
||||||
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", clientId, clientSecret)
|
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", clientId, clientSecret)
|
||||||
request, err := http.NewRequest("GET", accessTokenUrl, nil)
|
request, err := http.NewRequest("GET", accessTokenUrl, nil)
|
||||||
|
@@ -16,6 +16,7 @@ package ldap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash/fnv"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
@@ -49,20 +50,20 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
|
|
||||||
if r.AuthenticationChoice() == "simple" {
|
if r.AuthenticationChoice() == "simple" {
|
||||||
bindUsername, bindOrg, err := getNameAndOrgFromDN(string(r.Name()))
|
bindUsername, bindOrg, err := getNameAndOrgFromDN(string(r.Name()))
|
||||||
if err != "" {
|
if err != nil {
|
||||||
log.Printf("Bind failed ,ErrMsg=%s", err)
|
log.Printf("getNameAndOrgFromDN() error: %s", err.Error())
|
||||||
res.SetResultCode(ldap.LDAPResultInvalidDNSyntax)
|
res.SetResultCode(ldap.LDAPResultInvalidDNSyntax)
|
||||||
res.SetDiagnosticMessage("bind failed ErrMsg: " + err)
|
res.SetDiagnosticMessage(fmt.Sprintf("getNameAndOrgFromDN() error: %s", err.Error()))
|
||||||
w.Write(res)
|
w.Write(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bindPassword := string(r.AuthenticationSimple())
|
bindPassword := string(r.AuthenticationSimple())
|
||||||
bindUser, err := object.CheckUserPassword(bindOrg, bindUsername, bindPassword, "en")
|
bindUser, err := object.CheckUserPassword(bindOrg, bindUsername, bindPassword, "en")
|
||||||
if err != "" {
|
if err != nil {
|
||||||
log.Printf("Bind failed User=%s, Pass=%#v, ErrMsg=%s", string(r.Name()), r.Authentication(), err)
|
log.Printf("Bind failed User=%s, Pass=%#v, ErrMsg=%s", string(r.Name()), r.Authentication(), err)
|
||||||
res.SetResultCode(ldap.LDAPResultInvalidCredentials)
|
res.SetResultCode(ldap.LDAPResultInvalidCredentials)
|
||||||
res.SetDiagnosticMessage("invalid credentials ErrMsg: " + err)
|
res.SetDiagnosticMessage("invalid credentials ErrMsg: " + err.Error())
|
||||||
w.Write(res)
|
w.Write(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -78,7 +79,7 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
m.Client.OrgName = bindOrg
|
m.Client.OrgName = bindOrg
|
||||||
} else {
|
} else {
|
||||||
res.SetResultCode(ldap.LDAPResultAuthMethodNotSupported)
|
res.SetResultCode(ldap.LDAPResultAuthMethodNotSupported)
|
||||||
res.SetDiagnosticMessage("Authentication method not supported,Please use Simple Authentication")
|
res.SetDiagnosticMessage("Authentication method not supported, please use Simple Authentication")
|
||||||
}
|
}
|
||||||
w.Write(res)
|
w.Write(res)
|
||||||
}
|
}
|
||||||
@@ -113,10 +114,22 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
dn := fmt.Sprintf("cn=%s,%s", user.Name, string(r.BaseObject()))
|
dn := fmt.Sprintf("uid=%s,cn=%s,%s", user.Id, user.Name, string(r.BaseObject()))
|
||||||
e := ldap.NewSearchResultEntry(dn)
|
e := ldap.NewSearchResultEntry(dn)
|
||||||
|
uidNumberStr := fmt.Sprintf("%v", hash(user.Name))
|
||||||
for _, attr := range r.Attributes() {
|
e.AddAttribute("uidNumber", message.AttributeValue(uidNumberStr))
|
||||||
|
e.AddAttribute("gidNumber", message.AttributeValue(uidNumberStr))
|
||||||
|
e.AddAttribute("homeDirectory", message.AttributeValue("/home/"+user.Name))
|
||||||
|
e.AddAttribute("cn", message.AttributeValue(user.Name))
|
||||||
|
e.AddAttribute("uid", message.AttributeValue(user.Id))
|
||||||
|
attrs := r.Attributes()
|
||||||
|
for _, attr := range attrs {
|
||||||
|
if string(attr) == "*" {
|
||||||
|
attrs = AdditionalLdapAttributes
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, attr := range attrs {
|
||||||
e.AddAttribute(message.AttributeDescription(attr), getAttribute(string(attr), user))
|
e.AddAttribute(message.AttributeDescription(attr), getAttribute(string(attr), user))
|
||||||
if string(attr) == "cn" {
|
if string(attr) == "cn" {
|
||||||
e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user))
|
e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user))
|
||||||
@@ -127,3 +140,9 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
}
|
}
|
||||||
w.Write(res)
|
w.Write(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hash(s string) uint32 {
|
||||||
|
h := fnv.New32a()
|
||||||
|
h.Write([]byte(s))
|
||||||
|
return h.Sum32()
|
||||||
|
}
|
||||||
|
200
ldap/util.go
200
ldap/util.go
@@ -24,9 +24,73 @@ import (
|
|||||||
"github.com/lor00x/goldap/message"
|
"github.com/lor00x/goldap/message"
|
||||||
|
|
||||||
ldap "github.com/forestmgy/ldapserver"
|
ldap "github.com/forestmgy/ldapserver"
|
||||||
|
|
||||||
|
"github.com/xorm-io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getNameAndOrgFromDN(DN string) (string, string, string) {
|
type AttributeMapper func(user *object.User) message.AttributeValue
|
||||||
|
|
||||||
|
type FieldRelation struct {
|
||||||
|
userField string
|
||||||
|
notSearchable bool
|
||||||
|
hideOnStarOp bool
|
||||||
|
fieldMapper AttributeMapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rel FieldRelation) GetField() (string, error) {
|
||||||
|
if rel.notSearchable {
|
||||||
|
return "", fmt.Errorf("attribute %s not supported", rel.userField)
|
||||||
|
}
|
||||||
|
return rel.userField, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rel FieldRelation) GetAttributeValue(user *object.User) message.AttributeValue {
|
||||||
|
return rel.fieldMapper(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ldapAttributesMapping = map[string]FieldRelation{
|
||||||
|
"cn": {userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) message.AttributeValue {
|
||||||
|
return message.AttributeValue(user.Name)
|
||||||
|
}},
|
||||||
|
"uid": {userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) message.AttributeValue {
|
||||||
|
return message.AttributeValue(user.Name)
|
||||||
|
}},
|
||||||
|
"displayname": {userField: "displayName", fieldMapper: func(user *object.User) message.AttributeValue {
|
||||||
|
return message.AttributeValue(user.DisplayName)
|
||||||
|
}},
|
||||||
|
"email": {userField: "email", fieldMapper: func(user *object.User) message.AttributeValue {
|
||||||
|
return message.AttributeValue(user.Email)
|
||||||
|
}},
|
||||||
|
"mail": {userField: "email", fieldMapper: func(user *object.User) message.AttributeValue {
|
||||||
|
return message.AttributeValue(user.Email)
|
||||||
|
}},
|
||||||
|
"mobile": {userField: "phone", fieldMapper: func(user *object.User) message.AttributeValue {
|
||||||
|
return message.AttributeValue(user.Phone)
|
||||||
|
}},
|
||||||
|
"title": {userField: "tag", fieldMapper: func(user *object.User) message.AttributeValue {
|
||||||
|
return message.AttributeValue(user.Tag)
|
||||||
|
}},
|
||||||
|
"userPassword": {
|
||||||
|
userField: "userPassword",
|
||||||
|
notSearchable: true,
|
||||||
|
fieldMapper: func(user *object.User) message.AttributeValue {
|
||||||
|
return message.AttributeValue(getUserPasswordWithType(user))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var AdditionalLdapAttributes []message.LDAPString
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for k, v := range ldapAttributesMapping {
|
||||||
|
if v.hideOnStarOp {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
AdditionalLdapAttributes = append(AdditionalLdapAttributes, message.LDAPString(k))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNameAndOrgFromDN(DN string) (string, string, error) {
|
||||||
DNFields := strings.Split(DN, ",")
|
DNFields := strings.Split(DN, ",")
|
||||||
params := make(map[string]string, len(DNFields))
|
params := make(map[string]string, len(DNFields))
|
||||||
for _, field := range DNFields {
|
for _, field := range DNFields {
|
||||||
@@ -37,12 +101,12 @@ func getNameAndOrgFromDN(DN string) (string, string, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if params["cn"] == "" {
|
if params["cn"] == "" {
|
||||||
return "", "", "please use Admin Name format like cn=xxx,ou=xxx,dc=example,dc=com"
|
return "", "", fmt.Errorf("please use Admin Name format like cn=xxx,ou=xxx,dc=example,dc=com")
|
||||||
}
|
}
|
||||||
if params["ou"] == "" {
|
if params["ou"] == "" {
|
||||||
return params["cn"], object.CasdoorOrganization, ""
|
return params["cn"], object.CasdoorOrganization, nil
|
||||||
}
|
}
|
||||||
return params["cn"], params["ou"], ""
|
return params["cn"], params["ou"], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNameAndOrgFromFilter(baseDN, filter string) (string, string, int) {
|
func getNameAndOrgFromFilter(baseDN, filter string) (string, string, int) {
|
||||||
@@ -50,7 +114,11 @@ func getNameAndOrgFromFilter(baseDN, filter string) (string, string, int) {
|
|||||||
return "", "", ldap.LDAPResultInvalidDNSyntax
|
return "", "", ldap.LDAPResultInvalidDNSyntax
|
||||||
}
|
}
|
||||||
|
|
||||||
name, org, _ := getNameAndOrgFromDN(fmt.Sprintf("cn=%s,", getUsername(filter)) + baseDN)
|
name, org, err := getNameAndOrgFromDN(fmt.Sprintf("cn=%s,", getUsername(filter)) + baseDN)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
return name, org, ldap.LDAPResultSuccess
|
return name, org, ldap.LDAPResultSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +151,92 @@ func stringInSlice(value string, list []string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildUserFilterCondition(filter interface{}) (builder.Cond, error) {
|
||||||
|
switch f := filter.(type) {
|
||||||
|
case message.FilterAnd:
|
||||||
|
conditions := make([]builder.Cond, len(f))
|
||||||
|
for i, v := range f {
|
||||||
|
cond, err := buildUserFilterCondition(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conditions[i] = cond
|
||||||
|
}
|
||||||
|
return builder.And(conditions...), nil
|
||||||
|
case message.FilterOr:
|
||||||
|
conditions := make([]builder.Cond, len(f))
|
||||||
|
for i, v := range f {
|
||||||
|
cond, err := buildUserFilterCondition(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conditions[i] = cond
|
||||||
|
}
|
||||||
|
return builder.Or(conditions...), nil
|
||||||
|
case message.FilterNot:
|
||||||
|
cond, err := buildUserFilterCondition(f.Filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return builder.Not{cond}, nil
|
||||||
|
case message.FilterEqualityMatch:
|
||||||
|
field, err := getUserFieldFromAttribute(string(f.AttributeDesc()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return builder.Eq{field: string(f.AssertionValue())}, nil
|
||||||
|
case message.FilterPresent:
|
||||||
|
field, err := getUserFieldFromAttribute(string(f))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return builder.NotNull{field}, nil
|
||||||
|
case message.FilterGreaterOrEqual:
|
||||||
|
field, err := getUserFieldFromAttribute(string(f.AttributeDesc()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return builder.Gte{field: string(f.AssertionValue())}, nil
|
||||||
|
case message.FilterLessOrEqual:
|
||||||
|
field, err := getUserFieldFromAttribute(string(f.AttributeDesc()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return builder.Lte{field: string(f.AssertionValue())}, nil
|
||||||
|
case message.FilterSubstrings:
|
||||||
|
field, err := getUserFieldFromAttribute(string(f.Type_()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var expr string
|
||||||
|
for _, substring := range f.Substrings() {
|
||||||
|
switch s := substring.(type) {
|
||||||
|
case message.SubstringInitial:
|
||||||
|
expr += string(s) + "%"
|
||||||
|
continue
|
||||||
|
case message.SubstringAny:
|
||||||
|
expr += string(s) + "%"
|
||||||
|
continue
|
||||||
|
case message.SubstringFinal:
|
||||||
|
expr += string(s)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.Expr(field+" LIKE ?", expr), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("LDAP filter operation %#v not supported", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildSafeCondition(filter interface{}) builder.Cond {
|
||||||
|
condition, err := buildUserFilterCondition(filter)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("err = %v", err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return condition
|
||||||
|
}
|
||||||
|
|
||||||
func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int) {
|
func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int) {
|
||||||
var err error
|
var err error
|
||||||
r := m.GetSearchRequest()
|
r := m.GetSearchRequest()
|
||||||
@@ -94,15 +248,14 @@ func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int)
|
|||||||
|
|
||||||
if name == "*" && m.Client.IsOrgAdmin { // get all users from organization 'org'
|
if name == "*" && m.Client.IsOrgAdmin { // get all users from organization 'org'
|
||||||
if m.Client.IsGlobalAdmin && org == "*" {
|
if m.Client.IsGlobalAdmin && org == "*" {
|
||||||
|
filteredUsers, err = object.GetGlobalUsersWithFilter(buildSafeCondition(r.Filter()))
|
||||||
filteredUsers, err = object.GetGlobalUsers()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return filteredUsers, ldap.LDAPResultSuccess
|
return filteredUsers, ldap.LDAPResultSuccess
|
||||||
}
|
}
|
||||||
if m.Client.IsGlobalAdmin || org == m.Client.OrgName {
|
if m.Client.IsGlobalAdmin || org == m.Client.OrgName {
|
||||||
filteredUsers, err = object.GetUsers(org)
|
filteredUsers, err = object.GetUsersWithFilter(org, buildSafeCondition(r.Filter()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -144,7 +297,7 @@ func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int)
|
|||||||
return nil, ldap.LDAPResultNoSuchObject
|
return nil, ldap.LDAPResultNoSuchObject
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := object.GetUsersByTag(org, name)
|
users, err := object.GetUsersByTagWithFilter(org, name, buildSafeCondition(r.Filter()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -178,24 +331,17 @@ func getUserPasswordWithType(user *object.User) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getAttribute(attributeName string, user *object.User) message.AttributeValue {
|
func getAttribute(attributeName string, user *object.User) message.AttributeValue {
|
||||||
switch attributeName {
|
v, ok := ldapAttributesMapping[attributeName]
|
||||||
case "cn":
|
if !ok {
|
||||||
return message.AttributeValue(user.Name)
|
|
||||||
case "uid":
|
|
||||||
return message.AttributeValue(user.Name)
|
|
||||||
case "displayname":
|
|
||||||
return message.AttributeValue(user.DisplayName)
|
|
||||||
case "email":
|
|
||||||
return message.AttributeValue(user.Email)
|
|
||||||
case "mail":
|
|
||||||
return message.AttributeValue(user.Email)
|
|
||||||
case "mobile":
|
|
||||||
return message.AttributeValue(user.Phone)
|
|
||||||
case "title":
|
|
||||||
return message.AttributeValue(user.Tag)
|
|
||||||
case "userPassword":
|
|
||||||
return message.AttributeValue(getUserPasswordWithType(user))
|
|
||||||
default:
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
return v.GetAttributeValue(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserFieldFromAttribute(attributeName string) (string, error) {
|
||||||
|
v, ok := ldapAttributesMapping[attributeName]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("attribute %s not supported", attributeName)
|
||||||
|
}
|
||||||
|
return v.GetField()
|
||||||
}
|
}
|
||||||
|
87
ldap/util_test.go
Normal file
87
ldap/util_test.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
ber "github.com/go-asn1-ber/asn1-ber"
|
||||||
|
goldap "github.com/go-ldap/ldap/v3"
|
||||||
|
"github.com/lor00x/goldap/message"
|
||||||
|
"github.com/xorm-io/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
func args(exp ...interface{}) []interface{} {
|
||||||
|
return exp
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLdapFilterAsQuery(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
description string
|
||||||
|
input string
|
||||||
|
expectedExpr string
|
||||||
|
expectedArgs []interface{}
|
||||||
|
}{
|
||||||
|
{"Should be SQL for FilterAnd", "(&(mail=2)(email=1))", "email=? AND email=?", args("2", "1")},
|
||||||
|
{"Should be SQL for FilterOr", "(|(mail=2)(email=1))", "email=? OR email=?", args("2", "1")},
|
||||||
|
{"Should be SQL for FilterNot", "(!(mail=2))", "NOT email=?", args("2")},
|
||||||
|
{"Should be SQL for FilterEqualityMatch", "(mail=2)", "email=?", args("2")},
|
||||||
|
{"Should be SQL for FilterPresent", "(mail=*)", "email IS NOT NULL", nil},
|
||||||
|
{"Should be SQL for FilterGreaterOrEqual", "(mail>=admin)", "email>=?", args("admin")},
|
||||||
|
{"Should be SQL for FilterLessOrEqual", "(mail<=admin)", "email<=?", args("admin")},
|
||||||
|
{"Should be SQL for FilterSubstrings", "(mail=admin*ex*c*m)", "email LIKE ?", args("admin%ex%c%m")},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scenery := range scenarios {
|
||||||
|
t.Run(scenery.description, func(t *testing.T) {
|
||||||
|
searchRequest, err := buildLdapSearchRequest(scenery.input)
|
||||||
|
if err != nil {
|
||||||
|
assert.FailNow(t, "Unable to create searchRequest", err)
|
||||||
|
}
|
||||||
|
m, err := message.ReadLDAPMessage(message.NewBytes(0, searchRequest.Bytes()))
|
||||||
|
if err != nil {
|
||||||
|
assert.FailNow(t, "Unable to create searchRequest", err)
|
||||||
|
}
|
||||||
|
req := m.ProtocolOp().(message.SearchRequest)
|
||||||
|
|
||||||
|
cond, err := buildUserFilterCondition(req.Filter())
|
||||||
|
if err != nil {
|
||||||
|
assert.FailNow(t, "Unable to build condition", err)
|
||||||
|
}
|
||||||
|
expr, args, err := builder.ToSQL(cond)
|
||||||
|
if err != nil {
|
||||||
|
assert.FailNow(t, "Unable to build sql", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, scenery.expectedExpr, expr)
|
||||||
|
assert.Equal(t, scenery.expectedArgs, args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildLdapSearchRequest(filter string) (*ber.Packet, error) {
|
||||||
|
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
|
||||||
|
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 1, "MessageID"))
|
||||||
|
|
||||||
|
pkt := ber.Encode(ber.ClassApplication, ber.TypeConstructed, goldap.ApplicationSearchRequest, nil, "Search Request")
|
||||||
|
pkt.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "", "Base DN"))
|
||||||
|
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, 0, "Scope"))
|
||||||
|
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagEnumerated, 0, "Deref Aliases"))
|
||||||
|
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 0, "Size Limit"))
|
||||||
|
pkt.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, 0, "Time Limit"))
|
||||||
|
pkt.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, false, "Types Only"))
|
||||||
|
// compile and encode filter
|
||||||
|
filterPacket, err := goldap.CompileFilter(filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pkt.AppendChild(filterPacket)
|
||||||
|
// encode attributes
|
||||||
|
attributesPacket := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
|
||||||
|
attributesPacket.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, "*", "Attribute"))
|
||||||
|
pkt.AppendChild(attributesPacket)
|
||||||
|
|
||||||
|
packet.AppendChild(pkt)
|
||||||
|
|
||||||
|
return packet, nil
|
||||||
|
}
|
1
main.go
1
main.go
@@ -34,7 +34,6 @@ func main() {
|
|||||||
object.InitFlag()
|
object.InitFlag()
|
||||||
object.InitAdapter()
|
object.InitAdapter()
|
||||||
object.CreateTables()
|
object.CreateTables()
|
||||||
object.DoMigration()
|
|
||||||
|
|
||||||
object.InitDb()
|
object.InitDb()
|
||||||
object.InitFromFile()
|
object.InitFromFile()
|
||||||
|
@@ -319,6 +319,9 @@ func GetMaskedApplication(application *Application, userId string) *Application
|
|||||||
if application.OrganizationObj.DefaultPassword != "" {
|
if application.OrganizationObj.DefaultPassword != "" {
|
||||||
application.OrganizationObj.DefaultPassword = "***"
|
application.OrganizationObj.DefaultPassword = "***"
|
||||||
}
|
}
|
||||||
|
if application.OrganizationObj.MasterVerificationCode != "" {
|
||||||
|
application.OrganizationObj.MasterVerificationCode = "***"
|
||||||
|
}
|
||||||
if application.OrganizationObj.PasswordType != "" {
|
if application.OrganizationObj.PasswordType != "" {
|
||||||
application.OrganizationObj.PasswordType = "***"
|
application.OrganizationObj.PasswordType = "***"
|
||||||
}
|
}
|
||||||
@@ -345,6 +348,34 @@ func GetMaskedApplications(applications []*Application, userId string) []*Applic
|
|||||||
return applications
|
return applications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAllowedApplications(applications []*Application, userId string) ([]*Application, error) {
|
||||||
|
if userId == "" || isUserIdGlobalAdmin(userId) {
|
||||||
|
return applications, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := GetUser(userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if user != nil && user.IsAdmin {
|
||||||
|
return applications, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
res := []*Application{}
|
||||||
|
for _, application := range applications {
|
||||||
|
var allowed bool
|
||||||
|
allowed, err = CheckLoginPermission(userId, application)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowed {
|
||||||
|
res = append(res, application)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateApplication(id string, application *Application) (bool, error) {
|
func UpdateApplication(id string, application *Application) (bool, error) {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
oldApplication, err := getApplication(owner, name)
|
oldApplication, err := getApplication(owner, name)
|
||||||
|
@@ -87,7 +87,7 @@ func GetGlobalCertsCount(field, value string) (int64, error) {
|
|||||||
return session.Count(&Cert{})
|
return session.Count(&Cert{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGlobleCerts() ([]*Cert, error) {
|
func GetGlobalCerts() ([]*Cert, error) {
|
||||||
certs := []*Cert{}
|
certs := []*Cert{}
|
||||||
err := ormer.Engine.Desc("created_time").Find(&certs)
|
err := ormer.Engine.Desc("created_time").Find(&certs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -163,6 +163,12 @@ func UpdateCert(id string, cert *Cert) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := cert.populateContent()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
|
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@@ -172,10 +178,9 @@ func UpdateCert(id string, cert *Cert) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AddCert(cert *Cert) (bool, error) {
|
func AddCert(cert *Cert) (bool, error) {
|
||||||
if cert.Certificate == "" || cert.PrivateKey == "" {
|
err := cert.populateContent()
|
||||||
certificate, privateKey := generateRsaKeys(cert.BitSize, cert.ExpireInYears, cert.Name, cert.Owner)
|
if err != nil {
|
||||||
cert.Certificate = certificate
|
return false, err
|
||||||
cert.PrivateKey = privateKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := ormer.Engine.Insert(cert)
|
affected, err := ormer.Engine.Insert(cert)
|
||||||
@@ -199,6 +204,20 @@ func (p *Cert) GetId() string {
|
|||||||
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Cert) populateContent() error {
|
||||||
|
if p.Certificate == "" || p.PrivateKey == "" {
|
||||||
|
certificate, privateKey, err := generateRsaKeys(p.BitSize, p.ExpireInYears, p.Name, p.Owner)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Certificate = certificate
|
||||||
|
p.PrivateKey = privateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getCertByApplication(application *Application) (*Cert, error) {
|
func getCertByApplication(application *Application) (*Cert, error) {
|
||||||
if application.Cert != "" {
|
if application.Cert != "" {
|
||||||
return getCertByName(application.Cert)
|
return getCertByName(application.Cert)
|
||||||
|
@@ -142,7 +142,7 @@ func CheckUserSignup(application *Application, organization *Organization, form
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkSigninErrorTimes(user *User, lang string) string {
|
func checkSigninErrorTimes(user *User, lang string) error {
|
||||||
if user.SigninWrongTimes >= SigninWrongTimesLimit {
|
if user.SigninWrongTimes >= SigninWrongTimesLimit {
|
||||||
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
|
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
|
||||||
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
|
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
|
||||||
@@ -150,37 +150,39 @@ func checkSigninErrorTimes(user *User, lang string) string {
|
|||||||
|
|
||||||
// deny the login if the error times is greater than the limit and the last login time is less than the duration
|
// deny the login if the error times is greater than the limit and the last login time is less than the duration
|
||||||
if minutes > 0 {
|
if minutes > 0 {
|
||||||
return fmt.Sprintf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), minutes)
|
return fmt.Errorf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), minutes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset the error times
|
// reset the error times
|
||||||
user.SigninWrongTimes = 0
|
user.SigninWrongTimes = 0
|
||||||
|
|
||||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, false)
|
_, err := UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, false)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckPassword(user *User, password string, lang string, options ...bool) string {
|
func CheckPassword(user *User, password string, lang string, options ...bool) error {
|
||||||
enableCaptcha := false
|
enableCaptcha := false
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
enableCaptcha = options[0]
|
enableCaptcha = options[0]
|
||||||
}
|
}
|
||||||
// check the login error times
|
// check the login error times
|
||||||
if !enableCaptcha {
|
if !enableCaptcha {
|
||||||
if msg := checkSigninErrorTimes(user, lang); msg != "" {
|
err := checkSigninErrorTimes(user, lang)
|
||||||
return msg
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
organization, err := GetOrganizationByUser(user)
|
organization, err := GetOrganizationByUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if organization == nil {
|
if organization == nil {
|
||||||
return i18n.Translate(lang, "check:Organization does not exist")
|
return fmt.Errorf(i18n.Translate(lang, "check:Organization does not exist"))
|
||||||
}
|
}
|
||||||
|
|
||||||
passwordType := user.PasswordType
|
passwordType := user.PasswordType
|
||||||
@@ -191,19 +193,17 @@ func CheckPassword(user *User, password string, lang string, options ...bool) st
|
|||||||
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 resetUserSigninErrorTimes(user)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) {
|
if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) {
|
||||||
resetUserSigninErrorTimes(user)
|
return resetUserSigninErrorTimes(user)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return recordSigninErrorInfo(user, lang, enableCaptcha)
|
return recordSigninErrorInfo(user, lang, enableCaptcha)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf(i18n.Translate(lang, "check:unsupported password type: %s"), organization.PasswordType)
|
return fmt.Errorf(i18n.Translate(lang, "check:unsupported password type: %s"), organization.PasswordType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,10 +217,10 @@ func CheckPasswordComplexity(user *User, password string) string {
|
|||||||
return CheckPasswordComplexityByOrg(organization, password)
|
return CheckPasswordComplexityByOrg(organization, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkLdapUserPassword(user *User, password string, lang string) string {
|
func checkLdapUserPassword(user *User, password string, lang string) error {
|
||||||
ldaps, err := GetLdaps(user.Owner)
|
ldaps, err := GetLdaps(user.Owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Error()
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapLoginSuccess := false
|
ldapLoginSuccess := false
|
||||||
@@ -237,65 +237,73 @@ func checkLdapUserPassword(user *User, password string, lang string) string {
|
|||||||
|
|
||||||
searchResult, err := conn.Conn.Search(searchReq)
|
searchResult, err := conn.Conn.Search(searchReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Error()
|
conn.Close()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(searchResult.Entries) == 0 {
|
if len(searchResult.Entries) == 0 {
|
||||||
|
conn.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(searchResult.Entries) > 1 {
|
if len(searchResult.Entries) > 1 {
|
||||||
return i18n.Translate(lang, "check:Multiple accounts with same uid, please check your ldap server")
|
conn.Close()
|
||||||
|
return fmt.Errorf(i18n.Translate(lang, "check:Multiple accounts with same uid, please check your ldap server"))
|
||||||
}
|
}
|
||||||
|
|
||||||
hit = true
|
hit = true
|
||||||
dn := searchResult.Entries[0].DN
|
dn := searchResult.Entries[0].DN
|
||||||
if err := conn.Conn.Bind(dn, password); err == nil {
|
if err = conn.Conn.Bind(dn, password); err == nil {
|
||||||
ldapLoginSuccess = true
|
ldapLoginSuccess = true
|
||||||
|
conn.Close()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ldapLoginSuccess {
|
if !ldapLoginSuccess {
|
||||||
if !hit {
|
if !hit {
|
||||||
return "user not exist"
|
return fmt.Errorf("user not exist")
|
||||||
}
|
}
|
||||||
return i18n.Translate(lang, "check:LDAP user name or password incorrect")
|
return fmt.Errorf(i18n.Translate(lang, "check:LDAP user name or password incorrect"))
|
||||||
}
|
}
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckUserPassword(organization string, username string, password string, lang string, options ...bool) (*User, string) {
|
func CheckUserPassword(organization string, username string, password string, lang string, options ...bool) (*User, error) {
|
||||||
enableCaptcha := false
|
enableCaptcha := false
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
enableCaptcha = options[0]
|
enableCaptcha = options[0]
|
||||||
}
|
}
|
||||||
user, err := GetUserByFields(organization, username)
|
user, err := GetUserByFields(organization, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user == nil || user.IsDeleted {
|
if user == nil || user.IsDeleted {
|
||||||
return nil, fmt.Sprintf(i18n.Translate(lang, "general:The user: %s doesn't exist"), util.GetId(organization, username))
|
return nil, fmt.Errorf(i18n.Translate(lang, "general:The user: %s doesn't exist"), util.GetId(organization, username))
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.IsForbidden {
|
if user.IsForbidden {
|
||||||
return nil, i18n.Translate(lang, "check:The user is forbidden to sign in, please contact the administrator")
|
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user is forbidden to sign in, please contact the administrator"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.Ldap != "" {
|
if user.Ldap != "" {
|
||||||
// ONLY for ldap users
|
// only for LDAP users
|
||||||
if msg := checkLdapUserPassword(user, password, lang); msg != "" {
|
err = checkLdapUserPassword(user, password, lang)
|
||||||
if msg == "user not exist" {
|
if err != nil {
|
||||||
return nil, fmt.Sprintf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
|
if err.Error() == "user not exist" {
|
||||||
|
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
|
||||||
}
|
}
|
||||||
return nil, msg
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if msg := CheckPassword(user, password, lang, enableCaptcha); msg != "" {
|
err = CheckPassword(user, password, lang, enableCaptcha)
|
||||||
return nil, msg
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return user, ""
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckUserPermission(requestUserId, userId string, strict bool, lang string) (bool, error) {
|
func CheckUserPermission(requestUserId, userId string, strict bool, lang string) (bool, error) {
|
||||||
@@ -308,7 +316,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool, lang string)
|
|||||||
if userId != "" {
|
if userId != "" {
|
||||||
targetUser, err := GetUser(userId)
|
targetUser, err := GetUser(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if targetUser == nil {
|
if targetUser == nil {
|
||||||
@@ -366,7 +374,7 @@ func CheckLoginPermission(userId string, application *Application) (bool, error)
|
|||||||
allowCount := 0
|
allowCount := 0
|
||||||
denyCount := 0
|
denyCount := 0
|
||||||
for _, permission := range permissions {
|
for _, permission := range permissions {
|
||||||
if !permission.IsEnabled || permission.ResourceType != "Application" || !permission.isResourceHit(application.Name) {
|
if !permission.IsEnabled || permission.State != "Approved" || permission.ResourceType != "Application" || !permission.isResourceHit(application.Name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -36,20 +36,23 @@ func isValidRealName(s string) bool {
|
|||||||
return reRealName.MatchString(s)
|
return reRealName.MatchString(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetUserSigninErrorTimes(user *User) {
|
func resetUserSigninErrorTimes(user *User) error {
|
||||||
// if the password is correct and wrong times is not zero, reset the error times
|
// if the password is correct and wrong times is not zero, reset the error times
|
||||||
if user.SigninWrongTimes == 0 {
|
if user.SigninWrongTimes == 0 {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
user.SigninWrongTimes = 0
|
user.SigninWrongTimes = 0
|
||||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
|
_, err := UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordSigninErrorInfo(user *User, lang string, options ...bool) string {
|
func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
|
||||||
enableCaptcha := false
|
enableCaptcha := false
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
enableCaptcha = options[0]
|
enableCaptcha = options[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// increase failed login count
|
// increase failed login count
|
||||||
if user.SigninWrongTimes < SigninWrongTimesLimit {
|
if user.SigninWrongTimes < SigninWrongTimesLimit {
|
||||||
user.SigninWrongTimes++
|
user.SigninWrongTimes++
|
||||||
@@ -61,13 +64,18 @@ func recordSigninErrorInfo(user *User, lang string, options ...bool) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update user
|
// update user
|
||||||
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
|
_, err := UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
|
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
|
||||||
if leftChances == 0 && enableCaptcha {
|
if leftChances == 0 && enableCaptcha {
|
||||||
return fmt.Sprint(i18n.Translate(lang, "check:password or code is incorrect"))
|
return fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect"))
|
||||||
} else if leftChances >= 0 {
|
} else if leftChances >= 0 {
|
||||||
return fmt.Sprintf(i18n.Translate(lang, "check:password or code is incorrect, you have %d remaining chances"), leftChances)
|
return fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect, you have %d remaining chances"), leftChances)
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't show the chance error message if the user has no chance left
|
// don't show the chance error message if the user has no chance left
|
||||||
return fmt.Sprintf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), int(LastSignWrongTimeDuration.Minutes()))
|
return fmt.Errorf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), int(LastSignWrongTimeDuration.Minutes()))
|
||||||
}
|
}
|
||||||
|
@@ -396,15 +396,22 @@ func initBuiltInPermission() {
|
|||||||
Name: "permission-built-in",
|
Name: "permission-built-in",
|
||||||
CreatedTime: util.GetCurrentTime(),
|
CreatedTime: util.GetCurrentTime(),
|
||||||
DisplayName: "Built-in Permission",
|
DisplayName: "Built-in Permission",
|
||||||
|
Description: "Built-in Permission",
|
||||||
Users: []string{"built-in/*"},
|
Users: []string{"built-in/*"},
|
||||||
|
Groups: []string{},
|
||||||
Roles: []string{},
|
Roles: []string{},
|
||||||
Domains: []string{},
|
Domains: []string{},
|
||||||
Model: "model-built-in",
|
Model: "model-built-in",
|
||||||
|
Adapter: "",
|
||||||
ResourceType: "Application",
|
ResourceType: "Application",
|
||||||
Resources: []string{"app-built-in"},
|
Resources: []string{"app-built-in"},
|
||||||
Actions: []string{"Read", "Write", "Admin"},
|
Actions: []string{"Read", "Write", "Admin"},
|
||||||
Effect: "Allow",
|
Effect: "Allow",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
|
Submitter: "admin",
|
||||||
|
Approver: "admin",
|
||||||
|
ApproveTime: util.GetCurrentTime(),
|
||||||
|
State: "Approved",
|
||||||
}
|
}
|
||||||
_, err = AddPermission(permission)
|
_, err = AddPermission(permission)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -100,6 +100,7 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) e
|
|||||||
|
|
||||||
users, err := conn.GetLdapUsers(ldap)
|
users, err := conn.GetLdapUsers(ldap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
|
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -111,6 +112,8 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) e
|
|||||||
} else {
|
} else {
|
||||||
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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -81,6 +81,17 @@ func (ldap *Ldap) GetLdapConn() (c *LdapConn, err error) {
|
|||||||
return &LdapConn{Conn: conn, IsAD: isAD}, nil
|
return &LdapConn{Conn: conn, IsAD: isAD}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *LdapConn) Close() {
|
||||||
|
if l.Conn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.Conn.Unbind()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func isMicrosoftAD(Conn *goldap.Conn) (bool, error) {
|
func isMicrosoftAD(Conn *goldap.Conn) (bool, error) {
|
||||||
SearchFilter := "(objectClass=*)"
|
SearchFilter := "(objectClass=*)"
|
||||||
SearchAttributes := []string{"vendorname", "vendorversion", "isGlobalCatalogReady", "forestFunctionality"}
|
SearchAttributes := []string{"vendorname", "vendorversion", "isGlobalCatalogReady", "forestFunctionality"}
|
||||||
@@ -305,7 +316,7 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
name, err := syncUser.buildLdapUserName()
|
name, err := syncUser.buildLdapUserName(owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -354,10 +365,10 @@ func GetExistUuids(owner string, uuids []string) ([]string, error) {
|
|||||||
return existUuids, nil
|
return existUuids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ldapUser *LdapUser) buildLdapUserName() (string, error) {
|
func (ldapUser *LdapUser) buildLdapUserName(owner string) (string, error) {
|
||||||
user := User{}
|
user := User{}
|
||||||
uidWithNumber := fmt.Sprintf("%s_%s", ldapUser.Uid, ldapUser.UidNumber)
|
uidWithNumber := fmt.Sprintf("%s_%s", ldapUser.Uid, ldapUser.UidNumber)
|
||||||
has, err := ormer.Engine.Where("name = ? or name = ?", ldapUser.Uid, uidWithNumber).Get(&user)
|
has, err := ormer.Engine.Where("owner = ? and (name = ? or name = ?)", owner, ldapUser.Uid, uidWithNumber).Get(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@@ -1,51 +0,0 @@
|
|||||||
// Copyright 2023 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 "github.com/xorm-io/xorm/migrate"
|
|
||||||
|
|
||||||
type Migrator interface {
|
|
||||||
IsMigrationNeeded() bool
|
|
||||||
DoMigration() *migrate.Migration
|
|
||||||
}
|
|
||||||
|
|
||||||
func DoMigration() {
|
|
||||||
migrators := []Migrator{
|
|
||||||
&Migrator_1_101_0_PR_1083{},
|
|
||||||
&Migrator_1_235_0_PR_1530{},
|
|
||||||
&Migrator_1_240_0_PR_1539{},
|
|
||||||
&Migrator_1_314_0_PR_1841{},
|
|
||||||
// more migrators add here in chronological order...
|
|
||||||
}
|
|
||||||
|
|
||||||
migrations := []*migrate.Migration{}
|
|
||||||
|
|
||||||
for _, migrator := range migrators {
|
|
||||||
if migrator.IsMigrationNeeded() {
|
|
||||||
migrations = append(migrations, migrator.DoMigration())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
options := &migrate.Options{
|
|
||||||
TableName: "migration",
|
|
||||||
IDColumnName: "id",
|
|
||||||
}
|
|
||||||
|
|
||||||
m := migrate.New(ormer.Engine, options, migrations)
|
|
||||||
err := m.Migrate()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,70 +0,0 @@
|
|||||||
// Copyright 2023 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/xorm-io/xorm"
|
|
||||||
"github.com/xorm-io/xorm/migrate"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Migrator_1_101_0_PR_1083 struct{}
|
|
||||||
|
|
||||||
func (*Migrator_1_101_0_PR_1083) IsMigrationNeeded() bool {
|
|
||||||
exist1, _ := ormer.Engine.IsTableExist("model")
|
|
||||||
exist2, _ := ormer.Engine.IsTableExist("permission")
|
|
||||||
exist3, _ := ormer.Engine.IsTableExist("permission_rule")
|
|
||||||
|
|
||||||
if exist1 && exist2 && exist3 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Migrator_1_101_0_PR_1083) DoMigration() *migrate.Migration {
|
|
||||||
migration := migrate.Migration{
|
|
||||||
ID: "20230209MigratePermissionRule--Use V5 instead of V1 to store permissionID",
|
|
||||||
Migrate: func(engine *xorm.Engine) error {
|
|
||||||
models := []*Model{}
|
|
||||||
err := engine.Table("model").Find(&models, &Model{})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
isHit := false
|
|
||||||
for _, model := range models {
|
|
||||||
if strings.Contains(model.ModelText, "permission") {
|
|
||||||
// update model table
|
|
||||||
model.ModelText = strings.Replace(model.ModelText, "permission,", "", -1)
|
|
||||||
UpdateModel(model.GetId(), model)
|
|
||||||
isHit = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isHit {
|
|
||||||
// update permission_rule table
|
|
||||||
sql := "UPDATE `permission_rule`SET V0 = V1, V1 = V2, V2 = V3, V3 = V4, V4 = V5 WHERE V0 IN (SELECT CONCAT(owner, '/', name) AS permission_id FROM `permission`)"
|
|
||||||
_, err = engine.Exec(sql)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return &migration
|
|
||||||
}
|
|
@@ -1,46 +0,0 @@
|
|||||||
// Copyright 2023 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 (
|
|
||||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
|
||||||
"github.com/xorm-io/xorm"
|
|
||||||
"github.com/xorm-io/xorm/migrate"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Migrator_1_235_0_PR_1530 struct{}
|
|
||||||
|
|
||||||
func (*Migrator_1_235_0_PR_1530) IsMigrationNeeded() bool {
|
|
||||||
exist, _ := ormer.Engine.IsTableExist("casbin_rule")
|
|
||||||
|
|
||||||
return exist
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Migrator_1_235_0_PR_1530) DoMigration() *migrate.Migration {
|
|
||||||
migration := migrate.Migration{
|
|
||||||
ID: "20221015CasbinRule--fill ptype field with p",
|
|
||||||
Migrate: func(engine *xorm.Engine) error {
|
|
||||||
_, err := engine.Cols("ptype").Update(&xormadapter.CasbinRule{
|
|
||||||
Ptype: "p",
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
},
|
|
||||||
Rollback: func(engine *xorm.Engine) error {
|
|
||||||
return engine.DropTables(&xormadapter.CasbinRule{})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return &migration
|
|
||||||
}
|
|
@@ -1,141 +0,0 @@
|
|||||||
// Copyright 2023 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 (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/xorm-io/xorm"
|
|
||||||
"github.com/xorm-io/xorm/migrate"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Migrator_1_240_0_PR_1539 struct{}
|
|
||||||
|
|
||||||
func (*Migrator_1_240_0_PR_1539) IsMigrationNeeded() bool {
|
|
||||||
exist, _ := ormer.Engine.IsTableExist("session")
|
|
||||||
err := ormer.Engine.Table("session").Find(&[]*Session{})
|
|
||||||
|
|
||||||
if exist && err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Migrator_1_240_0_PR_1539) DoMigration() *migrate.Migration {
|
|
||||||
migration := migrate.Migration{
|
|
||||||
ID: "20230211MigrateSession--Create a new field 'application' for table `session`",
|
|
||||||
Migrate: func(engine *xorm.Engine) error {
|
|
||||||
if alreadyCreated, _ := engine.IsTableExist("session_tmp"); alreadyCreated {
|
|
||||||
return errors.New("there is already a table called 'session_tmp', please rename or delete it for casdoor version migration and restart")
|
|
||||||
}
|
|
||||||
|
|
||||||
type oldSession struct {
|
|
||||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
|
||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
|
||||||
|
|
||||||
SessionId []string `json:"sessionId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
tx := engine.NewSession()
|
|
||||||
|
|
||||||
defer tx.Close()
|
|
||||||
|
|
||||||
err := tx.Begin()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.Table("session_tmp").CreateTable(&Session{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
oldSessions := []*oldSession{}
|
|
||||||
newSessions := []*Session{}
|
|
||||||
|
|
||||||
err = tx.Table("session").Find(&oldSessions)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, oldSession := range oldSessions {
|
|
||||||
newApplication := "null"
|
|
||||||
if oldSession.Owner == "built-in" {
|
|
||||||
newApplication = "app-built-in"
|
|
||||||
}
|
|
||||||
newSessions = append(newSessions, &Session{
|
|
||||||
Owner: oldSession.Owner,
|
|
||||||
Name: oldSession.Name,
|
|
||||||
Application: newApplication,
|
|
||||||
CreatedTime: oldSession.CreatedTime,
|
|
||||||
SessionId: oldSession.SessionId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
rollbackFlag := false
|
|
||||||
_, err = tx.Table("session_tmp").Insert(newSessions)
|
|
||||||
count1, _ := tx.Table("session_tmp").Count()
|
|
||||||
count2, _ := tx.Table("session").Count()
|
|
||||||
|
|
||||||
if err != nil || count1 != count2 {
|
|
||||||
rollbackFlag = true
|
|
||||||
}
|
|
||||||
|
|
||||||
delete := &Session{
|
|
||||||
Application: "null",
|
|
||||||
}
|
|
||||||
_, err = tx.Table("session_tmp").Delete(*delete)
|
|
||||||
if err != nil {
|
|
||||||
rollbackFlag = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if rollbackFlag {
|
|
||||||
tx.DropTable("session_tmp")
|
|
||||||
return errors.New("there is something wrong with data migration for table `session`, if there is a table called `session_tmp` not created by you in casdoor, please drop it, then restart anyhow")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.DropTable("session")
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("fail to drop table `session` for casdoor, please drop it and rename the table `session_tmp` to `session` manually and restart")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Already drop table `session`
|
|
||||||
// Can't find an api from xorm for altering table name
|
|
||||||
err = tx.Table("session").CreateTable(&Session{})
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("there is something wrong with data migration for table `session`, please restart")
|
|
||||||
}
|
|
||||||
|
|
||||||
sessions := []*Session{}
|
|
||||||
tx.Table("session_tmp").Find(&sessions)
|
|
||||||
_, err = tx.Table("session").Insert(sessions)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("there is something wrong with data migration for table `session`, please drop table `session` and rename table `session_tmp` to `session` and restart")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.DropTable("session_tmp")
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("fail to drop table `session_tmp` for casdoor, please drop it manually and restart")
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.Commit()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return &migration
|
|
||||||
}
|
|
@@ -1,68 +0,0 @@
|
|||||||
// Copyright 2023 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 (
|
|
||||||
"github.com/xorm-io/xorm"
|
|
||||||
"github.com/xorm-io/xorm/migrate"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Migrator_1_314_0_PR_1841 struct{}
|
|
||||||
|
|
||||||
func (*Migrator_1_314_0_PR_1841) IsMigrationNeeded() bool {
|
|
||||||
count, err := ormer.Engine.Where("password_type=?", "").Count(&User{})
|
|
||||||
if err != nil {
|
|
||||||
// table doesn't exist
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return count > 100
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Migrator_1_314_0_PR_1841) DoMigration() *migrate.Migration {
|
|
||||||
migration := migrate.Migration{
|
|
||||||
ID: "20230515MigrateUser--Create a new field 'passwordType' for table `user`",
|
|
||||||
Migrate: func(engine *xorm.Engine) error {
|
|
||||||
tx := engine.NewSession()
|
|
||||||
|
|
||||||
defer tx.Close()
|
|
||||||
|
|
||||||
err := tx.Begin()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
organizations := []*Organization{}
|
|
||||||
err = tx.Table("organization").Find(&organizations)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, organization := range organizations {
|
|
||||||
user := &User{PasswordType: organization.PasswordType}
|
|
||||||
_, err = tx.Where("owner = ?", organization.Name).Cols("password_type").Update(user)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.Commit()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return &migration
|
|
||||||
}
|
|
@@ -51,23 +51,24 @@ type Organization struct {
|
|||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
|
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
|
||||||
Favicon string `xorm:"varchar(100)" json:"favicon"`
|
Favicon string `xorm:"varchar(100)" json:"favicon"`
|
||||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||||
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||||
PasswordOptions []string `xorm:"varchar(100)" json:"passwordOptions"`
|
PasswordOptions []string `xorm:"varchar(100)" json:"passwordOptions"`
|
||||||
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
|
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
|
||||||
DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"`
|
DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"`
|
||||||
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
||||||
Tags []string `xorm:"mediumtext" json:"tags"`
|
Tags []string `xorm:"mediumtext" json:"tags"`
|
||||||
Languages []string `xorm:"varchar(255)" json:"languages"`
|
Languages []string `xorm:"varchar(255)" json:"languages"`
|
||||||
ThemeData *ThemeData `xorm:"json" json:"themeData"`
|
ThemeData *ThemeData `xorm:"json" json:"themeData"`
|
||||||
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
|
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
|
||||||
DefaultPassword string `xorm:"varchar(100)" json:"defaultPassword"`
|
DefaultPassword string `xorm:"varchar(100)" json:"defaultPassword"`
|
||||||
InitScore int `json:"initScore"`
|
MasterVerificationCode string `xorm:"varchar(100)" json:"masterVerificationCode"`
|
||||||
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
InitScore int `json:"initScore"`
|
||||||
IsProfilePublic bool `json:"isProfilePublic"`
|
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
||||||
|
IsProfilePublic bool `json:"isProfilePublic"`
|
||||||
|
|
||||||
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
|
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
|
||||||
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`
|
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`
|
||||||
@@ -159,6 +160,9 @@ func GetMaskedOrganization(organization *Organization, errs ...error) (*Organiza
|
|||||||
if organization.DefaultPassword != "" {
|
if organization.DefaultPassword != "" {
|
||||||
organization.DefaultPassword = "***"
|
organization.DefaultPassword = "***"
|
||||||
}
|
}
|
||||||
|
if organization.MasterVerificationCode != "" {
|
||||||
|
organization.MasterVerificationCode = "***"
|
||||||
|
}
|
||||||
return organization, nil
|
return organization, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,6 +217,9 @@ func UpdateOrganization(id string, organization *Organization) (bool, error) {
|
|||||||
if organization.DefaultPassword == "***" {
|
if organization.DefaultPassword == "***" {
|
||||||
session.Omit("default_password")
|
session.Omit("default_password")
|
||||||
}
|
}
|
||||||
|
if organization.MasterVerificationCode == "***" {
|
||||||
|
session.Omit("master_verification_code")
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := session.Update(organization)
|
affected, err := session.Update(organization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -64,7 +64,6 @@ func InitConfig() {
|
|||||||
|
|
||||||
InitAdapter()
|
InitAdapter()
|
||||||
CreateTables()
|
CreateTables()
|
||||||
DoMigration()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitAdapter() {
|
func InitAdapter() {
|
||||||
@@ -330,11 +329,6 @@ func (a *Ormer) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(PermissionRule))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(xormadapter.CasbinRule))
|
err = a.Engine.Sync2(new(xormadapter.CasbinRule))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@@ -49,17 +49,6 @@ type Permission struct {
|
|||||||
State string `xorm:"varchar(100)" json:"state"`
|
State string `xorm:"varchar(100)" json:"state"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PermissionRule struct {
|
|
||||||
Ptype string `xorm:"varchar(100) index not null default ''" json:"ptype"`
|
|
||||||
V0 string `xorm:"varchar(100) index not null default ''" json:"v0"`
|
|
||||||
V1 string `xorm:"varchar(100) index not null default ''" json:"v1"`
|
|
||||||
V2 string `xorm:"varchar(100) index not null default ''" json:"v2"`
|
|
||||||
V3 string `xorm:"varchar(100) index not null default ''" json:"v3"`
|
|
||||||
V4 string `xorm:"varchar(100) index not null default ''" json:"v4"`
|
|
||||||
V5 string `xorm:"varchar(100) index not null default ''" json:"v5"`
|
|
||||||
Id string `xorm:"varchar(100) index not null default ''" json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
const builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
|
const builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
|
||||||
|
|
||||||
func GetPermissionCount(owner, field, value string) (int64, error) {
|
func GetPermissionCount(owner, field, value string) (int64, error) {
|
||||||
@@ -131,7 +120,11 @@ func checkPermissionValid(permission *Permission) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
groupingPolicies := getGroupingPolicies(permission)
|
groupingPolicies, err := getGroupingPolicies(permission)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if len(groupingPolicies) > 0 {
|
if len(groupingPolicies) > 0 {
|
||||||
_, err = enforcer.AddGroupingPolicies(groupingPolicies)
|
_, err = enforcer.AddGroupingPolicies(groupingPolicies)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/casbin/casbin/v2/log"
|
"github.com/casbin/casbin/v2/log"
|
||||||
"github.com/casbin/casbin/v2/model"
|
"github.com/casbin/casbin/v2/model"
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -137,6 +138,16 @@ func getPolicies(permission *Permission) [][]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getRolesInRole(roleId string, visited map[string]struct{}) ([]*Role, error) {
|
func getRolesInRole(roleId string, visited map[string]struct{}) ([]*Role, error) {
|
||||||
|
roleOwner, roleName := util.GetOwnerAndNameFromId(roleId)
|
||||||
|
if roleName == "*" {
|
||||||
|
roles, err := GetRoles(roleOwner)
|
||||||
|
if err != nil {
|
||||||
|
return []*Role{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return roles, nil
|
||||||
|
}
|
||||||
|
|
||||||
role, err := GetRole(roleId)
|
role, err := GetRole(roleId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*Role{}, err
|
return []*Role{}, err
|
||||||
@@ -162,7 +173,7 @@ func getRolesInRole(roleId string, visited map[string]struct{}) ([]*Role, error)
|
|||||||
return roles, nil
|
return roles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGroupingPolicies(permission *Permission) [][]string {
|
func getGroupingPolicies(permission *Permission) ([][]string, error) {
|
||||||
var groupingPolicies [][]string
|
var groupingPolicies [][]string
|
||||||
|
|
||||||
domainExist := len(permission.Domains) > 0
|
domainExist := len(permission.Domains) > 0
|
||||||
@@ -170,12 +181,18 @@ func getGroupingPolicies(permission *Permission) [][]string {
|
|||||||
|
|
||||||
for _, roleId := range permission.Roles {
|
for _, roleId := range permission.Roles {
|
||||||
visited := map[string]struct{}{}
|
visited := map[string]struct{}{}
|
||||||
|
|
||||||
|
if roleId == "*" {
|
||||||
|
roleId = util.GetId(permission.Owner, "*")
|
||||||
|
}
|
||||||
|
|
||||||
rolesInRole, err := getRolesInRole(roleId, visited)
|
rolesInRole, err := getRolesInRole(roleId, visited)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, role := range rolesInRole {
|
for _, role := range rolesInRole {
|
||||||
roleId := role.GetId()
|
roleId = role.GetId()
|
||||||
for _, subUser := range role.Users {
|
for _, subUser := range role.Users {
|
||||||
if domainExist {
|
if domainExist {
|
||||||
for _, domain := range permission.Domains {
|
for _, domain := range permission.Domains {
|
||||||
@@ -198,7 +215,7 @@ func getGroupingPolicies(permission *Permission) [][]string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupingPolicies
|
return groupingPolicies, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addPolicies(permission *Permission) error {
|
func addPolicies(permission *Permission) error {
|
||||||
@@ -231,7 +248,10 @@ func addGroupingPolicies(permission *Permission) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
groupingPolicies := getGroupingPolicies(permission)
|
groupingPolicies, err := getGroupingPolicies(permission)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if len(groupingPolicies) > 0 {
|
if len(groupingPolicies) > 0 {
|
||||||
_, err = enforcer.AddGroupingPolicies(groupingPolicies)
|
_, err = enforcer.AddGroupingPolicies(groupingPolicies)
|
||||||
@@ -249,7 +269,10 @@ func removeGroupingPolicies(permission *Permission) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
groupingPolicies := getGroupingPolicies(permission)
|
groupingPolicies, err := getGroupingPolicies(permission)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if len(groupingPolicies) > 0 {
|
if len(groupingPolicies) > 0 {
|
||||||
_, err = enforcer.RemoveGroupingPolicies(groupingPolicies)
|
_, err = enforcer.RemoveGroupingPolicies(groupingPolicies)
|
||||||
@@ -287,7 +310,12 @@ func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) ([
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, role := range GetAllRoles(userId) {
|
allRoles, err := GetAllRoles(userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range allRoles {
|
||||||
permissionsByRole, err := GetPermissionsByRole(role)
|
permissionsByRole, err := GetPermissionsByRole(role)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -321,17 +349,17 @@ func GetAllActions(userId string) ([]string, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllRoles(userId string) []string {
|
func GetAllRoles(userId string) ([]string, error) {
|
||||||
roles, err := getRolesByUser(userId)
|
roles, err := getRolesByUser(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var res []string
|
res := []string{}
|
||||||
for _, role := range roles {
|
for _, role := range roles {
|
||||||
res = append(res, role.Name)
|
res = append(res, role.Name)
|
||||||
}
|
}
|
||||||
return res
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBuiltInModel(modelText string) (model.Model, error) {
|
func GetBuiltInModel(modelText string) (model.Model, error) {
|
||||||
|
@@ -17,6 +17,8 @@ package object
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/idp"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/pp"
|
"github.com/casdoor/casdoor/pp"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@@ -30,8 +32,8 @@ type Product struct {
|
|||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
|
||||||
Image string `xorm:"varchar(100)" json:"image"`
|
Image string `xorm:"varchar(100)" json:"image"`
|
||||||
Detail string `xorm:"varchar(255)" json:"detail"`
|
Detail string `xorm:"varchar(1000)" json:"detail"`
|
||||||
Description string `xorm:"varchar(100)" json:"description"`
|
Description string `xorm:"varchar(200)" json:"description"`
|
||||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||||
Price float64 `json:"price"`
|
Price float64 `json:"price"`
|
||||||
@@ -158,30 +160,28 @@ func (product *Product) getProvider(providerName string) (*Provider, error) {
|
|||||||
return provider, nil
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuyProduct(id string, user *User, providerName, pricingName, planName, host string) (*Payment, error) {
|
func BuyProduct(id string, user *User, providerName, pricingName, planName, host, paymentEnv string) (payment *Payment, attachInfo map[string]interface{}, err error) {
|
||||||
product, err := GetProduct(id)
|
product, err := GetProduct(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if product == nil {
|
if product == nil {
|
||||||
return nil, fmt.Errorf("the product: %s does not exist", id)
|
return nil, nil, fmt.Errorf("the product: %s does not exist", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, err := product.getProvider(providerName)
|
provider, err := product.getProvider(providerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pProvider, err := GetPaymentProvider(provider)
|
pProvider, err := GetPaymentProvider(provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
owner := product.Owner
|
owner := product.Owner
|
||||||
productName := product.Name
|
|
||||||
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
|
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
|
||||||
paymentName := fmt.Sprintf("payment_%v", util.GenerateTimeId())
|
paymentName := fmt.Sprintf("payment_%v", util.GenerateTimeId())
|
||||||
productDisplayName := product.DisplayName
|
|
||||||
|
|
||||||
originFrontend, originBackend := getOriginFromHost(host)
|
originFrontend, originBackend := getOriginFromHost(host)
|
||||||
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
|
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
|
||||||
@@ -191,26 +191,46 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
if pricingName != "" && planName != "" {
|
if pricingName != "" && planName != "" {
|
||||||
plan, err := GetPlan(util.GetId(owner, planName))
|
plan, err := GetPlan(util.GetId(owner, planName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if plan == nil {
|
if plan == nil {
|
||||||
return nil, fmt.Errorf("the plan: %s does not exist", planName)
|
return nil, nil, fmt.Errorf("the plan: %s does not exist", planName)
|
||||||
}
|
}
|
||||||
sub := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
|
sub := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
|
||||||
_, err = AddSubscription(sub)
|
_, err = AddSubscription(sub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
|
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create an OrderId and get the payUrl
|
// Create an order
|
||||||
payUrl, orderId, err := pProvider.Pay(providerName, productName, payerName, paymentName, productDisplayName, product.Price, product.Currency, returnUrl, notifyUrl)
|
payReq := &pp.PayReq{
|
||||||
|
ProviderName: providerName,
|
||||||
|
ProductName: product.Name,
|
||||||
|
PayerName: payerName,
|
||||||
|
PayerId: user.Id,
|
||||||
|
PaymentName: paymentName,
|
||||||
|
ProductDisplayName: product.DisplayName,
|
||||||
|
Price: product.Price,
|
||||||
|
Currency: product.Currency,
|
||||||
|
ReturnUrl: returnUrl,
|
||||||
|
NotifyUrl: notifyUrl,
|
||||||
|
PaymentEnv: paymentEnv,
|
||||||
|
}
|
||||||
|
// custom process for WeChat & WeChat Pay
|
||||||
|
if provider.Type == "WeChat Pay" {
|
||||||
|
payReq.PayerId, err = getUserExtraProperty(user, "WeChat", idp.BuildWechatOpenIdKey(provider.ClientId2))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payResp, err := pProvider.Pay(payReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// Create a Payment linked with Product and Order
|
// Create a Payment linked with Product and Order
|
||||||
payment := &Payment{
|
payment = &Payment{
|
||||||
Owner: product.Owner,
|
Owner: product.Owner,
|
||||||
Name: paymentName,
|
Name: paymentName,
|
||||||
CreatedTime: util.GetCurrentTime(),
|
CreatedTime: util.GetCurrentTime(),
|
||||||
@@ -219,8 +239,8 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
Provider: provider.Name,
|
Provider: provider.Name,
|
||||||
Type: provider.Type,
|
Type: provider.Type,
|
||||||
|
|
||||||
ProductName: productName,
|
ProductName: product.Name,
|
||||||
ProductDisplayName: productDisplayName,
|
ProductDisplayName: product.DisplayName,
|
||||||
Detail: product.Detail,
|
Detail: product.Detail,
|
||||||
Tag: product.Tag,
|
Tag: product.Tag,
|
||||||
Currency: product.Currency,
|
Currency: product.Currency,
|
||||||
@@ -228,10 +248,10 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
ReturnUrl: product.ReturnUrl,
|
ReturnUrl: product.ReturnUrl,
|
||||||
|
|
||||||
User: user.Name,
|
User: user.Name,
|
||||||
PayUrl: payUrl,
|
PayUrl: payResp.PayUrl,
|
||||||
SuccessUrl: returnUrl,
|
SuccessUrl: returnUrl,
|
||||||
State: pp.PaymentStateCreated,
|
State: pp.PaymentStateCreated,
|
||||||
OutOrderId: orderId,
|
OutOrderId: payResp.OrderId,
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider.Type == "Dummy" {
|
if provider.Type == "Dummy" {
|
||||||
@@ -240,13 +260,13 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
|
|
||||||
affected, err := AddPayment(payment)
|
affected, err := AddPayment(payment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !affected {
|
if !affected {
|
||||||
return nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
return nil, nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
||||||
}
|
}
|
||||||
return payment, err
|
return payment, payResp.AttachInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtendProductWithProviders(product *Product) error {
|
func ExtendProductWithProviders(product *Product) error {
|
||||||
|
144
object/token.go
144
object/token.go
@@ -17,6 +17,7 @@ package object
|
|||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -51,15 +52,17 @@ type Token struct {
|
|||||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||||
User string `xorm:"varchar(100)" json:"user"`
|
User string `xorm:"varchar(100)" json:"user"`
|
||||||
|
|
||||||
Code string `xorm:"varchar(100) index" json:"code"`
|
Code string `xorm:"varchar(100) index" json:"code"`
|
||||||
AccessToken string `xorm:"mediumtext" json:"accessToken"`
|
AccessToken string `xorm:"mediumtext" json:"accessToken"`
|
||||||
RefreshToken string `xorm:"mediumtext" json:"refreshToken"`
|
RefreshToken string `xorm:"mediumtext" json:"refreshToken"`
|
||||||
ExpiresIn int `json:"expiresIn"`
|
AccessTokenHash string `xorm:"varchar(100) index" json:"accessTokenHash"`
|
||||||
Scope string `xorm:"varchar(100)" json:"scope"`
|
RefreshTokenHash string `xorm:"varchar(100) index" json:"refreshTokenHash"`
|
||||||
TokenType string `xorm:"varchar(100)" json:"tokenType"`
|
ExpiresIn int `json:"expiresIn"`
|
||||||
CodeChallenge string `xorm:"varchar(100)" json:"codeChallenge"`
|
Scope string `xorm:"varchar(100)" json:"scope"`
|
||||||
CodeIsUsed bool `json:"codeIsUsed"`
|
TokenType string `xorm:"varchar(100)" json:"tokenType"`
|
||||||
CodeExpireIn int64 `json:"codeExpireIn"`
|
CodeChallenge string `xorm:"varchar(100)" json:"codeChallenge"`
|
||||||
|
CodeIsUsed bool `json:"codeIsUsed"`
|
||||||
|
CodeExpireIn int64 `json:"codeExpireIn"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenWrapper struct {
|
type TokenWrapper struct {
|
||||||
@@ -141,6 +144,48 @@ func getTokenByCode(code string) (*Token, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetTokenByAccessToken(accessToken string) (*Token, error) {
|
||||||
|
token := Token{AccessTokenHash: getTokenHash(accessToken)}
|
||||||
|
existed, err := ormer.Engine.Get(&token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !existed {
|
||||||
|
token = Token{AccessToken: accessToken}
|
||||||
|
existed, err = ormer.Engine.Get(&token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !existed {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTokenByRefreshToken(refreshToken string) (*Token, error) {
|
||||||
|
token := Token{RefreshTokenHash: getTokenHash(refreshToken)}
|
||||||
|
existed, err := ormer.Engine.Get(&token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !existed {
|
||||||
|
token = Token{RefreshToken: refreshToken}
|
||||||
|
existed, err = ormer.Engine.Get(&token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !existed {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &token, nil
|
||||||
|
}
|
||||||
|
|
||||||
func updateUsedByCode(token *Token) bool {
|
func updateUsedByCode(token *Token) bool {
|
||||||
affected, err := ormer.Engine.Where("code=?", token.Code).Cols("code_is_used").Update(token)
|
affected, err := ormer.Engine.Where("code=?", token.Code).Cols("code_is_used").Update(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -159,6 +204,24 @@ func (token *Token) GetId() string {
|
|||||||
return fmt.Sprintf("%s/%s", token.Owner, token.Name)
|
return fmt.Sprintf("%s/%s", token.Owner, token.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTokenHash(input string) string {
|
||||||
|
hash := sha256.Sum256([]byte(input))
|
||||||
|
res := hex.EncodeToString(hash[:])
|
||||||
|
if len(res) > 64 {
|
||||||
|
return res[:64]
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (token *Token) popularHashes() {
|
||||||
|
if token.AccessTokenHash == "" && token.AccessToken != "" {
|
||||||
|
token.AccessTokenHash = getTokenHash(token.AccessToken)
|
||||||
|
}
|
||||||
|
if token.RefreshTokenHash == "" && token.RefreshToken != "" {
|
||||||
|
token.RefreshTokenHash = getTokenHash(token.RefreshToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateToken(id string, token *Token) (bool, error) {
|
func UpdateToken(id string, token *Token) (bool, error) {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
if t, err := getToken(owner, name); err != nil {
|
if t, err := getToken(owner, name); err != nil {
|
||||||
@@ -167,6 +230,8 @@ func UpdateToken(id string, token *Token) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token.popularHashes()
|
||||||
|
|
||||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(token)
|
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@@ -176,6 +241,8 @@ func UpdateToken(id string, token *Token) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AddToken(token *Token) (bool, error) {
|
func AddToken(token *Token) (bool, error) {
|
||||||
|
token.popularHashes()
|
||||||
|
|
||||||
affected, err := ormer.Engine.Insert(token)
|
affected, err := ormer.Engine.Insert(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@@ -194,18 +261,16 @@ func DeleteToken(token *Token) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token, error) {
|
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token, error) {
|
||||||
token := Token{AccessToken: accessToken}
|
token, err := GetTokenByAccessToken(accessToken)
|
||||||
existed, err := ormer.Engine.Get(&token)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, nil, err
|
return false, nil, nil, err
|
||||||
}
|
}
|
||||||
|
if token == nil {
|
||||||
if !existed {
|
|
||||||
return false, nil, nil, nil
|
return false, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
token.ExpiresIn = 0
|
token.ExpiresIn = 0
|
||||||
affected, err := ormer.Engine.ID(core.PK{token.Owner, token.Name}).Cols("expires_in").Update(&token)
|
affected, err := ormer.Engine.ID(core.PK{token.Owner, token.Name}).Cols("expires_in").Update(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, nil, err
|
return false, nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -215,22 +280,7 @@ func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token, e
|
|||||||
return false, nil, nil, err
|
return false, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return affected != 0, application, &token, nil
|
return affected != 0, application, token, nil
|
||||||
}
|
|
||||||
|
|
||||||
func GetTokenByAccessToken(accessToken string) (*Token, error) {
|
|
||||||
// Check if the accessToken is in the database
|
|
||||||
token := Token{AccessToken: accessToken}
|
|
||||||
existed, err := ormer.Engine.Get(&token)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !existed {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &token, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTokenByTokenAndApplication(token string, application string) (*Token, error) {
|
func GetTokenByTokenAndApplication(token string, application string) (*Token, error) {
|
||||||
@@ -432,16 +482,17 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
ErrorDescription: "client_id is invalid",
|
ErrorDescription: "client_id is invalid",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if clientSecret != "" && application.ClientSecret != clientSecret {
|
if clientSecret != "" && application.ClientSecret != clientSecret {
|
||||||
return &TokenError{
|
return &TokenError{
|
||||||
Error: InvalidClient,
|
Error: InvalidClient,
|
||||||
ErrorDescription: "client_secret is invalid",
|
ErrorDescription: "client_secret is invalid",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether the refresh token is valid, and has not expired.
|
// check whether the refresh token is valid, and has not expired.
|
||||||
token := Token{RefreshToken: refreshToken}
|
token, err := GetTokenByRefreshToken(refreshToken)
|
||||||
existed, err := ormer.Engine.Get(&token)
|
if err != nil || token == nil {
|
||||||
if err != nil || !existed {
|
|
||||||
return &TokenError{
|
return &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
ErrorDescription: "refresh token is invalid, expired or revoked",
|
ErrorDescription: "refresh token is invalid, expired or revoked",
|
||||||
@@ -452,6 +503,12 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if cert == nil {
|
||||||
|
return &TokenError{
|
||||||
|
Error: InvalidGrant,
|
||||||
|
ErrorDescription: fmt.Sprintf("cert: %s cannot be found", application.Cert),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err = ParseJwtToken(refreshToken, cert)
|
_, err = ParseJwtToken(refreshToken, cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -460,6 +517,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate a new token
|
// generate a new token
|
||||||
user, err := getUser(application.Organization, token.User)
|
user, err := getUser(application.Organization, token.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -477,6 +535,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newAccessToken, newRefreshToken, tokenName, 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{
|
||||||
@@ -504,7 +563,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = DeleteToken(&token)
|
_, err = DeleteToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -517,7 +576,6 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
ExpiresIn: newToken.ExpiresIn,
|
ExpiresIn: newToken.ExpiresIn,
|
||||||
Scope: newToken.Scope,
|
Scope: newToken.Scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
return tokenWrapper, nil
|
return tokenWrapper, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,25 +679,25 @@ func GetPasswordToken(application *Application, username string, password string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
ErrorDescription: "the user does not exist",
|
ErrorDescription: "the user does not exist",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
var msg string
|
|
||||||
if user.Ldap != "" {
|
if user.Ldap != "" {
|
||||||
msg = checkLdapUserPassword(user, password, "en")
|
err = checkLdapUserPassword(user, password, "en")
|
||||||
} else {
|
} else {
|
||||||
msg = CheckPassword(user, password, "en")
|
err = CheckPassword(user, password, "en")
|
||||||
}
|
}
|
||||||
if msg != "" {
|
if err != nil {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
ErrorDescription: "invalid username or password",
|
ErrorDescription: fmt.Sprintf("invalid username or password: %s", err.Error()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.IsForbidden {
|
if user.IsForbidden {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
@@ -729,13 +787,13 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
|
|||||||
|
|
||||||
// GetTokenByUser
|
// 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, nonce string, host string) (*Token, error) {
|
||||||
err := ExtendUserWithRolesAndPermissions(user)
|
err := ExtendUserWithRolesAndPermissions(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
|
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, nonce, scope, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,12 @@ type Claims struct {
|
|||||||
type UserShort struct {
|
type UserShort 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"`
|
||||||
|
|
||||||
|
Id string `xorm:"varchar(100) index" json:"id"`
|
||||||
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
Avatar string `xorm:"varchar(500)" json:"avatar"`
|
||||||
|
Email string `xorm:"varchar(100) index" json:"email"`
|
||||||
|
Phone string `xorm:"varchar(20) index" json:"phone"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserWithoutThirdIdp struct {
|
type UserWithoutThirdIdp struct {
|
||||||
@@ -144,6 +150,12 @@ func getShortUser(user *User) *UserShort {
|
|||||||
res := &UserShort{
|
res := &UserShort{
|
||||||
Owner: user.Owner,
|
Owner: user.Owner,
|
||||||
Name: user.Name,
|
Name: user.Name,
|
||||||
|
|
||||||
|
Id: user.Id,
|
||||||
|
DisplayName: user.DisplayName,
|
||||||
|
Avatar: user.Avatar,
|
||||||
|
Email: user.Email,
|
||||||
|
Phone: user.Phone,
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@@ -24,14 +24,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateRsaKeys(bitSize int, expireInYears int, commonName string, organization string) (string, string) {
|
func generateRsaKeys(bitSize int, expireInYears int, commonName string, organization string) (string, string, error) {
|
||||||
// https://stackoverflow.com/questions/64104586/use-golang-to-get-rsa-key-the-same-way-openssl-genrsa
|
// https://stackoverflow.com/questions/64104586/use-golang-to-get-rsa-key-the-same-way-openssl-genrsa
|
||||||
// https://stackoverflow.com/questions/43822945/golang-can-i-create-x509keypair-using-rsa-key
|
// https://stackoverflow.com/questions/43822945/golang-can-i-create-x509keypair-using-rsa-key
|
||||||
|
|
||||||
// Generate RSA key.
|
// Generate RSA key.
|
||||||
key, err := rsa.GenerateKey(rand.Reader, bitSize)
|
key, err := rsa.GenerateKey(rand.Reader, bitSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode private key to PKCS#1 ASN.1 PEM.
|
// Encode private key to PKCS#1 ASN.1 PEM.
|
||||||
@@ -54,9 +54,10 @@ func generateRsaKeys(bitSize int, expireInYears int, commonName string, organiza
|
|||||||
},
|
},
|
||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
|
cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a pem block with the certificate
|
// Generate a pem block with the certificate
|
||||||
@@ -65,5 +66,5 @@ func generateRsaKeys(bitSize int, expireInYears int, commonName string, organiza
|
|||||||
Bytes: cert,
|
Bytes: cert,
|
||||||
})
|
})
|
||||||
|
|
||||||
return string(certPem), string(privateKeyPem)
|
return string(certPem), string(privateKeyPem), nil
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,10 @@ import (
|
|||||||
|
|
||||||
func TestGenerateRsaKeys(t *testing.T) {
|
func TestGenerateRsaKeys(t *testing.T) {
|
||||||
fileId := "token_jwt_key"
|
fileId := "token_jwt_key"
|
||||||
certificate, privateKey := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization")
|
certificate, privateKey, err := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Write certificate (aka certificate) to file.
|
// Write certificate (aka certificate) to file.
|
||||||
util.WriteStringToPath(certificate, fmt.Sprintf("%s.pem", fileId))
|
util.WriteStringToPath(certificate, fmt.Sprintf("%s.pem", fileId))
|
||||||
|
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/go-webauthn/webauthn/webauthn"
|
"github.com/go-webauthn/webauthn/webauthn"
|
||||||
|
"github.com/xorm-io/builder"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -231,6 +232,20 @@ func GetGlobalUsers() ([]*User, error) {
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetGlobalUsersWithFilter(cond builder.Cond) ([]*User, error) {
|
||||||
|
users := []*User{}
|
||||||
|
session := ormer.Engine.Desc("created_time")
|
||||||
|
if cond != nil {
|
||||||
|
session = session.Where(cond)
|
||||||
|
}
|
||||||
|
err := session.Find(&users)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetPaginationGlobalUsers(offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
func GetPaginationGlobalUsers(offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
session := GetSessionForUser("", offset, limit, field, value, sortField, sortOrder)
|
session := GetSessionForUser("", offset, limit, field, value, sortField, sortOrder)
|
||||||
@@ -266,9 +281,27 @@ func GetUsers(owner string) ([]*User, error) {
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUsersByTag(owner string, tag string) ([]*User, error) {
|
func GetUsersWithFilter(owner string, cond builder.Cond) ([]*User, error) {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
err := ormer.Engine.Desc("created_time").Find(&users, &User{Owner: owner, Tag: tag})
|
session := ormer.Engine.Desc("created_time")
|
||||||
|
if cond != nil {
|
||||||
|
session = session.Where(cond)
|
||||||
|
}
|
||||||
|
err := session.Find(&users, &User{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUsersByTagWithFilter(owner string, tag string, cond builder.Cond) ([]*User, error) {
|
||||||
|
users := []*User{}
|
||||||
|
session := ormer.Engine.Desc("created_time")
|
||||||
|
if cond != nil {
|
||||||
|
session = session.Where(cond)
|
||||||
|
}
|
||||||
|
err := session.Find(&users, &User{Owner: owner, Tag: tag})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -664,6 +697,8 @@ func UpdateUserForAllFields(id string, user *User) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user.UpdatedTime = util.GetCurrentTime()
|
||||||
|
|
||||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(user)
|
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@@ -851,7 +886,7 @@ func (user *User) GetId() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isUserIdGlobalAdmin(userId string) bool {
|
func isUserIdGlobalAdmin(userId string) bool {
|
||||||
return strings.HasPrefix(userId, "built-in/")
|
return strings.HasPrefix(userId, "built-in/") || strings.HasPrefix(userId, "app/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtendUserWithRolesAndPermissions(user *User) (err error) {
|
func ExtendUserWithRolesAndPermissions(user *User) (err error) {
|
||||||
|
@@ -35,11 +35,7 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
|
|||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("downloadImage() error for url [%s]: %s\n", url, err.Error())
|
fmt.Printf("downloadImage() error for url [%s]: %s\n", url, err.Error())
|
||||||
if strings.Contains(err.Error(), "EOF") || strings.Contains(err.Error(), "no such host") || strings.Contains(err.Error(), "did not properly respond after a period of time") || strings.Contains(err.Error(), "unrecognized name") {
|
return nil, "", nil
|
||||||
return nil, "", nil
|
|
||||||
} else {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
@@ -58,6 +54,8 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
|
|||||||
|
|
||||||
if strings.Contains(contentType, "text/html") {
|
if strings.Contains(contentType, "text/html") {
|
||||||
fileExtension = ".html"
|
fileExtension = ".html"
|
||||||
|
} else if contentType == "image/vnd.microsoft.icon" {
|
||||||
|
fileExtension = ".ico"
|
||||||
} else {
|
} else {
|
||||||
fileExtensions, err := mime.ExtensionsByType(contentType)
|
fileExtensions, err := mime.ExtensionsByType(contentType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -186,10 +186,47 @@ func parseSize(sizes string) []int {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var publicEmailDomains = map[string]int{
|
||||||
|
"gmail.com": 1,
|
||||||
|
"163.com": 1,
|
||||||
|
"qq.com": 1,
|
||||||
|
"yahoo.com": 1,
|
||||||
|
"hotmail.com": 1,
|
||||||
|
"outlook.com": 1,
|
||||||
|
"icloud.com": 1,
|
||||||
|
"mail.com": 1,
|
||||||
|
"aol.com": 1,
|
||||||
|
"live.com": 1,
|
||||||
|
"yandex.com": 1,
|
||||||
|
"yahoo.co.jp": 1,
|
||||||
|
"yahoo.co.in": 1,
|
||||||
|
"yahoo.co.uk": 1,
|
||||||
|
"me.com": 1,
|
||||||
|
"msn.com": 1,
|
||||||
|
"comcast.net": 1,
|
||||||
|
"sbcglobal.net": 1,
|
||||||
|
"verizon.net": 1,
|
||||||
|
"earthlink.net": 1,
|
||||||
|
"cox.net": 1,
|
||||||
|
"rediffmail.com": 1,
|
||||||
|
"in.com": 1,
|
||||||
|
"hotmail.co.uk": 1,
|
||||||
|
"hotmail.fr": 1,
|
||||||
|
"zoho.com": 1,
|
||||||
|
"gmx.com": 1,
|
||||||
|
"gmx.de": 1,
|
||||||
|
"gmx.net": 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPublicEmailDomain(domain string) bool {
|
||||||
|
_, exists := publicEmailDomains[domain]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, string, error) {
|
func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, string, error) {
|
||||||
tokens := strings.Split(email, "@")
|
tokens := strings.Split(email, "@")
|
||||||
domain := tokens[1]
|
domain := tokens[1]
|
||||||
if domain == "gmail.com" || domain == "163.com" || domain == "qq.com" {
|
if isPublicEmailDomain(domain) {
|
||||||
return nil, "", nil
|
return nil, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,11 +240,11 @@ func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, str
|
|||||||
if buffer != nil {
|
if buffer != nil {
|
||||||
faviconUrl, err = GetFaviconUrl(buffer.String())
|
faviconUrl, err = GetFaviconUrl(buffer.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
fmt.Printf("getFaviconFileBuffer() error, faviconUrl is empty, error = %s\n", err.Error())
|
||||||
}
|
} else {
|
||||||
|
if !strings.HasPrefix(faviconUrl, "http") {
|
||||||
if !strings.HasPrefix(faviconUrl, "http") {
|
faviconUrl = util.UrlJoin(htmlUrl, faviconUrl)
|
||||||
faviconUrl = util.UrlJoin(htmlUrl, faviconUrl)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,7 +20,10 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/idp"
|
"github.com/casdoor/casdoor/idp"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -110,6 +113,10 @@ func SetUserField(user *User, field string, value string) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user != nil {
|
||||||
|
user.UpdatedTime = util.GetCurrentTime()
|
||||||
|
}
|
||||||
|
|
||||||
_, err = ormer.Engine.ID(core.PK{user.Owner, user.Name}).Cols("hash").Update(user)
|
_, err = ormer.Engine.ID(core.PK{user.Owner, user.Name}).Cols("hash").Update(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@@ -137,6 +144,25 @@ func setUserProperty(user *User, field string, value string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUserProperty(user *User, field string) string {
|
||||||
|
if user.Properties == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return user.Properties[field]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserExtraProperty(user *User, providerType, key string) (string, error) {
|
||||||
|
extraJson := getUserProperty(user, fmt.Sprintf("oauth_%s_extra", providerType))
|
||||||
|
if extraJson == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
extra := make(map[string]string)
|
||||||
|
if err := jsoniter.Unmarshal([]byte(extraJson), &extra); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return extra[key], nil
|
||||||
|
}
|
||||||
|
|
||||||
func SetUserOAuthProperties(organization *Organization, user *User, providerType string, userInfo *idp.UserInfo) (bool, error) {
|
func SetUserOAuthProperties(organization *Organization, user *User, providerType string, userInfo *idp.UserInfo) (bool, error) {
|
||||||
if userInfo.Id != "" {
|
if userInfo.Id != "" {
|
||||||
propertyName := fmt.Sprintf("oauth_%s_id", providerType)
|
propertyName := fmt.Sprintf("oauth_%s_id", providerType)
|
||||||
@@ -180,6 +206,27 @@ func SetUserOAuthProperties(organization *Organization, user *User, providerType
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if userInfo.Extra != nil {
|
||||||
|
// Save extra info as json string
|
||||||
|
propertyName := fmt.Sprintf("oauth_%s_extra", providerType)
|
||||||
|
oldExtraJson := getUserProperty(user, propertyName)
|
||||||
|
extra := make(map[string]string)
|
||||||
|
if oldExtraJson != "" {
|
||||||
|
if err := jsoniter.Unmarshal([]byte(oldExtraJson), &extra); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k, v := range userInfo.Extra {
|
||||||
|
extra[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
newExtraJson, err := jsoniter.Marshal(extra)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
setUserProperty(user, propertyName, string(newExtraJson))
|
||||||
|
}
|
||||||
|
|
||||||
return UpdateUserForAllFields(user.GetId(), user)
|
return UpdateUserForAllFields(user.GetId(), user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -82,7 +82,12 @@ func IsAllowSend(user *User, remoteAddr, recordType string) error {
|
|||||||
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 {
|
||||||
sender := organization.DisplayName
|
sender := organization.DisplayName
|
||||||
title := provider.Title
|
title := provider.Title
|
||||||
|
|
||||||
code := getRandomCode(6)
|
code := getRandomCode(6)
|
||||||
|
if organization.MasterVerificationCode != "" {
|
||||||
|
code = organization.MasterVerificationCode
|
||||||
|
}
|
||||||
|
|
||||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||||
content := fmt.Sprintf(provider.Content, code)
|
content := fmt.Sprintf(provider.Content, code)
|
||||||
|
|
||||||
@@ -107,6 +112,10 @@ func SendVerificationCodeToPhone(organization *Organization, user *User, provide
|
|||||||
}
|
}
|
||||||
|
|
||||||
code := getRandomCode(6)
|
code := getRandomCode(6)
|
||||||
|
if organization.MasterVerificationCode != "" {
|
||||||
|
code = organization.MasterVerificationCode
|
||||||
|
}
|
||||||
|
|
||||||
if err := SendSms(provider, code, dest); err != nil {
|
if err := SendSms(provider, code, dest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -156,7 +165,7 @@ func getVerificationRecord(dest string) (*VerificationRecord, error) {
|
|||||||
return &record, nil
|
return &record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckVerificationCode(dest, code, lang string) *VerifyResult {
|
func CheckVerificationCode(dest string, code string, lang string) *VerifyResult {
|
||||||
record, err := getVerificationRecord(dest)
|
record, err := getVerificationRecord(dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -183,32 +192,32 @@ func CheckVerificationCode(dest, code, lang string) *VerifyResult {
|
|||||||
return &VerifyResult{VerificationSuccess, ""}
|
return &VerifyResult{VerificationSuccess, ""}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DisableVerificationCode(dest string) (err error) {
|
func DisableVerificationCode(dest string) error {
|
||||||
record, err := getVerificationRecord(dest)
|
record, err := getVerificationRecord(dest)
|
||||||
if record == nil || err != nil {
|
if record == nil || err != nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
record.IsUsed = true
|
record.IsUsed = true
|
||||||
_, err = ormer.Engine.ID(core.PK{record.Owner, record.Name}).AllCols().Update(record)
|
_, err = ormer.Engine.ID(core.PK{record.Owner, record.Name}).AllCols().Update(record)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckSigninCode(user *User, dest, code, lang string) string {
|
func CheckSigninCode(user *User, dest, code, lang string) error {
|
||||||
// check the login error times
|
// check the login error times
|
||||||
if msg := checkSigninErrorTimes(user, lang); msg != "" {
|
err := checkSigninErrorTimes(user, lang)
|
||||||
return msg
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := CheckVerificationCode(dest, code, lang)
|
result := CheckVerificationCode(dest, code, lang)
|
||||||
switch result.Code {
|
switch result.Code {
|
||||||
case VerificationSuccess:
|
case VerificationSuccess:
|
||||||
resetUserSigninErrorTimes(user)
|
return resetUserSigninErrorTimes(user)
|
||||||
return ""
|
|
||||||
case wrongCodeError:
|
case wrongCodeError:
|
||||||
return recordSigninErrorInfo(user, lang)
|
return recordSigninErrorInfo(user, lang)
|
||||||
default:
|
default:
|
||||||
return result.Msg
|
return fmt.Errorf(result.Msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
pp/alipay.go
20
pp/alipay.go
@@ -49,20 +49,24 @@ func NewAlipayPaymentProvider(appId string, appCertificate string, appPrivateKey
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *AlipayPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
// pp.Client.DebugSwitch = gopay.DebugOn
|
// pp.Client.DebugSwitch = gopay.DebugOn
|
||||||
bm := gopay.BodyMap{}
|
bm := gopay.BodyMap{}
|
||||||
pp.Client.SetReturnUrl(returnUrl)
|
pp.Client.SetReturnUrl(r.ReturnUrl)
|
||||||
pp.Client.SetNotifyUrl(notifyUrl)
|
pp.Client.SetNotifyUrl(r.NotifyUrl)
|
||||||
bm.Set("subject", joinAttachString([]string{productName, productDisplayName, providerName}))
|
bm.Set("subject", joinAttachString([]string{r.ProductName, r.ProductDisplayName, r.ProviderName}))
|
||||||
bm.Set("out_trade_no", paymentName)
|
bm.Set("out_trade_no", r.PaymentName)
|
||||||
bm.Set("total_amount", priceFloat64ToString(price))
|
bm.Set("total_amount", priceFloat64ToString(r.Price))
|
||||||
|
|
||||||
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return payUrl, paymentName, nil
|
payResp := &PayResp{
|
||||||
|
PayUrl: payUrl,
|
||||||
|
OrderId: r.PaymentName,
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *AlipayPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *AlipayPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
@@ -21,8 +21,10 @@ func NewDummyPaymentProvider() (*DummyPaymentProvider, error) {
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *DummyPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *DummyPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
return returnUrl, "", nil
|
return &PayResp{
|
||||||
|
PayUrl: r.ReturnUrl,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *DummyPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *DummyPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
36
pp/gc.go
36
pp/gc.go
@@ -153,22 +153,22 @@ func (pp *GcPaymentProvider) doPost(postBytes []byte) ([]byte, error) {
|
|||||||
return respBytes, nil
|
return respBytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *GcPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
payReqInfo := GcPayReqInfo{
|
payReqInfo := GcPayReqInfo{
|
||||||
OrderDate: util.GenerateSimpleTimeId(),
|
OrderDate: util.GenerateSimpleTimeId(),
|
||||||
OrderNo: paymentName,
|
OrderNo: r.PaymentName,
|
||||||
Amount: getPriceString(price),
|
Amount: getPriceString(r.Price),
|
||||||
Xmpch: pp.Xmpch,
|
Xmpch: pp.Xmpch,
|
||||||
Body: productDisplayName,
|
Body: r.ProductDisplayName,
|
||||||
ReturnUrl: returnUrl,
|
ReturnUrl: r.ReturnUrl,
|
||||||
NotifyUrl: notifyUrl,
|
NotifyUrl: r.NotifyUrl,
|
||||||
Remark1: payerName,
|
Remark1: r.PayerName,
|
||||||
Remark2: productName,
|
Remark2: r.ProductName,
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(payReqInfo)
|
b, err := json.Marshal(payReqInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
body := GcRequestBody{
|
body := GcRequestBody{
|
||||||
@@ -184,36 +184,38 @@ func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerN
|
|||||||
|
|
||||||
bodyBytes, err := json.Marshal(body)
|
bodyBytes, err := json.Marshal(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
respBytes, err := pp.doPost(bodyBytes)
|
respBytes, err := pp.doPost(bodyBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var respBody GcResponseBody
|
var respBody GcResponseBody
|
||||||
err = json.Unmarshal(respBytes, &respBody)
|
err = json.Unmarshal(respBytes, &respBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if respBody.ReturnCode != "SUCCESS" {
|
if respBody.ReturnCode != "SUCCESS" {
|
||||||
return "", "", fmt.Errorf("%s: %s", respBody.ReturnCode, respBody.ReturnMsg)
|
return nil, fmt.Errorf("%s: %s", respBody.ReturnCode, respBody.ReturnMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
payRespInfoBytes, err := base64.StdEncoding.DecodeString(respBody.Data)
|
payRespInfoBytes, err := base64.StdEncoding.DecodeString(respBody.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var payRespInfo GcPayRespInfo
|
var payRespInfo GcPayRespInfo
|
||||||
err = json.Unmarshal(payRespInfoBytes, &payRespInfo)
|
err = json.Unmarshal(payRespInfoBytes, &payRespInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
payResp := &PayResp{
|
||||||
return payRespInfo.PayUrl, "", nil
|
PayUrl: payRespInfo.PayUrl,
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *GcPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *GcPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
22
pp/paypal.go
22
pp/paypal.go
@@ -49,16 +49,16 @@ func NewPaypalPaymentProvider(clientID string, secret string) (*PaypalPaymentPro
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *PaypalPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
// https://github.com/go-pay/gopay/blob/main/doc/paypal.md
|
// https://github.com/go-pay/gopay/blob/main/doc/paypal.md
|
||||||
units := make([]*paypal.PurchaseUnit, 0, 1)
|
units := make([]*paypal.PurchaseUnit, 0, 1)
|
||||||
unit := &paypal.PurchaseUnit{
|
unit := &paypal.PurchaseUnit{
|
||||||
ReferenceId: util.GetRandomString(16),
|
ReferenceId: util.GetRandomString(16),
|
||||||
Amount: &paypal.Amount{
|
Amount: &paypal.Amount{
|
||||||
CurrencyCode: currency, // e.g."USD"
|
CurrencyCode: r.Currency, // e.g."USD"
|
||||||
Value: priceFloat64ToString(price), // e.g."100.00"
|
Value: priceFloat64ToString(r.Price), // e.g."100.00"
|
||||||
},
|
},
|
||||||
Description: joinAttachString([]string{productDisplayName, productName, providerName}),
|
Description: joinAttachString([]string{r.ProductDisplayName, r.ProductName, r.ProviderName}),
|
||||||
}
|
}
|
||||||
units = append(units, unit)
|
units = append(units, unit)
|
||||||
|
|
||||||
@@ -68,23 +68,27 @@ func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, pa
|
|||||||
bm.SetBodyMap("application_context", func(b gopay.BodyMap) {
|
bm.SetBodyMap("application_context", func(b gopay.BodyMap) {
|
||||||
b.Set("brand_name", "Casdoor")
|
b.Set("brand_name", "Casdoor")
|
||||||
b.Set("locale", "en-PT")
|
b.Set("locale", "en-PT")
|
||||||
b.Set("return_url", returnUrl)
|
b.Set("return_url", r.ReturnUrl)
|
||||||
b.Set("cancel_url", returnUrl)
|
b.Set("cancel_url", r.ReturnUrl)
|
||||||
})
|
})
|
||||||
|
|
||||||
ppRsp, err := pp.Client.CreateOrder(context.Background(), bm)
|
ppRsp, err := pp.Client.CreateOrder(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
if ppRsp.Code != paypal.Success {
|
if ppRsp.Code != paypal.Success {
|
||||||
return "", "", errors.New(ppRsp.Error)
|
return nil, errors.New(ppRsp.Error)
|
||||||
}
|
}
|
||||||
// {"id":"9BR68863NE220374S","status":"CREATED",
|
// {"id":"9BR68863NE220374S","status":"CREATED",
|
||||||
// "links":[{"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S","rel":"self","method":"GET"},
|
// "links":[{"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S","rel":"self","method":"GET"},
|
||||||
// {"href":"https://www.sandbox.paypal.com/checkoutnow?token=9BR68863NE220374S","rel":"approve","method":"GET"},
|
// {"href":"https://www.sandbox.paypal.com/checkoutnow?token=9BR68863NE220374S","rel":"approve","method":"GET"},
|
||||||
// {"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S","rel":"update","method":"PATCH"},
|
// {"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S","rel":"update","method":"PATCH"},
|
||||||
// {"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S/capture","rel":"capture","method":"POST"}]}
|
// {"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S/capture","rel":"capture","method":"POST"}]}
|
||||||
return ppRsp.Response.Links[1].Href, ppRsp.Response.Id, nil
|
payResp := &PayResp{
|
||||||
|
PayUrl: ppRsp.Response.Links[1].Href,
|
||||||
|
OrderId: ppRsp.Response.Id,
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *PaypalPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *PaypalPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
@@ -24,6 +24,32 @@ const (
|
|||||||
PaymentStateError PaymentState = "Error"
|
PaymentStateError PaymentState = "Error"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PaymentEnvWechatBrowser = "WechatBrowser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PayReq struct {
|
||||||
|
ProviderName string
|
||||||
|
ProductName string
|
||||||
|
PayerName string
|
||||||
|
PayerId string
|
||||||
|
PaymentName string
|
||||||
|
ProductDisplayName string
|
||||||
|
Price float64
|
||||||
|
Currency string
|
||||||
|
|
||||||
|
ReturnUrl string
|
||||||
|
NotifyUrl string
|
||||||
|
|
||||||
|
PaymentEnv string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayResp struct {
|
||||||
|
PayUrl string
|
||||||
|
OrderId string
|
||||||
|
AttachInfo map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
type NotifyResult struct {
|
type NotifyResult struct {
|
||||||
PaymentName string
|
PaymentName string
|
||||||
PaymentStatus PaymentState
|
PaymentStatus PaymentState
|
||||||
@@ -39,7 +65,7 @@ type NotifyResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PaymentProvider interface {
|
type PaymentProvider interface {
|
||||||
Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error)
|
Pay(req *PayReq) (*PayResp, error)
|
||||||
Notify(body []byte, orderId string) (*NotifyResult, error)
|
Notify(body []byte, orderId string) (*NotifyResult, error)
|
||||||
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
||||||
GetResponseError(err error) string
|
GetResponseError(err error) string
|
||||||
|
35
pp/stripe.go
35
pp/stripe.go
@@ -46,30 +46,30 @@ func NewStripePaymentProvider(PublishableKey, SecretKey string) (*StripePaymentP
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *StripePaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (payUrl string, orderId string, err error) {
|
func (pp *StripePaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
// Create a temp product
|
// Create a temp product
|
||||||
description := joinAttachString([]string{productName, productDisplayName, providerName})
|
description := joinAttachString([]string{r.ProductName, r.ProductDisplayName, r.ProviderName})
|
||||||
productParams := &stripe.ProductParams{
|
productParams := &stripe.ProductParams{
|
||||||
Name: stripe.String(productDisplayName),
|
Name: stripe.String(r.ProductDisplayName),
|
||||||
Description: stripe.String(description),
|
Description: stripe.String(description),
|
||||||
DefaultPriceData: &stripe.ProductDefaultPriceDataParams{
|
DefaultPriceData: &stripe.ProductDefaultPriceDataParams{
|
||||||
UnitAmount: stripe.Int64(priceFloat64ToInt64(price)),
|
UnitAmount: stripe.Int64(priceFloat64ToInt64(r.Price)),
|
||||||
Currency: stripe.String(currency),
|
Currency: stripe.String(r.Currency),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
sProduct, err := stripeProduct.New(productParams)
|
sProduct, err := stripeProduct.New(productParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create a price for an existing product
|
// Create a price for an existing product
|
||||||
priceParams := &stripe.PriceParams{
|
priceParams := &stripe.PriceParams{
|
||||||
Currency: stripe.String(currency),
|
Currency: stripe.String(r.Currency),
|
||||||
UnitAmount: stripe.Int64(priceFloat64ToInt64(price)),
|
UnitAmount: stripe.Int64(priceFloat64ToInt64(r.Price)),
|
||||||
Product: stripe.String(sProduct.ID),
|
Product: stripe.String(sProduct.ID),
|
||||||
}
|
}
|
||||||
sPrice, err := stripePrice.New(priceParams)
|
sPrice, err := stripePrice.New(priceParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Create a Checkout Session
|
// Create a Checkout Session
|
||||||
checkoutParams := &stripe.CheckoutSessionParams{
|
checkoutParams := &stripe.CheckoutSessionParams{
|
||||||
@@ -80,17 +80,21 @@ func (pp *StripePaymentProvider) Pay(providerName string, productName string, pa
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
|
Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
|
||||||
SuccessURL: stripe.String(returnUrl),
|
SuccessURL: stripe.String(r.ReturnUrl),
|
||||||
CancelURL: stripe.String(returnUrl),
|
CancelURL: stripe.String(r.ReturnUrl),
|
||||||
ClientReferenceID: stripe.String(paymentName),
|
ClientReferenceID: stripe.String(r.PaymentName),
|
||||||
ExpiresAt: stripe.Int64(time.Now().Add(30 * time.Minute).Unix()),
|
ExpiresAt: stripe.Int64(time.Now().Add(30 * time.Minute).Unix()),
|
||||||
}
|
}
|
||||||
checkoutParams.AddMetadata("product_description", description)
|
checkoutParams.AddMetadata("product_description", description)
|
||||||
sCheckout, err := stripeCheckout.New(checkoutParams)
|
sCheckout, err := stripeCheckout.New(checkoutParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return sCheckout.URL, sCheckout.ID, nil
|
payResp := &PayResp{
|
||||||
|
PayUrl: sCheckout.URL,
|
||||||
|
OrderId: sCheckout.ID,
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *StripePaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *StripePaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
@@ -128,6 +132,9 @@ func (pp *StripePaymentProvider) Notify(body []byte, orderId string) (*NotifyRes
|
|||||||
}
|
}
|
||||||
// Once payment is successful, the Checkout Session will contain a reference to the successful `PaymentIntent`
|
// Once payment is successful, the Checkout Session will contain a reference to the successful `PaymentIntent`
|
||||||
sIntent, err := stripeIntent.Get(sCheckout.PaymentIntent.ID, nil)
|
sIntent, err := stripeIntent.Get(sCheckout.PaymentIntent.ID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
productName string
|
productName string
|
||||||
productDisplayName string
|
productDisplayName string
|
||||||
|
@@ -63,27 +63,66 @@ func NewWechatPaymentProvider(mchId string, apiV3Key string, appId string, seria
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *WechatPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
func (pp *WechatPaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
bm := gopay.BodyMap{}
|
bm := gopay.BodyMap{}
|
||||||
bm.Set("attach", joinAttachString([]string{productDisplayName, productName, providerName}))
|
desc := joinAttachString([]string{r.ProductDisplayName, r.ProductName, r.ProviderName})
|
||||||
|
bm.Set("attach", desc)
|
||||||
bm.Set("appid", pp.AppId)
|
bm.Set("appid", pp.AppId)
|
||||||
bm.Set("description", productDisplayName)
|
bm.Set("description", r.ProductDisplayName)
|
||||||
bm.Set("notify_url", notifyUrl)
|
bm.Set("notify_url", r.NotifyUrl)
|
||||||
bm.Set("out_trade_no", paymentName)
|
bm.Set("out_trade_no", r.PaymentName)
|
||||||
bm.SetBodyMap("amount", func(bm gopay.BodyMap) {
|
bm.SetBodyMap("amount", func(bm gopay.BodyMap) {
|
||||||
bm.Set("total", priceFloat64ToInt64(price))
|
bm.Set("total", priceFloat64ToInt64(r.Price))
|
||||||
bm.Set("currency", currency)
|
bm.Set("currency", r.Currency)
|
||||||
})
|
})
|
||||||
|
// In Wechat browser, we use JSAPI
|
||||||
nativeRsp, err := pp.Client.V3TransactionNative(context.Background(), bm)
|
if r.PaymentEnv == PaymentEnvWechatBrowser {
|
||||||
if err != nil {
|
if r.PayerId == "" {
|
||||||
return "", "", err
|
return nil, errors.New("failed to get the payer's openid, please retry login")
|
||||||
|
}
|
||||||
|
bm.SetBodyMap("payer", func(bm gopay.BodyMap) {
|
||||||
|
bm.Set("openid", r.PayerId) // If the account is signup via Wechat, the PayerId is the Wechat OpenId e.g.oxW9O1ZDvgreSHuBSQDiQ2F055PI
|
||||||
|
})
|
||||||
|
jsapiRsp, err := pp.Client.V3TransactionJsapi(context.Background(), bm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if jsapiRsp.Code != wechat.Success {
|
||||||
|
return nil, errors.New(jsapiRsp.Error)
|
||||||
|
}
|
||||||
|
// use RSA256 to sign the pay request
|
||||||
|
params, err := pp.Client.PaySignOfJSAPI(pp.AppId, jsapiRsp.Response.PrepayId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
payResp := &PayResp{
|
||||||
|
PayUrl: "",
|
||||||
|
OrderId: r.PaymentName, // Wechat can use paymentName as the OutTradeNo to query order status
|
||||||
|
AttachInfo: map[string]interface{}{
|
||||||
|
"appId": params.AppId,
|
||||||
|
"timeStamp": params.TimeStamp,
|
||||||
|
"nonceStr": params.NonceStr,
|
||||||
|
"package": params.Package,
|
||||||
|
"signType": "RSA",
|
||||||
|
"paySign": params.PaySign,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
|
} else {
|
||||||
|
// In other case, we use NativeAPI
|
||||||
|
nativeRsp, err := pp.Client.V3TransactionNative(context.Background(), bm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if nativeRsp.Code != wechat.Success {
|
||||||
|
return nil, errors.New(nativeRsp.Error)
|
||||||
|
}
|
||||||
|
payResp := &PayResp{
|
||||||
|
PayUrl: nativeRsp.Response.CodeUrl,
|
||||||
|
OrderId: r.PaymentName, // Wechat can use paymentName as the OutTradeNo to query order status
|
||||||
|
}
|
||||||
|
return payResp, nil
|
||||||
}
|
}
|
||||||
if nativeRsp.Code != wechat.Success {
|
|
||||||
return "", "", errors.New(nativeRsp.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nativeRsp.Response.CodeUrl, paymentName, nil // Wechat can use paymentName as the OutTradeNo to query order status
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *WechatPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
func (pp *WechatPaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
@@ -55,15 +55,18 @@ func handleAccessRequest(w radius.ResponseWriter, r *radius.Request) {
|
|||||||
password := rfc2865.UserPassword_GetString(r.Packet)
|
password := rfc2865.UserPassword_GetString(r.Packet)
|
||||||
organization := rfc2865.Class_GetString(r.Packet)
|
organization := rfc2865.Class_GetString(r.Packet)
|
||||||
log.Printf("handleAccessRequest() username=%v, org=%v, password=%v", username, organization, password)
|
log.Printf("handleAccessRequest() username=%v, org=%v, password=%v", username, organization, password)
|
||||||
|
|
||||||
if organization == "" {
|
if organization == "" {
|
||||||
w.Write(r.Response(radius.CodeAccessReject))
|
w.Write(r.Response(radius.CodeAccessReject))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, msg := object.CheckUserPassword(organization, username, password, "en")
|
|
||||||
if msg != "" {
|
_, err := object.CheckUserPassword(organization, username, password, "en")
|
||||||
|
if err != nil {
|
||||||
w.Write(r.Response(radius.CodeAccessReject))
|
w.Write(r.Response(radius.CodeAccessReject))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Write(r.Response(radius.CodeAccessAccept))
|
w.Write(r.Response(radius.CodeAccessAccept))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -83,13 +83,12 @@ func AutoSigninFilter(ctx *context.Context) {
|
|||||||
password := ctx.Input.Query("password")
|
password := ctx.Input.Query("password")
|
||||||
if userId != "" && password != "" && ctx.Input.Query("grant_type") == "" {
|
if userId != "" && password != "" && ctx.Input.Query("grant_type") == "" {
|
||||||
owner, name := util.GetOwnerAndNameFromId(userId)
|
owner, name := util.GetOwnerAndNameFromId(userId)
|
||||||
_, msg := object.CheckUserPassword(owner, name, password, "en")
|
_, err = object.CheckUserPassword(owner, name, password, "en")
|
||||||
if msg != "" {
|
if err != nil {
|
||||||
responseError(ctx, msg)
|
responseError(ctx, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setSessionUser(ctx, userId)
|
setSessionUser(ctx, userId)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -204,7 +204,7 @@ func initAPI() {
|
|||||||
beego.Router("/api/run-syncer", &controllers.ApiController{}, "GET:RunSyncer")
|
beego.Router("/api/run-syncer", &controllers.ApiController{}, "GET:RunSyncer")
|
||||||
|
|
||||||
beego.Router("/api/get-certs", &controllers.ApiController{}, "GET:GetCerts")
|
beego.Router("/api/get-certs", &controllers.ApiController{}, "GET:GetCerts")
|
||||||
beego.Router("/api/get-globle-certs", &controllers.ApiController{}, "GET:GetGlobleCerts")
|
beego.Router("/api/get-global-certs", &controllers.ApiController{}, "GET:GetGlobalCerts")
|
||||||
beego.Router("/api/get-cert", &controllers.ApiController{}, "GET:GetCert")
|
beego.Router("/api/get-cert", &controllers.ApiController{}, "GET:GetCert")
|
||||||
beego.Router("/api/update-cert", &controllers.ApiController{}, "POST:UpdateCert")
|
beego.Router("/api/update-cert", &controllers.ApiController{}, "POST:UpdateCert")
|
||||||
beego.Router("/api/add-cert", &controllers.ApiController{}, "POST:AddCert")
|
beego.Router("/api/add-cert", &controllers.ApiController{}, "POST:AddCert")
|
||||||
|
@@ -129,7 +129,7 @@ func StaticFilter(ctx *context.Context) {
|
|||||||
path += urlPath
|
path += urlPath
|
||||||
}
|
}
|
||||||
|
|
||||||
if !util.FileExist(path) {
|
if strings.Contains(path, "/../") || !util.FileExist(path) {
|
||||||
path = webBuildFolder + "/index.html"
|
path = webBuildFolder + "/index.html"
|
||||||
}
|
}
|
||||||
if !util.FileExist(path) {
|
if !util.FileExist(path) {
|
||||||
|
@@ -2023,13 +2023,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/get-globle-certs": {
|
"/api/get-global-certs": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
"Cert API"
|
"Cert API"
|
||||||
],
|
],
|
||||||
"description": "get globle certs",
|
"description": "get globle certs",
|
||||||
"operationId": "ApiController.GetGlobleCerts",
|
"operationId": "ApiController.GetGlobalCerts",
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "The Response object",
|
"description": "The Response object",
|
||||||
|
@@ -1311,12 +1311,12 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/object.User'
|
$ref: '#/definitions/object.User'
|
||||||
/api/get-globle-certs:
|
/api/get-global-certs:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Cert API
|
- Cert API
|
||||||
description: get globle certs
|
description: get globle certs
|
||||||
operationId: ApiController.GetGlobleCerts
|
operationId: ApiController.GetGlobalCerts
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
description: The Response object
|
description: The Response object
|
||||||
|
@@ -171,10 +171,27 @@ class CertEditPage extends React.Component {
|
|||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.cryptoAlgorithm} onChange={(value => {
|
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.cryptoAlgorithm} onChange={(value => {
|
||||||
this.updateCertField("cryptoAlgorithm", value);
|
this.updateCertField("cryptoAlgorithm", value);
|
||||||
|
if (value === "RS256") {
|
||||||
|
this.updateCertField("bitSize", 2048);
|
||||||
|
} else if (value === "HS256" || value === "ES256") {
|
||||||
|
this.updateCertField("bitSize", 256);
|
||||||
|
} else if (value === "ES384") {
|
||||||
|
this.updateCertField("bitSize", 384);
|
||||||
|
} else if (value === "ES521") {
|
||||||
|
this.updateCertField("bitSize", 521);
|
||||||
|
} else {
|
||||||
|
this.updateCertField("bitSize", 0);
|
||||||
|
}
|
||||||
|
this.updateCertField("certificate", "");
|
||||||
|
this.updateCertField("privateKey", "");
|
||||||
})}>
|
})}>
|
||||||
{
|
{
|
||||||
[
|
[
|
||||||
{id: "RS256", name: "RS256"},
|
{id: "RS256", name: "RS256 (RSA + SHA256)"},
|
||||||
|
{id: "HS256", name: "HS256 (HMAC + SHA256)"},
|
||||||
|
{id: "ES256", name: "ES256 (ECDSA using P-256 + SHA256)"},
|
||||||
|
{id: "ES384", name: "ES384 (ECDSA using P-384 + SHA256)"},
|
||||||
|
{id: "ES521", name: "ES521 (ECDSA using P-521 + SHA256)"},
|
||||||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||||
}
|
}
|
||||||
</Select>
|
</Select>
|
||||||
@@ -185,9 +202,15 @@ class CertEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("cert:Bit size"), i18next.t("cert:Bit size - Tooltip"))} :
|
{Setting.getLabel(i18next.t("cert:Bit size"), i18next.t("cert:Bit size - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<InputNumber value={this.state.cert.bitSize} onChange={value => {
|
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.bitSize} onChange={(value => {
|
||||||
this.updateCertField("bitSize", value);
|
this.updateCertField("bitSize", value);
|
||||||
}} />
|
this.updateCertField("certificate", "");
|
||||||
|
this.updateCertField("privateKey", "");
|
||||||
|
})}>
|
||||||
|
{
|
||||||
|
Setting.getCryptoAlgorithmOptions(this.state.cert.cryptoAlgorithm).map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
@@ -205,14 +228,14 @@ class CertEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("cert:Certificate"), i18next.t("cert:Certificate - Tooltip"))} :
|
{Setting.getLabel(i18next.t("cert:Certificate"), i18next.t("cert:Certificate - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={editorWidth} >
|
<Col span={editorWidth} >
|
||||||
<Button style={{marginRight: "10px", marginBottom: "10px"}} onClick={() => {
|
<Button style={{marginRight: "10px", marginBottom: "10px"}} disabled={this.state.cert.certificate === ""} onClick={() => {
|
||||||
copy(this.state.cert.certificate);
|
copy(this.state.cert.certificate);
|
||||||
Setting.showMessage("success", i18next.t("cert:Certificate copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("cert:Certificate copied to clipboard successfully"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18next.t("cert:Copy certificate")}
|
{i18next.t("cert:Copy certificate")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="primary" onClick={() => {
|
<Button type="primary" disabled={this.state.cert.certificate === ""} onClick={() => {
|
||||||
const blob = new Blob([this.state.cert.certificate], {type: "text/plain;charset=utf-8"});
|
const blob = new Blob([this.state.cert.certificate], {type: "text/plain;charset=utf-8"});
|
||||||
FileSaver.saveAs(blob, "token_jwt_key.pem");
|
FileSaver.saveAs(blob, "token_jwt_key.pem");
|
||||||
}}
|
}}
|
||||||
@@ -228,14 +251,14 @@ class CertEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("cert:Private key"), i18next.t("cert:Private key - Tooltip"))} :
|
{Setting.getLabel(i18next.t("cert:Private key"), i18next.t("cert:Private key - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={editorWidth} >
|
<Col span={editorWidth} >
|
||||||
<Button style={{marginRight: "10px", marginBottom: "10px"}} onClick={() => {
|
<Button style={{marginRight: "10px", marginBottom: "10px"}} disabled={this.state.cert.privateKey === ""} onClick={() => {
|
||||||
copy(this.state.cert.privateKey);
|
copy(this.state.cert.privateKey);
|
||||||
Setting.showMessage("success", i18next.t("cert:Private key copied to clipboard successfully"));
|
Setting.showMessage("success", i18next.t("cert:Private key copied to clipboard successfully"));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18next.t("cert:Copy private key")}
|
{i18next.t("cert:Copy private key")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="primary" onClick={() => {
|
<Button type="primary" disabled={this.state.cert.privateKey === ""} onClick={() => {
|
||||||
const blob = new Blob([this.state.cert.privateKey], {type: "text/plain;charset=utf-8"});
|
const blob = new Blob([this.state.cert.privateKey], {type: "text/plain;charset=utf-8"});
|
||||||
FileSaver.saveAs(blob, "token_jwt_key.key");
|
FileSaver.saveAs(blob, "token_jwt_key.key");
|
||||||
}}
|
}}
|
||||||
@@ -265,6 +288,7 @@ class CertEditPage extends React.Component {
|
|||||||
this.props.history.push("/certs");
|
this.props.history.push("/certs");
|
||||||
} else {
|
} else {
|
||||||
this.props.history.push(`/certs/${this.state.cert.owner}/${this.state.cert.name}`);
|
this.props.history.push(`/certs/${this.state.cert.owner}/${this.state.cert.name}`);
|
||||||
|
this.getCert();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
|
@@ -239,7 +239,7 @@ class CertListPage extends BaseListPage {
|
|||||||
value = params.type;
|
value = params.type;
|
||||||
}
|
}
|
||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
(Setting.isDefaultOrganizationSelected(this.props.account) ? CertBackend.getGlobleCerts(params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
(Setting.isDefaultOrganizationSelected(this.props.account) ? CertBackend.getGlobalCerts(params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
: CertBackend.getCerts(Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
: CertBackend.getCerts(Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@@ -323,6 +323,16 @@ class OrganizationEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Master verification code"), i18next.t("general:Master verification code - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.organization.masterVerificationCode} onChange={e => {
|
||||||
|
this.updateOrganizationField("masterVerificationCode", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||||
{Setting.getLabel(i18next.t("organization:Init score"), i18next.t("organization:Init score - Tooltip"))} :
|
{Setting.getLabel(i18next.t("organization:Init score"), i18next.t("organization:Init score - Tooltip"))} :
|
||||||
|
@@ -101,7 +101,7 @@ class PaymentResultPage extends React.Component {
|
|||||||
payment: payment,
|
payment: payment,
|
||||||
});
|
});
|
||||||
if (payment.state === "Created") {
|
if (payment.state === "Created") {
|
||||||
if (["PayPal", "Stripe", "Alipay"].includes(payment.type)) {
|
if (["PayPal", "Stripe", "Alipay", "WeChat Pay"].includes(payment.type)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
timeout: setTimeout(async() => {
|
timeout: setTimeout(async() => {
|
||||||
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
|
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
|
||||||
|
@@ -303,7 +303,7 @@ class PermissionEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} :
|
{Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select disabled={!this.hasRoleDefinition(this.state.model)} virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.permission.roles}
|
<Select disabled={!this.hasRoleDefinition(this.state.model)} placeholder={this.hasRoleDefinition(this.state.model) ? "" : "This field is disabled because the model is empty or it doesn't support RBAC (in another word, doesn't contain [role_definition])"} virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.permission.roles}
|
||||||
onChange={(value => {this.updatePermissionField("roles", value);})}
|
onChange={(value => {this.updatePermissionField("roles", value);})}
|
||||||
options={[
|
options={[
|
||||||
Setting.getOption(i18next.t("organization:All"), "*"),
|
Setting.getOption(i18next.t("organization:All"), "*"),
|
||||||
@@ -323,7 +323,7 @@ class PermissionEditPage extends React.Component {
|
|||||||
})}
|
})}
|
||||||
options={[
|
options={[
|
||||||
Setting.getOption(i18next.t("organization:All"), "*"),
|
Setting.getOption(i18next.t("organization:All"), "*"),
|
||||||
...this.state.permission.domains.map((domain) => Setting.getOption(domain, domain)),
|
...this.state.permission.domains.filter(domain => domain !== "*").map((domain) => Setting.getOption(domain, domain)),
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
@@ -44,7 +44,7 @@ class PermissionListPage extends BaseListPage {
|
|||||||
submitter: this.props.account.name,
|
submitter: this.props.account.name,
|
||||||
approver: "",
|
approver: "",
|
||||||
approveTime: "",
|
approveTime: "",
|
||||||
state: "Pending",
|
state: Setting.isLocalAdminUser(this.props.account) ? "Approved" : "Pending",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,6 +31,7 @@ class ProductBuyPage extends React.Component {
|
|||||||
pricingName: props?.pricingName ?? props?.match?.params?.pricingName ?? null,
|
pricingName: props?.pricingName ?? props?.match?.params?.pricingName ?? null,
|
||||||
planName: params.get("plan"),
|
planName: params.get("plan"),
|
||||||
userName: params.get("user"),
|
userName: params.get("user"),
|
||||||
|
paymentEnv: "",
|
||||||
product: null,
|
product: null,
|
||||||
pricing: props?.pricing ?? null,
|
pricing: props?.pricing ?? null,
|
||||||
plan: null,
|
plan: null,
|
||||||
@@ -38,8 +39,21 @@ class ProductBuyPage extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPaymentEnv() {
|
||||||
|
let env = "";
|
||||||
|
const ua = navigator.userAgent.toLocaleLowerCase();
|
||||||
|
// Only support Wechat Pay in Wechat Browser for mobile devices
|
||||||
|
if (ua.indexOf("micromessenger") !== -1 && ua.indexOf("mobile") !== -1) {
|
||||||
|
env = "WechatBrowser";
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
paymentEnv: env,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillMount() {
|
UNSAFE_componentWillMount() {
|
||||||
this.getProduct();
|
this.getProduct();
|
||||||
|
this.getPaymentEnv();
|
||||||
}
|
}
|
||||||
|
|
||||||
setStateAsync(state) {
|
setStateAsync(state) {
|
||||||
@@ -127,23 +141,74 @@ class ProductBuyPage extends React.Component {
|
|||||||
return `${this.getCurrencySymbol(product)}${product?.price} (${this.getCurrencyText(product)})`;
|
return `${this.getCurrencySymbol(product)}${product?.price} (${this.getCurrencyText(product)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call Weechat Pay via jsapi
|
||||||
|
onBridgeReady(attachInfo) {
|
||||||
|
const {WeixinJSBridge} = window;
|
||||||
|
// Setting.showMessage("success", "attachInfo is " + JSON.stringify(attachInfo));
|
||||||
|
this.setState({
|
||||||
|
isPlacingOrder: false,
|
||||||
|
});
|
||||||
|
WeixinJSBridge.invoke(
|
||||||
|
"getBrandWCPayRequest", {
|
||||||
|
"appId": attachInfo.appId,
|
||||||
|
"timeStamp": attachInfo.timeStamp,
|
||||||
|
"nonceStr": attachInfo.nonceStr,
|
||||||
|
"package": attachInfo.package,
|
||||||
|
"signType": attachInfo.signType,
|
||||||
|
"paySign": attachInfo.paySign,
|
||||||
|
},
|
||||||
|
function(res) {
|
||||||
|
if (res.err_msg === "get_brand_wcpay_request:ok") {
|
||||||
|
Setting.goToLink(attachInfo.payment.successUrl);
|
||||||
|
return ;
|
||||||
|
} else {
|
||||||
|
if (res.err_msg === "get_brand_wcpay_request:cancel") {
|
||||||
|
Setting.showMessage("error", i18next.t("product:Payment cancelled"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", i18next.t("product:Payment failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In Wechat browser, call this function to pay via jsapi
|
||||||
|
callWechatPay(attachInfo) {
|
||||||
|
const {WeixinJSBridge} = window;
|
||||||
|
if (typeof WeixinJSBridge === "undefined") {
|
||||||
|
if (document.addEventListener) {
|
||||||
|
document.addEventListener("WeixinJSBridgeReady", () => this.onBridgeReady(attachInfo), false);
|
||||||
|
} else if (document.attachEvent) {
|
||||||
|
document.attachEvent("WeixinJSBridgeReady", () => this.onBridgeReady(attachInfo));
|
||||||
|
document.attachEvent("onWeixinJSBridgeReady", () => this.onBridgeReady(attachInfo));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.onBridgeReady(attachInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buyProduct(product, provider) {
|
buyProduct(product, provider) {
|
||||||
this.setState({
|
this.setState({
|
||||||
isPlacingOrder: true,
|
isPlacingOrder: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
ProductBackend.buyProduct(product.owner, product.name, provider.name, this.state.pricingName ?? "", this.state.planName ?? "", this.state.userName ?? "")
|
ProductBackend.buyProduct(product.owner, product.name, provider.name, this.state.pricingName ?? "", this.state.planName ?? "", this.state.userName ?? "", this.state.paymentEnv)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
const payment = res.data;
|
const payment = res.data;
|
||||||
|
const attachInfo = res.data2;
|
||||||
let payUrl = payment.payUrl;
|
let payUrl = payment.payUrl;
|
||||||
if (provider.type === "WeChat Pay") {
|
if (provider.type === "WeChat Pay") {
|
||||||
|
if (this.state.paymentEnv === "WechatBrowser") {
|
||||||
|
attachInfo.payment = payment;
|
||||||
|
this.callWechatPay(attachInfo);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
payUrl = `/qrcode/${payment.owner}/${payment.name}?providerName=${provider.name}&payUrl=${encodeURI(payment.payUrl)}&successUrl=${encodeURI(payment.successUrl)}`;
|
payUrl = `/qrcode/${payment.owner}/${payment.name}?providerName=${provider.name}&payUrl=${encodeURI(payment.payUrl)}&successUrl=${encodeURI(payment.successUrl)}`;
|
||||||
}
|
}
|
||||||
Setting.goToLink(payUrl);
|
Setting.goToLink(payUrl);
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isPlacingOrder: false,
|
isPlacingOrder: false,
|
||||||
});
|
});
|
||||||
@@ -218,7 +283,7 @@ class ProductBuyPage extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className="login-content">
|
<div className="login-content">
|
||||||
<Spin spinning={this.state.isPlacingOrder} size="large" tip={i18next.t("product:Placing order...")} style={{paddingTop: "10%"}} >
|
<Spin spinning={this.state.isPlacingOrder} size="large" tip={i18next.t("product:Placing order...")} style={{paddingTop: "10%"}} >
|
||||||
<Descriptions title={<span style={{fontSize: 28}}>{i18next.t("product:Buy Product")}</span>} bordered>
|
<Descriptions title={<span style={Setting.isMobile() ? {fontSize: 20} : {fontSize: 28}}>{i18next.t("product:Buy Product")}</span>} bordered>
|
||||||
<Descriptions.Item label={i18next.t("general:Name")} span={3}>
|
<Descriptions.Item label={i18next.t("general:Name")} span={3}>
|
||||||
<span style={{fontSize: 25}}>
|
<span style={{fontSize: 25}}>
|
||||||
{Setting.getLanguageText(product?.displayName)}
|
{Setting.getLanguageText(product?.displayName)}
|
||||||
|
@@ -1069,6 +1069,38 @@ export function getProviderTypeOptions(category) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCryptoAlgorithmOptions(cryptoAlgorithm) {
|
||||||
|
if (cryptoAlgorithm === "RS256") {
|
||||||
|
return (
|
||||||
|
[
|
||||||
|
{id: 1024, name: "1024"},
|
||||||
|
{id: 2048, name: "2048"},
|
||||||
|
{id: 4096, name: "4096"},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else if (cryptoAlgorithm === "HS256" || cryptoAlgorithm === "ES256") {
|
||||||
|
return (
|
||||||
|
[
|
||||||
|
{id: 256, name: "256"},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else if (cryptoAlgorithm === "ES384") {
|
||||||
|
return (
|
||||||
|
[
|
||||||
|
{id: 384, name: "384"},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else if (cryptoAlgorithm === "ES521") {
|
||||||
|
return (
|
||||||
|
[
|
||||||
|
{id: 521, name: "521"},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function renderLogo(application) {
|
export function renderLogo(application) {
|
||||||
if (application === null) {
|
if (application === null) {
|
||||||
return null;
|
return null;
|
||||||
|
@@ -337,7 +337,7 @@ class LoginPage extends React.Component {
|
|||||||
const casParams = Util.getCasParameters();
|
const casParams = Util.getCasParameters();
|
||||||
values["type"] = this.state.type;
|
values["type"] = this.state.type;
|
||||||
AuthBackend.loginCas(values, casParams).then((res) => {
|
AuthBackend.loginCas(values, casParams).then((res) => {
|
||||||
if (res.status === "ok") {
|
const loginHandler = (res) => {
|
||||||
let msg = "Logged in successfully. ";
|
let msg = "Logged in successfully. ";
|
||||||
if (casParams.service === "") {
|
if (casParams.service === "") {
|
||||||
// If service was not specified, Casdoor must display a message notifying the client that it has successfully initiated a single sign-on session.
|
// If service was not specified, Casdoor must display a message notifying the client that it has successfully initiated a single sign-on session.
|
||||||
@@ -351,6 +351,28 @@ class LoginPage extends React.Component {
|
|||||||
newUrl.searchParams.append("ticket", st);
|
newUrl.searchParams.append("ticket", st);
|
||||||
window.location.href = newUrl.toString();
|
window.location.href = newUrl.toString();
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (res.status === "ok") {
|
||||||
|
if (res.data === NextMfa) {
|
||||||
|
this.setState({
|
||||||
|
getVerifyTotp: () => {
|
||||||
|
return (
|
||||||
|
<MfaAuthVerifyForm
|
||||||
|
mfaProps={res.data2}
|
||||||
|
formValues={values}
|
||||||
|
authParams={casParams}
|
||||||
|
application={this.getApplicationObj()}
|
||||||
|
onFail={() => {
|
||||||
|
Setting.showMessage("error", i18next.t("mfa:Verification failed"));
|
||||||
|
}}
|
||||||
|
onSuccess={(res) => loginHandler(res)}
|
||||||
|
/>);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
loginHandler(res);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
|
||||||
}
|
}
|
||||||
@@ -361,7 +383,7 @@ class LoginPage extends React.Component {
|
|||||||
this.populateOauthValues(values);
|
this.populateOauthValues(values);
|
||||||
AuthBackend.login(values, oAuthParams)
|
AuthBackend.login(values, oAuthParams)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const callback = (res) => {
|
const loginHandler = (res) => {
|
||||||
const responseType = values["type"];
|
const responseType = values["type"];
|
||||||
|
|
||||||
if (responseType === "login") {
|
if (responseType === "login") {
|
||||||
@@ -396,12 +418,12 @@ class LoginPage extends React.Component {
|
|||||||
<MfaAuthVerifyForm
|
<MfaAuthVerifyForm
|
||||||
mfaProps={res.data2}
|
mfaProps={res.data2}
|
||||||
formValues={values}
|
formValues={values}
|
||||||
oAuthParams={oAuthParams}
|
authParams={oAuthParams}
|
||||||
application={this.getApplicationObj()}
|
application={this.getApplicationObj()}
|
||||||
onFail={() => {
|
onFail={() => {
|
||||||
Setting.showMessage("error", i18next.t("mfa:Verification failed"));
|
Setting.showMessage("error", i18next.t("mfa:Verification failed"));
|
||||||
}}
|
}}
|
||||||
onSuccess={(res) => callback(res)}
|
onSuccess={(res) => loginHandler(res)}
|
||||||
/>);
|
/>);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -414,7 +436,7 @@ class LoginPage extends React.Component {
|
|||||||
const sub = res.data2;
|
const sub = res.data2;
|
||||||
Setting.goToLink(`/buy-plan/${sub.owner}/${sub.pricing}/result?subscription=${sub.name}`);
|
Setting.goToLink(`/buy-plan/${sub.owner}/${sub.pricing}/result?subscription=${sub.name}`);
|
||||||
} else {
|
} else {
|
||||||
callback(res);
|
loginHandler(res);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
|
||||||
|
@@ -262,7 +262,7 @@ class PromptPage extends React.Component {
|
|||||||
|
|
||||||
initSteps(user, application) {
|
initSteps(user, application) {
|
||||||
const steps = [];
|
const steps = [];
|
||||||
if (!Setting.isPromptAnswered(user, application) && this.state.promptType === "provider") {
|
if (Setting.hasPromptPage(application)) {
|
||||||
steps.push({
|
steps.push({
|
||||||
content: this.renderPromptProvider(application),
|
content: this.renderPromptProvider(application),
|
||||||
name: "provider",
|
name: "provider",
|
||||||
|
@@ -382,7 +382,7 @@ export function getAuthUrl(application, provider, method) {
|
|||||||
let redirectUri = `${window.location.origin}/callback`;
|
let redirectUri = `${window.location.origin}/callback`;
|
||||||
const scope = authInfo[provider.type].scope;
|
const scope = authInfo[provider.type].scope;
|
||||||
|
|
||||||
const isShortState = provider.type === "WeChat" && navigator.userAgent.includes("MicroMessenger");
|
const isShortState = (provider.type === "WeChat" && navigator.userAgent.includes("MicroMessenger")) || (provider.type === "Twitter");
|
||||||
const state = Util.getStateFromQueryParams(application.name, provider.name, method, isShortState);
|
const state = Util.getStateFromQueryParams(application.name, provider.name, method, isShortState);
|
||||||
const codeChallenge = "P3S-a7dr8bgM4bF6vOyiKkKETDl16rcAzao9F8UIL1Y"; // SHA256(Base64-URL-encode("casdoor-verifier"))
|
const codeChallenge = "P3S-a7dr8bgM4bF6vOyiKkKETDl16rcAzao9F8UIL1Y"; // SHA256(Base64-URL-encode("casdoor-verifier"))
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@ import MfaVerifyTotpForm from "./MfaVerifyTotpForm";
|
|||||||
export const NextMfa = "NextMfa";
|
export const NextMfa = "NextMfa";
|
||||||
export const RequiredMfa = "RequiredMfa";
|
export const RequiredMfa = "RequiredMfa";
|
||||||
|
|
||||||
export function MfaAuthVerifyForm({formValues, oAuthParams, mfaProps, application, onSuccess, onFail}) {
|
export function MfaAuthVerifyForm({formValues, authParams, mfaProps, application, onSuccess, onFail}) {
|
||||||
formValues.password = "";
|
formValues.password = "";
|
||||||
formValues.username = "";
|
formValues.username = "";
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -34,7 +34,8 @@ export function MfaAuthVerifyForm({formValues, oAuthParams, mfaProps, applicatio
|
|||||||
const verify = ({passcode}) => {
|
const verify = ({passcode}) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const values = {...formValues, passcode, mfaType};
|
const values = {...formValues, passcode, mfaType};
|
||||||
AuthBackend.login(values, oAuthParams).then((res) => {
|
const loginFunction = formValues.type === "cas" ? AuthBackend.loginCas : AuthBackend.login;
|
||||||
|
loginFunction(values, authParams).then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
onSuccess(res);
|
onSuccess(res);
|
||||||
} else {
|
} else {
|
||||||
@@ -49,7 +50,9 @@ export function MfaAuthVerifyForm({formValues, oAuthParams, mfaProps, applicatio
|
|||||||
|
|
||||||
const recover = () => {
|
const recover = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
AuthBackend.login({...formValues, recoveryCode}, oAuthParams).then(res => {
|
const values = {...formValues, recoveryCode};
|
||||||
|
const loginFunction = formValues.type === "cas" ? AuthBackend.loginCas : AuthBackend.login;
|
||||||
|
loginFunction(values, authParams).then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
onSuccess(res);
|
onSuccess(res);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -24,8 +24,8 @@ export function getCerts(owner, page = "", pageSize = "", field = "", value = ""
|
|||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGlobleCerts(page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
|
export function getGlobalCerts(page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
|
||||||
return fetch(`${Setting.ServerUrl}/api/get-globle-certs?&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
|
return fetch(`${Setting.ServerUrl}/api/get-global-certs?&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
headers: {
|
headers: {
|
||||||
|
@@ -70,8 +70,8 @@ export function deleteProduct(product) {
|
|||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buyProduct(owner, name, providerName, pricingName = "", planName = "", userName = "") {
|
export function buyProduct(owner, name, providerName, pricingName = "", planName = "", userName = "", paymentEnv = "") {
|
||||||
return fetch(`${Setting.ServerUrl}/api/buy-product?id=${owner}/${encodeURIComponent(name)}&providerName=${providerName}&pricingName=${pricingName}&planName=${planName}&userName=${userName}`, {
|
return fetch(`${Setting.ServerUrl}/api/buy-product?id=${owner}/${encodeURIComponent(name)}&providerName=${providerName}&pricingName=${pricingName}&planName=${planName}&userName=${userName}&paymentEnv=${paymentEnv}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: "include",
|
credentials: "include",
|
||||||
headers: {
|
headers: {
|
||||||
|
Reference in New Issue
Block a user