mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-02 02:20:30 +08:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2bb8491499 | ||
![]() |
293283ed25 | ||
![]() |
9cb519d1e9 | ||
![]() |
fb9b8f1662 | ||
![]() |
2fec3f72ae | ||
![]() |
11695220a8 | ||
![]() |
155660b0d7 | ||
![]() |
1c72f5300c | ||
![]() |
3dd56195d9 | ||
![]() |
8865244262 | ||
![]() |
3400fa1e9c | ||
![]() |
bdc5c92ef0 | ||
![]() |
4e3eedf246 | ||
![]() |
8e98fc5a9f |
@@ -217,7 +217,7 @@ func (c *ApiController) Signup() {
|
||||
record.User = user.Name
|
||||
util.SafeGoroutine(func() { object.AddRecord(record) })
|
||||
|
||||
userId := fmt.Sprintf("%s/%s", user.Owner, user.Name)
|
||||
userId := user.GetId()
|
||||
util.LogInfo(c.Ctx, "API: [%s] is signed up as new user", userId)
|
||||
|
||||
c.ResponseOk(userId)
|
||||
|
@@ -21,7 +21,8 @@ import (
|
||||
)
|
||||
|
||||
type LinkForm struct {
|
||||
ProviderType string `json:"providerType"`
|
||||
ProviderType string `json:"providerType"`
|
||||
User object.User `json:"user"`
|
||||
}
|
||||
|
||||
// Unlink ...
|
||||
@@ -40,16 +41,55 @@ func (c *ApiController) Unlink() {
|
||||
}
|
||||
providerType := form.ProviderType
|
||||
|
||||
// the user will be unlinked from the provider
|
||||
unlinkedUser := form.User
|
||||
user := object.GetUser(userId)
|
||||
value := object.GetUserField(user, providerType)
|
||||
|
||||
if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin {
|
||||
// if the user is not the same as the one we are unlinking, we need to make sure the user is the global admin.
|
||||
c.ResponseError("You are not the global admin, you can't unlink other users")
|
||||
return
|
||||
}
|
||||
|
||||
if user.Id == unlinkedUser.Id && !user.IsGlobalAdmin {
|
||||
// if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error.
|
||||
application := object.GetApplicationByUser(user)
|
||||
if application == nil {
|
||||
c.ResponseError("You can't unlink yourself, you are not a member of any application")
|
||||
return
|
||||
}
|
||||
|
||||
if len(application.Providers) == 0 {
|
||||
c.ResponseError("This application has no providers")
|
||||
return
|
||||
}
|
||||
|
||||
provider := application.GetProviderItemByType(providerType)
|
||||
if provider == nil {
|
||||
c.ResponseError("This application has no providers of type " + providerType)
|
||||
return
|
||||
}
|
||||
|
||||
if !provider.CanUnlink {
|
||||
c.ResponseError("This provider can't be unlinked")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// only two situations can happen here
|
||||
// 1. the user is the global admin
|
||||
// 2. the user is unlinking themselves and provider can be unlinked
|
||||
|
||||
value := object.GetUserField(&unlinkedUser, providerType)
|
||||
|
||||
if value == "" {
|
||||
c.ResponseError("Please link first", value)
|
||||
return
|
||||
}
|
||||
|
||||
object.ClearUserOAuthProperties(user, providerType)
|
||||
object.ClearUserOAuthProperties(&unlinkedUser, providerType)
|
||||
|
||||
object.LinkUserAccount(user, providerType, "")
|
||||
object.LinkUserAccount(&unlinkedUser, providerType, "")
|
||||
c.ResponseOk()
|
||||
}
|
||||
|
@@ -80,12 +80,16 @@ func (c *ApiController) GetUsers() {
|
||||
// @Title GetUser
|
||||
// @Tag User API
|
||||
// @Description get user
|
||||
// @Param id query string true "The id of the user"
|
||||
// @Param id query string true "The id of the user"
|
||||
// @Param owner query string false "The owner of the user"
|
||||
// @Param email query string false "The email of the user"
|
||||
// @Param phone query string false "The phone of the user"
|
||||
// @Success 200 {object} object.User The Response object
|
||||
// @router /get-user [get]
|
||||
func (c *ApiController) GetUser() {
|
||||
id := c.Input().Get("id")
|
||||
email := c.Input().Get("email")
|
||||
phone := c.Input().Get("phone")
|
||||
userId := c.Input().Get("userId")
|
||||
|
||||
owner := c.Input().Get("owner")
|
||||
@@ -96,7 +100,7 @@ func (c *ApiController) GetUser() {
|
||||
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", owner))
|
||||
if !organization.IsProfilePublic {
|
||||
requestUserId := c.GetSessionUsername()
|
||||
hasPermission, err := object.CheckUserPermission(requestUserId, id, false)
|
||||
hasPermission, err := object.CheckUserPermission(requestUserId, id, owner, false)
|
||||
if !hasPermission {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -104,14 +108,22 @@ func (c *ApiController) GetUser() {
|
||||
}
|
||||
|
||||
var user *object.User
|
||||
if email != "" {
|
||||
switch {
|
||||
case email != "":
|
||||
user = object.GetUserByEmail(owner, email)
|
||||
} else if userId != "" {
|
||||
case phone != "":
|
||||
user = object.GetUserByPhone(owner, phone)
|
||||
case userId != "":
|
||||
user = object.GetUserByUserId(owner, userId)
|
||||
} else {
|
||||
default:
|
||||
user = object.GetUser(id)
|
||||
}
|
||||
|
||||
roles := object.GetRolesByUser(user.GetId())
|
||||
user.Roles = roles
|
||||
permissions := object.GetPermissionsByUser(user.GetId())
|
||||
user.Permissions = permissions
|
||||
|
||||
c.Data["json"] = object.GetMaskedUser(user)
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -252,7 +264,7 @@ func (c *ApiController) SetPassword() {
|
||||
requestUserId := c.GetSessionUsername()
|
||||
userId := fmt.Sprintf("%s/%s", userOwner, userName)
|
||||
|
||||
hasPermission, err := object.CheckUserPermission(requestUserId, userId, true)
|
||||
hasPermission, err := object.CheckUserPermission(requestUserId, userId, userOwner, true)
|
||||
if !hasPermission {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
@@ -49,8 +49,24 @@ func (c *ApiController) SendVerificationCode() {
|
||||
applicationId := c.Ctx.Request.Form.Get("applicationId")
|
||||
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
|
||||
|
||||
if destType == "" || dest == "" || applicationId == "" || !strings.Contains(applicationId, "/") || checkType == "" {
|
||||
c.ResponseError("Missing parameter.")
|
||||
if destType == "" {
|
||||
c.ResponseError("Missing parameter: type.")
|
||||
return
|
||||
}
|
||||
if dest == "" {
|
||||
c.ResponseError("Missing parameter: dest.")
|
||||
return
|
||||
}
|
||||
if applicationId == "" {
|
||||
c.ResponseError("Missing parameter: applicationId.")
|
||||
return
|
||||
}
|
||||
if !strings.Contains(applicationId, "/") {
|
||||
c.ResponseError("Wrong parameter: applicationId.")
|
||||
return
|
||||
}
|
||||
if checkType == "" {
|
||||
c.ResponseError("Missing parameter: checkType.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -152,13 +168,35 @@ func (c *ApiController) ResetEmailOrPhone() {
|
||||
}
|
||||
|
||||
checkDest := dest
|
||||
org := object.GetOrganizationByUser(user)
|
||||
if destType == "phone" {
|
||||
org := object.GetOrganizationByUser(user)
|
||||
phoneItem := object.GetAccountItemByName("Phone", org)
|
||||
if phoneItem == nil {
|
||||
c.ResponseError("Unable to get the phone modify rule.")
|
||||
return
|
||||
}
|
||||
|
||||
if pass, errMsg := object.CheckAccountItemModifyRule(phoneItem, user); !pass {
|
||||
c.ResponseError(errMsg)
|
||||
return
|
||||
}
|
||||
|
||||
phonePrefix := "86"
|
||||
if org != nil && org.PhonePrefix != "" {
|
||||
phonePrefix = org.PhonePrefix
|
||||
}
|
||||
checkDest = fmt.Sprintf("+%s%s", phonePrefix, dest)
|
||||
} else if destType == "email" {
|
||||
emailItem := object.GetAccountItemByName("Email", org)
|
||||
if emailItem == nil {
|
||||
c.ResponseError("Unable to get the email modify rule.")
|
||||
return
|
||||
}
|
||||
|
||||
if pass, errMsg := object.CheckAccountItemModifyRule(emailItem, user); !pass {
|
||||
c.ResponseError(errMsg)
|
||||
return
|
||||
}
|
||||
}
|
||||
if ret := object.CheckVerificationCode(checkDest, code); len(ret) != 0 {
|
||||
c.ResponseError(ret)
|
||||
|
@@ -139,7 +139,7 @@
|
||||
"cryptoAlgorithm": "RS256",
|
||||
"bitSize": 4096,
|
||||
"expireInYears": 20,
|
||||
"publicKey": "",
|
||||
"certificate": "",
|
||||
"privateKey": ""
|
||||
}
|
||||
],
|
||||
|
@@ -33,7 +33,7 @@ type Cert struct {
|
||||
BitSize int `json:"bitSize"`
|
||||
ExpireInYears int `json:"expireInYears"`
|
||||
|
||||
PublicKey string `xorm:"mediumtext" json:"publicKey"`
|
||||
Certificate string `xorm:"mediumtext" json:"certificate"`
|
||||
PrivateKey string `xorm:"mediumtext" json:"privateKey"`
|
||||
AuthorityPublicKey string `xorm:"mediumtext" json:"authorityPublicKey"`
|
||||
AuthorityRootPublicKey string `xorm:"mediumtext" json:"authorityRootPublicKey"`
|
||||
@@ -123,9 +123,9 @@ func UpdateCert(id string, cert *Cert) bool {
|
||||
}
|
||||
|
||||
func AddCert(cert *Cert) bool {
|
||||
if cert.PublicKey == "" || cert.PrivateKey == "" {
|
||||
publicKey, privateKey := generateRsaKeys(cert.BitSize, cert.ExpireInYears, cert.Name, cert.Owner)
|
||||
cert.PublicKey = publicKey
|
||||
if cert.Certificate == "" || cert.PrivateKey == "" {
|
||||
certificate, privateKey := generateRsaKeys(cert.BitSize, cert.ExpireInYears, cert.Name, cert.Owner)
|
||||
cert.Certificate = certificate
|
||||
cert.PrivateKey = privateKey
|
||||
}
|
||||
|
||||
|
@@ -197,14 +197,18 @@ func filterField(field string) bool {
|
||||
return reFieldWhiteList.MatchString(field)
|
||||
}
|
||||
|
||||
func CheckUserPermission(requestUserId, userId string, strict bool) (bool, error) {
|
||||
func CheckUserPermission(requestUserId, userId, userOwner string, strict bool) (bool, error) {
|
||||
if requestUserId == "" {
|
||||
return false, fmt.Errorf("please login first")
|
||||
}
|
||||
|
||||
targetUser := GetUser(userId)
|
||||
if targetUser == nil {
|
||||
return false, fmt.Errorf("the user: %s doesn't exist", userId)
|
||||
if userId != "" {
|
||||
targetUser := GetUser(userId)
|
||||
if targetUser == nil {
|
||||
return false, fmt.Errorf("the user: %s doesn't exist", userId)
|
||||
}
|
||||
|
||||
userOwner = targetUser.Owner
|
||||
}
|
||||
|
||||
hasPermission := false
|
||||
@@ -219,7 +223,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool) (bool, error
|
||||
hasPermission = true
|
||||
} else if requestUserId == userId {
|
||||
hasPermission = true
|
||||
} else if targetUser.Owner == requestUser.Owner {
|
||||
} else if userOwner == requestUser.Owner {
|
||||
if strict {
|
||||
hasPermission = requestUser.IsAdmin
|
||||
} else {
|
||||
@@ -236,7 +240,7 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
|
||||
allowed := true
|
||||
var err error
|
||||
for _, permission := range permissions {
|
||||
if !permission.IsEnabled {
|
||||
if !permission.IsEnabled || len(permission.Users) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@@ -25,6 +25,7 @@ import (
|
||||
func InitDb() {
|
||||
existed := initBuiltInOrganization()
|
||||
if !existed {
|
||||
initBuiltInPermission()
|
||||
initBuiltInProvider()
|
||||
initBuiltInUser()
|
||||
initBuiltInApplication()
|
||||
@@ -70,6 +71,8 @@ func initBuiltInOrganization() bool {
|
||||
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
||||
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
||||
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
|
||||
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
|
||||
@@ -167,7 +170,7 @@ func readTokenFromFile() (string, string) {
|
||||
}
|
||||
|
||||
func initBuiltInCert() {
|
||||
tokenJwtPublicKey, tokenJwtPrivateKey := readTokenFromFile()
|
||||
tokenJwtCertificate, tokenJwtPrivateKey := readTokenFromFile()
|
||||
cert := getCert("admin", "cert-built-in")
|
||||
if cert != nil {
|
||||
return
|
||||
@@ -183,7 +186,7 @@ func initBuiltInCert() {
|
||||
CryptoAlgorithm: "RS256",
|
||||
BitSize: 4096,
|
||||
ExpireInYears: 20,
|
||||
PublicKey: tokenJwtPublicKey,
|
||||
Certificate: tokenJwtCertificate,
|
||||
PrivateKey: tokenJwtPrivateKey,
|
||||
}
|
||||
AddCert(cert)
|
||||
@@ -230,3 +233,25 @@ func initBuiltInProvider() {
|
||||
func initWebAuthn() {
|
||||
gob.Register(webauthn.SessionData{})
|
||||
}
|
||||
|
||||
func initBuiltInPermission() {
|
||||
permission := GetPermission("built-in/permission-built-in")
|
||||
if permission != nil {
|
||||
return
|
||||
}
|
||||
|
||||
permission = &Permission{
|
||||
Owner: "built-in",
|
||||
Name: "permission-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Built-in Permission",
|
||||
Users: []string{"built-in/admin"},
|
||||
Roles: []string{},
|
||||
ResourceType: "Application",
|
||||
Resources: []string{"app-built-in"},
|
||||
Actions: []string{"Read", "Write", "Admin"},
|
||||
Effect: "Allow",
|
||||
IsEnabled: true,
|
||||
}
|
||||
AddPermission(permission)
|
||||
}
|
||||
|
@@ -89,6 +89,8 @@ func initDefinedOrganization(organization *Organization) {
|
||||
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
|
||||
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
||||
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
||||
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
|
||||
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
|
||||
|
@@ -97,7 +97,7 @@ func GetJsonWebKeySet() (jose.JSONWebKeySet, error) {
|
||||
//link here: https://self-issued.info/docs/draft-ietf-jose-json-web-key.html
|
||||
//or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key
|
||||
for _, cert := range certs {
|
||||
certPemBlock := []byte(cert.PublicKey)
|
||||
certPemBlock := []byte(cert.Certificate)
|
||||
certDerBlock, _ := pem.Decode(certPemBlock)
|
||||
x509Cert, _ := x509.ParseCertificate(certDerBlock.Bytes)
|
||||
|
||||
|
@@ -15,6 +15,8 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/cred"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
@@ -186,3 +188,31 @@ func DeleteOrganization(organization *Organization) bool {
|
||||
func GetOrganizationByUser(user *User) *Organization {
|
||||
return getOrganization("admin", user.Owner)
|
||||
}
|
||||
|
||||
func GetAccountItemByName(name string, organization *Organization) *AccountItem {
|
||||
if organization == nil {
|
||||
return nil
|
||||
}
|
||||
for _, accountItem := range organization.AccountItems {
|
||||
if accountItem.Name == name {
|
||||
return accountItem
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User) (bool, string) {
|
||||
switch accountItem.ModifyRule {
|
||||
case "Admin":
|
||||
if !(user.IsAdmin || user.IsGlobalAdmin) {
|
||||
return false, fmt.Sprintf("Only admin can modify the %s.", accountItem.Name)
|
||||
}
|
||||
case "Immutable":
|
||||
return false, fmt.Sprintf("The %s is immutable.", accountItem.Name)
|
||||
case "Self":
|
||||
break
|
||||
default:
|
||||
return false, fmt.Sprintf("Unknown modify rule %s.", accountItem.ModifyRule)
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
@@ -229,3 +229,13 @@ func removePolicies(permission *Permission) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func GetPermissionsByUser(userId string) []*Permission {
|
||||
permissions := []*Permission{}
|
||||
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return permissions
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ func TestProduct(t *testing.T) {
|
||||
product := GetProduct("admin/product_123")
|
||||
provider := getProvider(product.Owner, "provider_pay_alipay")
|
||||
cert := getCert(product.Owner, "cert-pay-alipay")
|
||||
pProvider := pp.GetPaymentProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, cert.PublicKey, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey)
|
||||
pProvider := pp.GetPaymentProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, cert.Certificate, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey)
|
||||
|
||||
paymentName := util.GenerateTimeId()
|
||||
returnUrl := ""
|
||||
|
@@ -214,7 +214,7 @@ func (p *Provider) getPaymentProvider() (pp.PaymentProvider, *Cert, error) {
|
||||
}
|
||||
}
|
||||
|
||||
pProvider := pp.GetPaymentProvider(p.Type, p.ClientId, p.ClientSecret, p.Host, cert.PublicKey, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey)
|
||||
pProvider := pp.GetPaymentProvider(p.Type, p.ClientId, p.ClientSecret, p.Host, cert.Certificate, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey)
|
||||
if pProvider == nil {
|
||||
return nil, cert, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
|
||||
}
|
||||
|
@@ -33,6 +33,15 @@ func (application *Application) GetProviderItem(providerName string) *ProviderIt
|
||||
return nil
|
||||
}
|
||||
|
||||
func (application *Application) GetProviderItemByType(providerType string) *ProviderItem {
|
||||
for _, item := range application.Providers {
|
||||
if item.Provider.Type == providerType {
|
||||
return item
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pi *ProviderItem) IsProviderVisible() bool {
|
||||
if pi.Provider == nil {
|
||||
return false
|
||||
|
@@ -121,3 +121,13 @@ func DeleteRole(role *Role) bool {
|
||||
func (role *Role) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", role.Owner, role.Name)
|
||||
}
|
||||
|
||||
func GetRolesByUser(userId string) []*Role {
|
||||
roles := []*Role{}
|
||||
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&roles)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ import (
|
||||
)
|
||||
|
||||
//returns a saml2 response
|
||||
func NewSamlResponse(user *User, host string, publicKey string, destination string, iss string, requestId string, redirectUri []string) (*etree.Element, error) {
|
||||
func NewSamlResponse(user *User, host string, certificate string, destination string, iss string, requestId string, redirectUri []string) (*etree.Element, error) {
|
||||
samlResponse := &etree.Element{
|
||||
Space: "samlp",
|
||||
Tag: "Response",
|
||||
@@ -177,8 +177,8 @@ type Attribute struct {
|
||||
func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, error) {
|
||||
//_, originBackend := getOriginFromHost(host)
|
||||
cert := getCertByApplication(application)
|
||||
block, _ := pem.Decode([]byte(cert.PublicKey))
|
||||
publicKey := base64.StdEncoding.EncodeToString(block.Bytes)
|
||||
block, _ := pem.Decode([]byte(cert.Certificate))
|
||||
certificate := base64.StdEncoding.EncodeToString(block.Bytes)
|
||||
|
||||
origin := beego.AppConfig.String("origin")
|
||||
originFrontend, originBackend := getOriginFromHost(host)
|
||||
@@ -199,7 +199,7 @@ func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, e
|
||||
KeyInfo: KeyInfo{
|
||||
X509Data: X509Data{
|
||||
X509Certificate: X509Certificate{
|
||||
Cert: publicKey,
|
||||
Cert: certificate,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -248,18 +248,18 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
return "", "", fmt.Errorf("err: invalid issuer url")
|
||||
}
|
||||
|
||||
// get public key string
|
||||
// get certificate string
|
||||
cert := getCertByApplication(application)
|
||||
block, _ := pem.Decode([]byte(cert.PublicKey))
|
||||
publicKey := base64.StdEncoding.EncodeToString(block.Bytes)
|
||||
block, _ := pem.Decode([]byte(cert.Certificate))
|
||||
certificate := base64.StdEncoding.EncodeToString(block.Bytes)
|
||||
|
||||
_, originBackend := getOriginFromHost(host)
|
||||
|
||||
// build signedResponse
|
||||
samlResponse, _ := NewSamlResponse(user, originBackend, publicKey, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer.Url, authnRequest.ID, application.RedirectUris)
|
||||
samlResponse, _ := NewSamlResponse(user, originBackend, certificate, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer.Url, authnRequest.ID, application.RedirectUris)
|
||||
randomKeyStore := &X509Key{
|
||||
PrivateKey: cert.PrivateKey,
|
||||
X509Certificate: publicKey,
|
||||
X509Certificate: certificate,
|
||||
}
|
||||
ctx := dsig.NewDefaultSigningContext(randomKeyStore)
|
||||
ctx.Hash = crypto.SHA1
|
||||
|
@@ -22,7 +22,7 @@ import (
|
||||
func (syncer *Syncer) syncUsers() {
|
||||
fmt.Printf("Running syncUsers()..\n")
|
||||
|
||||
users, userMap := syncer.getUserMap()
|
||||
users, userMap, userNameMap := syncer.getUserMap()
|
||||
oUsers, oUserMap, err := syncer.getOriginalUserMap()
|
||||
if err != nil {
|
||||
fmt.Printf(err.Error())
|
||||
@@ -44,9 +44,11 @@ func (syncer *Syncer) syncUsers() {
|
||||
for _, oUser := range oUsers {
|
||||
id := oUser.Id
|
||||
if _, ok := userMap[id]; !ok {
|
||||
newUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
|
||||
fmt.Printf("New user: %v\n", newUser)
|
||||
newUsers = append(newUsers, newUser)
|
||||
if _, ok := userNameMap[oUser.Name]; !ok {
|
||||
newUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
|
||||
fmt.Printf("New user: %v\n", newUser)
|
||||
newUsers = append(newUsers, newUser)
|
||||
}
|
||||
} else {
|
||||
user := userMap[id]
|
||||
oHash := syncer.calculateHash(oUser)
|
||||
|
@@ -19,12 +19,15 @@ func (syncer *Syncer) getUsers() []*User {
|
||||
return users
|
||||
}
|
||||
|
||||
func (syncer *Syncer) getUserMap() ([]*User, map[string]*User) {
|
||||
func (syncer *Syncer) getUserMap() ([]*User, map[string]*User, map[string]*User) {
|
||||
users := syncer.getUsers()
|
||||
|
||||
m := map[string]*User{}
|
||||
m1 := map[string]*User{}
|
||||
m2 := map[string]*User{}
|
||||
for _, user := range users {
|
||||
m[user.Id] = user
|
||||
m1[user.Id] = user
|
||||
m2[user.Name] = user
|
||||
}
|
||||
return users, m
|
||||
|
||||
return users, m1, m2
|
||||
}
|
||||
|
@@ -241,11 +241,11 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
|
||||
samlResponse := NewSamlResponse11(user, request.RequestID, host)
|
||||
|
||||
cert := getCertByApplication(application)
|
||||
block, _ := pem.Decode([]byte(cert.PublicKey))
|
||||
publicKey := base64.StdEncoding.EncodeToString(block.Bytes)
|
||||
block, _ := pem.Decode([]byte(cert.Certificate))
|
||||
certificate := base64.StdEncoding.EncodeToString(block.Bytes)
|
||||
randomKeyStore := &X509Key{
|
||||
PrivateKey: cert.PrivateKey,
|
||||
X509Certificate: publicKey,
|
||||
X509Certificate: certificate,
|
||||
}
|
||||
|
||||
ctx := dsig.NewDefaultSigningContext(randomKeyStore)
|
||||
|
@@ -129,13 +129,13 @@ func ParseJwtToken(token string, cert *Cert) (*Claims, error) {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
// RSA public key
|
||||
publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.PublicKey))
|
||||
// RSA certificate
|
||||
certificate, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return publicKey, nil
|
||||
return certificate, nil
|
||||
})
|
||||
|
||||
if t != nil {
|
||||
|
@@ -23,10 +23,10 @@ import (
|
||||
|
||||
func TestGenerateRsaKeys(t *testing.T) {
|
||||
fileId := "token_jwt_key"
|
||||
publicKey, privateKey := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization")
|
||||
certificate, privateKey := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization")
|
||||
|
||||
// Write certificate (aka public key) to file.
|
||||
util.WriteStringToPath(publicKey, fmt.Sprintf("%s.pem", fileId))
|
||||
// Write certificate (aka certificate) to file.
|
||||
util.WriteStringToPath(certificate, fmt.Sprintf("%s.pem", fileId))
|
||||
|
||||
// Write private key to file.
|
||||
util.WriteStringToPath(privateKey, fmt.Sprintf("%s.key", fileId))
|
||||
|
@@ -73,7 +73,7 @@ type User struct {
|
||||
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
|
||||
LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"`
|
||||
|
||||
Github string `xorm:"varchar(100)" json:"github"`
|
||||
GitHub string `xorm:"github varchar(100)" json:"github"`
|
||||
Google string `xorm:"varchar(100)" json:"google"`
|
||||
QQ string `xorm:"qq varchar(100)" json:"qq"`
|
||||
WeChat string `xorm:"wechat varchar(100)" json:"wechat"`
|
||||
@@ -104,6 +104,9 @@ type User struct {
|
||||
|
||||
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
|
||||
Properties map[string]string `json:"properties"`
|
||||
|
||||
Roles []*Role `json:"roles"`
|
||||
Permissions []*Permission `json:"permissions"`
|
||||
}
|
||||
|
||||
type Userinfo struct {
|
||||
@@ -270,6 +273,24 @@ func GetUserByEmail(owner string, email string) *User {
|
||||
}
|
||||
}
|
||||
|
||||
func GetUserByPhone(owner string, phone string) *User {
|
||||
if owner == "" || phone == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
user := User{Owner: owner, Phone: phone}
|
||||
existed, err := adapter.Engine.Get(&user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &user
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetUserByUserId(owner string, userId string) *User {
|
||||
if owner == "" || userId == "" {
|
||||
return nil
|
||||
|
@@ -37,11 +37,11 @@ func TestSyncAvatarsFromGitHub(t *testing.T) {
|
||||
|
||||
users := GetGlobalUsers()
|
||||
for _, user := range users {
|
||||
if user.Github == "" {
|
||||
if user.GitHub == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
user.Avatar = fmt.Sprintf("https://avatars.githubusercontent.com/%s", user.Github)
|
||||
user.Avatar = fmt.Sprintf("https://avatars.githubusercontent.com/%s", user.GitHub)
|
||||
updateUserColumn("avatar", user)
|
||||
}
|
||||
}
|
||||
|
@@ -106,6 +106,10 @@ func setUserProperty(user *User, field string, value string) {
|
||||
if value == "" {
|
||||
delete(user.Properties, field)
|
||||
} else {
|
||||
if user.Properties == nil {
|
||||
user.Properties = make(map[string]string)
|
||||
}
|
||||
|
||||
user.Properties[field] = value
|
||||
}
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ type AlipayPaymentProvider struct {
|
||||
Client *alipay.Client
|
||||
}
|
||||
|
||||
func NewAlipayPaymentProvider(appId string, appPublicKey string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) *AlipayPaymentProvider {
|
||||
func NewAlipayPaymentProvider(appId string, appCertificate string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) *AlipayPaymentProvider {
|
||||
pp := &AlipayPaymentProvider{}
|
||||
|
||||
client, err := alipay.NewClient(appId, appPrivateKey, true)
|
||||
@@ -36,7 +36,7 @@ func NewAlipayPaymentProvider(appId string, appPublicKey string, appPrivateKey s
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = client.SetCertSnByContent([]byte(appPublicKey), []byte(authorityRootPublicKey), []byte(authorityPublicKey))
|
||||
err = client.SetCertSnByContent([]byte(appCertificate), []byte(authorityRootPublicKey), []byte(authorityPublicKey))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@@ -22,9 +22,9 @@ type PaymentProvider interface {
|
||||
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
||||
}
|
||||
|
||||
func GetPaymentProvider(typ string, appId string, clientSecret string, host string, appPublicKey string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) PaymentProvider {
|
||||
func GetPaymentProvider(typ string, appId string, clientSecret string, host string, appCertificate string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) PaymentProvider {
|
||||
if typ == "Alipay" {
|
||||
return NewAlipayPaymentProvider(appId, appPublicKey, appPrivateKey, authorityPublicKey, authorityRootPublicKey)
|
||||
return NewAlipayPaymentProvider(appId, appCertificate, appPrivateKey, authorityPublicKey, authorityRootPublicKey)
|
||||
} else if typ == "GC" {
|
||||
return NewGcPaymentProvider(appId, clientSecret, host)
|
||||
}
|
||||
|
@@ -3571,7 +3571,7 @@
|
||||
"privateKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicKey": {
|
||||
"certificate": {
|
||||
"type": "string"
|
||||
},
|
||||
"scope": {
|
||||
|
@@ -2346,7 +2346,7 @@ definitions:
|
||||
type: string
|
||||
privateKey:
|
||||
type: string
|
||||
publicKey:
|
||||
certificate:
|
||||
type: string
|
||||
scope:
|
||||
type: string
|
||||
|
@@ -86,6 +86,8 @@ class AccountTable extends React.Component {
|
||||
{name: "Bio", displayName: i18next.t("user:Bio")},
|
||||
{name: "Tag", displayName: i18next.t("user:Tag")},
|
||||
{name: "Signup application", displayName: i18next.t("general:Signup application")},
|
||||
{name: "Roles", displayName: i18next.t("general:Roles")},
|
||||
{name: "Permissions", displayName: i18next.t("general:Permissions")},
|
||||
{name: "3rd-party logins", displayName: i18next.t("user:3rd-party logins")},
|
||||
{name: "Properties", displayName: i18next.t("user:Properties")},
|
||||
{name: "Is admin", displayName: i18next.t("user:Is admin")},
|
||||
|
@@ -235,6 +235,8 @@ class App extends Component {
|
||||
AuthBackend.logout()
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
const owner = this.state.account.owner;
|
||||
|
||||
this.setState({
|
||||
account: null
|
||||
});
|
||||
@@ -243,7 +245,9 @@ class App extends Component {
|
||||
let redirectUri = res.data2;
|
||||
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
|
||||
Setting.goToLink(redirectUri);
|
||||
}else{
|
||||
} else if (owner !== "built-in") {
|
||||
Setting.goToLink(`${window.location.origin}/login/${owner}`);
|
||||
} else {
|
||||
Setting.goToLinkSoft(this, "/");
|
||||
}
|
||||
} else {
|
||||
@@ -669,6 +673,7 @@ class App extends Component {
|
||||
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />)} />
|
||||
<Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/auto-signup/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signup"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
|
||||
|
@@ -164,25 +164,25 @@ class CertEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("cert:Public key"), i18next.t("cert:Public key - Tooltip"))} :
|
||||
{Setting.getLabel(i18next.t("cert:Certificate"), i18next.t("cert:Certificate - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={9} >
|
||||
<Button style={{marginRight: "10px", marginBottom: "10px"}} onClick={() => {
|
||||
copy(this.state.cert.publicKey);
|
||||
Setting.showMessage("success", i18next.t("cert:Public key copied to clipboard successfully"));
|
||||
copy(this.state.cert.certificate);
|
||||
Setting.showMessage("success", i18next.t("cert:Certificate copied to clipboard successfully"));
|
||||
}}
|
||||
>
|
||||
{i18next.t("cert:Copy public key")}
|
||||
{i18next.t("cert:Copy certificate")}
|
||||
</Button>
|
||||
<Button type="primary" onClick={() => {
|
||||
const blob = new Blob([this.state.cert.publicKey], {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");
|
||||
}}
|
||||
>
|
||||
{i18next.t("cert:Download public key")}
|
||||
{i18next.t("cert:Download certificate")}
|
||||
</Button>
|
||||
<TextArea autoSize={{minRows: 30, maxRows: 30}} value={this.state.cert.publicKey} onChange={e => {
|
||||
this.updateCertField("publicKey", e.target.value);
|
||||
<TextArea autoSize={{minRows: 30, maxRows: 30}} value={this.state.cert.certificate} onChange={e => {
|
||||
this.updateCertField("certificate", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
<Col span={1} />
|
||||
|
@@ -34,7 +34,7 @@ class CertListPage extends BaseListPage {
|
||||
cryptoAlgorithm: "RS256",
|
||||
bitSize: 4096,
|
||||
expireInYears: 20,
|
||||
publicKey: "",
|
||||
certificate: "",
|
||||
privateKey: "",
|
||||
};
|
||||
}
|
||||
|
@@ -57,6 +57,8 @@ class OrganizationListPage extends BaseListPage {
|
||||
{name: "Bio", visible: true, viewRule: "Public", modifyRule: "Self"},
|
||||
{name: "Tag", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
||||
{name: "Signup application", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
||||
{name: "Roles", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
||||
{name: "Permissions", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
||||
{name: "3rd-party logins", visible: true, viewRule: "Self", modifyRule: "Self"},
|
||||
{name: "Properties", visible: false, viewRule: "Admin", modifyRule: "Admin"},
|
||||
{name: "Is admin", visible: true, viewRule: "Admin", modifyRule: "Admin"},
|
||||
|
@@ -622,7 +622,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:IdP"), i18next.t("provider:IdP public key"))} :
|
||||
{Setting.getLabel(i18next.t("provider:IdP"), i18next.t("provider:IdP certificate"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.idP} onChange={e => {
|
||||
|
@@ -291,7 +291,7 @@ class UserEditPage extends React.Component {
|
||||
}} />
|
||||
</Col>
|
||||
<Col span={11} >
|
||||
{this.state.user.id === this.props.account?.id ? (<ResetModal application={this.state.application} buttonText={i18next.t("user:Reset Email...")} destType={"email"} />) : null}
|
||||
{this.state.user.id === this.props.account?.id ? (<ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Email...")} destType={"email"} />) : null}
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
@@ -309,7 +309,7 @@ class UserEditPage extends React.Component {
|
||||
}} />
|
||||
</Col>
|
||||
<Col span={11} >
|
||||
{this.state.user.id === this.props.account?.id ? (<ResetModal application={this.state.application} buttonText={i18next.t("user:Reset Phone...")} destType={"phone"} />) : null}
|
||||
{this.state.user.id === this.props.account?.id ? (<ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Phone...")} destType={"phone"} />) : null}
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
@@ -427,6 +427,32 @@ class UserEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
} else if (accountItem.name === "Roles") {
|
||||
return (
|
||||
<Row style={{marginTop: "20px", alignItems: "center"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Roles"), i18next.t("general:Roles - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
{
|
||||
Setting.getTags(this.state.user.roles.map(role => role.name))
|
||||
}
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
} else if (accountItem.name === "Permissions") {
|
||||
return (
|
||||
<Row style={{marginTop: "20px", alignItems: "center"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Permissions"), i18next.t("general:Permissions - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
{
|
||||
Setting.getTags(this.state.user.permissions.map(permission => permission.name))
|
||||
}
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
} else if (accountItem.name === "3rd-party logins") {
|
||||
return (
|
||||
!this.isSelfOrAdmin() ? null : (
|
||||
@@ -440,7 +466,7 @@ class UserEditPage extends React.Component {
|
||||
(this.state.application === null || this.state.user === null) ? null : (
|
||||
this.state.application?.providers.filter(providerItem => Setting.isProviderVisible(providerItem)).map((providerItem, index) =>
|
||||
(providerItem.provider.category === "OAuth") ? (
|
||||
<OAuthWidget key={providerItem.name} labelSpan={(Setting.isMobile()) ? 10 : 3} user={this.state.user} application={this.state.application} providerItem={providerItem} onUnlinked={() => {return this.unlinked();}} />
|
||||
<OAuthWidget key={providerItem.name} labelSpan={(Setting.isMobile()) ? 10 : 3} user={this.state.user} application={this.state.application} providerItem={providerItem} account={this.props.account} onUnlinked={() => {return this.unlinked();}} />
|
||||
) : (
|
||||
<SamlWidget key={providerItem.name} labelSpan={(Setting.isMobile()) ? 10 : 3} user={this.state.user} application={this.state.application} providerItem={providerItem} onUnlinked={() => {return this.unlinked();}} />
|
||||
)
|
||||
|
@@ -191,6 +191,10 @@ class LoginPage extends React.Component {
|
||||
values["type"] = "saml";
|
||||
}
|
||||
|
||||
if (this.state.owner != null) {
|
||||
values["organization"] = this.state.owner;
|
||||
}
|
||||
|
||||
AuthBackend.login(values, oAuthParams)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
@@ -203,6 +207,7 @@ class LoginPage extends React.Component {
|
||||
} else if (responseType === "code") {
|
||||
const code = res.data;
|
||||
const concatChar = oAuthParams?.redirectUri?.includes("?") ? "&" : "?";
|
||||
const noRedirect = oAuthParams.noRedirect;
|
||||
|
||||
if (Setting.hasPromptPage(application)) {
|
||||
AuthBackend.getAccount("")
|
||||
@@ -224,7 +229,19 @@ class LoginPage extends React.Component {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
|
||||
if (noRedirect === "true") {
|
||||
window.close();
|
||||
const newWindow = window.open(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
|
||||
if (newWindow) {
|
||||
setInterval(() => {
|
||||
if (!newWindow.closed) {
|
||||
newWindow.close();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Util.showMessage("success", `Authorization code: ${res.data}`);
|
||||
@@ -488,7 +505,7 @@ class LoginPage extends React.Component {
|
||||
)
|
||||
}
|
||||
{
|
||||
!application.enableSignUp ? null : this.renderFooter(application)
|
||||
this.renderFooter(application)
|
||||
}
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
@@ -518,16 +535,12 @@ class LoginPage extends React.Component {
|
||||
return this.renderProviderLogo(providerItem.provider, application, 40, 10, "big");
|
||||
})
|
||||
}
|
||||
{
|
||||
!application.enableSignUp ? null : (
|
||||
<div>
|
||||
<br />
|
||||
{
|
||||
this.renderFooter(application)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div>
|
||||
<br />
|
||||
{
|
||||
this.renderFooter(application)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -562,13 +575,19 @@ class LoginPage extends React.Component {
|
||||
}
|
||||
</span>
|
||||
<span style={{float: "right"}}>
|
||||
{i18next.t("login:No account?")}
|
||||
<a onClick={() => {
|
||||
sessionStorage.setItem("signinUrl", window.location.href);
|
||||
Setting.goToSignup(this, application);
|
||||
}}>
|
||||
{i18next.t("login:sign up now")}
|
||||
</a>
|
||||
{
|
||||
!application.enableSignUp ? null : (
|
||||
<>
|
||||
{i18next.t("login:No account?")}
|
||||
<a onClick={() => {
|
||||
sessionStorage.setItem("signinUrl", window.location.href);
|
||||
Setting.goToSignup(this, application);
|
||||
}}>
|
||||
{i18next.t("login:sign up now")}
|
||||
</a>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -638,13 +657,13 @@ class LoginPage extends React.Component {
|
||||
throw credentialRequestOptions.status.msg;
|
||||
}
|
||||
|
||||
credentialRequestOptions.publicKey.challenge = UserWebauthnBackend.webAuthnBufferDecode(credentialRequestOptions.publicKey.challenge);
|
||||
credentialRequestOptions.publicKey.allowCredentials.forEach(function(listItem) {
|
||||
credentialRequestOptions.certificate.challenge = UserWebauthnBackend.webAuthnBufferDecode(credentialRequestOptions.certificate.challenge);
|
||||
credentialRequestOptions.certificate.allowCredentials.forEach(function(listItem) {
|
||||
listItem.id = UserWebauthnBackend.webAuthnBufferDecode(listItem.id);
|
||||
});
|
||||
|
||||
return navigator.credentials.get({
|
||||
publicKey: credentialRequestOptions.publicKey
|
||||
certificate: credentialRequestOptions.certificate
|
||||
});
|
||||
})
|
||||
.then((assertion) => {
|
||||
@@ -692,7 +711,7 @@ class LoginPage extends React.Component {
|
||||
>
|
||||
<CountDownInput
|
||||
disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone}
|
||||
onButtonClickArgs={[this.state.username, "", Setting.getApplicationOrgName(application), true]}
|
||||
onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
|
@@ -103,6 +103,7 @@ export function getOAuthGetParameters(params) {
|
||||
const codeChallenge = getRefinedValue(queries.get("code_challenge"));
|
||||
const samlRequest = getRefinedValue(queries.get("SAMLRequest"));
|
||||
const relayState = getRefinedValue(queries.get("RelayState"));
|
||||
const noRedirect = getRefinedValue(queries.get("noRedirect"));
|
||||
|
||||
if ((clientId === undefined || clientId === null || clientId === "") && (samlRequest === "" || samlRequest === undefined)) {
|
||||
// login
|
||||
@@ -120,6 +121,7 @@ export function getOAuthGetParameters(params) {
|
||||
codeChallenge: codeChallenge,
|
||||
samlRequest: samlRequest,
|
||||
relayState: relayState,
|
||||
noRedirect: noRedirect,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -21,15 +21,15 @@ export function registerWebauthnCredential() {
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then((credentialCreationOptions) => {
|
||||
credentialCreationOptions.publicKey.challenge = webAuthnBufferDecode(credentialCreationOptions.publicKey.challenge);
|
||||
credentialCreationOptions.publicKey.user.id = webAuthnBufferDecode(credentialCreationOptions.publicKey.user.id);
|
||||
if (credentialCreationOptions.publicKey.excludeCredentials) {
|
||||
for (var i = 0; i < credentialCreationOptions.publicKey.excludeCredentials.length; i++) {
|
||||
credentialCreationOptions.publicKey.excludeCredentials[i].id = webAuthnBufferDecode(credentialCreationOptions.publicKey.excludeCredentials[i].id);
|
||||
credentialCreationOptions.certificate.challenge = webAuthnBufferDecode(credentialCreationOptions.certificate.challenge);
|
||||
credentialCreationOptions.certificate.user.id = webAuthnBufferDecode(credentialCreationOptions.certificate.user.id);
|
||||
if (credentialCreationOptions.certificate.excludeCredentials) {
|
||||
for (var i = 0; i < credentialCreationOptions.certificate.excludeCredentials.length; i++) {
|
||||
credentialCreationOptions.certificate.excludeCredentials[i].id = webAuthnBufferDecode(credentialCreationOptions.certificate.excludeCredentials[i].id);
|
||||
}
|
||||
}
|
||||
return navigator.credentials.create({
|
||||
publicKey: credentialCreationOptions.publicKey
|
||||
certificate: credentialCreationOptions.certificate
|
||||
});
|
||||
})
|
||||
.then((credential) => {
|
||||
|
@@ -91,6 +91,8 @@ class OAuthWidget extends React.Component {
|
||||
unlinkUser(providerType) {
|
||||
const body = {
|
||||
providerType: providerType,
|
||||
// should add the unlink user's info, cause the user may not be logged in, but a admin want to unlink the user.
|
||||
user: this.props.user,
|
||||
};
|
||||
AuthBackend.unlink(body)
|
||||
.then((res) => {
|
||||
@@ -113,6 +115,8 @@ class OAuthWidget extends React.Component {
|
||||
const displayName = this.getUserProperty(user, provider.type, "displayName");
|
||||
const email = this.getUserProperty(user, provider.type, "email");
|
||||
let avatarUrl = this.getUserProperty(user, provider.type, "avatarUrl");
|
||||
// the account user
|
||||
const account = this.props.account;
|
||||
|
||||
if (avatarUrl === "" || avatarUrl === undefined) {
|
||||
avatarUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAQAAACROWYpAAAAHElEQVR42mNkoAAwjmoe1TyqeVTzqOZRzcNZMwB18wAfEFQkPQAAAABJRU5ErkJggg==";
|
||||
@@ -161,10 +165,10 @@ class OAuthWidget extends React.Component {
|
||||
{
|
||||
linkedValue === "" ? (
|
||||
<a key={provider.displayName} href={Provider.getAuthUrl(application, provider, "link")}>
|
||||
<Button style={{marginLeft: "20px", width: "80px"}} type="primary">{i18next.t("user:Link")}</Button>
|
||||
<Button style={{marginLeft: "20px", width: "80px"}} type="primary" disabled={user.id !== account.id}>{i18next.t("user:Link")}</Button>
|
||||
</a>
|
||||
) : (
|
||||
<Button disabled={!providerItem.canUnlink} style={{marginLeft: "20px", width: "80px"}} onClick={() => this.unlinkUser(provider.type)}>{i18next.t("user:Unlink")}</Button>
|
||||
<Button disabled={!providerItem.canUnlink && !account.isGlobalAdmin} style={{marginLeft: "20px", width: "80px"}} onClick={() => this.unlinkUser(provider.type)}>{i18next.t("user:Unlink")}</Button>
|
||||
)
|
||||
}
|
||||
</Col>
|
||||
|
@@ -51,11 +51,11 @@
|
||||
"Bit size": "Bitgröße",
|
||||
"Bit size - Tooltip": "Bit Größe - Tooltip",
|
||||
"Copy private key": "Privaten Schlüssel kopieren",
|
||||
"Copy public key": "Öffentlichen Schlüssel kopieren",
|
||||
"Copy certificate": "Kopieren des Zertifikats",
|
||||
"Crypto algorithm": "Crypto-Algorithmus",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Privaten Schlüssel herunterladen",
|
||||
"Download public key": "Öffentlichen Schlüssel herunterladen",
|
||||
"Download certificate": "Zertifikat herunterladen",
|
||||
"Edit Cert": "Zitat bearbeiten",
|
||||
"Expire in years": "Gültig in Jahren",
|
||||
"Expire in years - Tooltip": "Verfällt in Jahren - Tooltip",
|
||||
@@ -63,9 +63,9 @@
|
||||
"Private key": "Privater Schlüssel",
|
||||
"Private key - Tooltip": "Privater Schlüssel - Tooltip",
|
||||
"Private key copied to clipboard successfully": "Privater Schlüssel erfolgreich in die Zwischenablage kopiert",
|
||||
"Public key": "Öffentlicher Schlüssel",
|
||||
"Public key - Tooltip": "Öffentlicher Schlüssel - Tooltip",
|
||||
"Public key copied to clipboard successfully": "Öffentlicher Schlüssel erfolgreich in die Zwischenablage kopiert",
|
||||
"Certificate": "Zertifikat",
|
||||
"Certificate - Tooltip": "Zertifikat - Tooltip",
|
||||
"Certificate copied to clipboard successfully": "Das Zertifikat wurde erfolgreich in die Zwischenablage kopiert",
|
||||
"Scope": "Bereich",
|
||||
"Scope - Tooltip": "Bereich - Tooltip",
|
||||
"Type": "Typ",
|
||||
@@ -426,7 +426,7 @@
|
||||
"Host": "Host",
|
||||
"Host - Tooltip": "Unique string-style identifier",
|
||||
"IdP": "IdP",
|
||||
"IdP public key": "IdP-öffentlicher Schlüssel",
|
||||
"IdP certificate": "IdP-öffentlicher Schlüssel",
|
||||
"Issuer URL": "Ausgabe-URL",
|
||||
"Issuer URL - Tooltip": "Ausgabe-URL - Tooltip",
|
||||
"Link copied to clipboard successfully": "Link erfolgreich in die Zwischenablage kopiert",
|
||||
|
@@ -51,11 +51,11 @@
|
||||
"Bit size": "Bit size",
|
||||
"Bit size - Tooltip": "Bit size - Tooltip",
|
||||
"Copy private key": "Copy private key",
|
||||
"Copy public key": "Copy public key",
|
||||
"Copy certificate": "Copy certificate",
|
||||
"Crypto algorithm": "Crypto algorithm",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Download private key",
|
||||
"Download public key": "Download public key",
|
||||
"Download certificate": "Download certificate",
|
||||
"Edit Cert": "Edit Cert",
|
||||
"Expire in years": "Expire in years",
|
||||
"Expire in years - Tooltip": "Expire in years - Tooltip",
|
||||
@@ -63,9 +63,9 @@
|
||||
"Private key": "Private key",
|
||||
"Private key - Tooltip": "Private key - Tooltip",
|
||||
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
|
||||
"Public key": "Public key",
|
||||
"Public key - Tooltip": "Public key - Tooltip",
|
||||
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully",
|
||||
"Certificate": "Certificate",
|
||||
"Certificate - Tooltip": "Certificate - Tooltip",
|
||||
"Certificate copied to clipboard successfully": "Certificate copied to clipboard successfully",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Type": "Type",
|
||||
@@ -426,7 +426,7 @@
|
||||
"Host": "Host",
|
||||
"Host - Tooltip": "Host - Tooltip",
|
||||
"IdP": "IdP",
|
||||
"IdP public key": "IdP public key",
|
||||
"IdP certificate": "IdP certificate",
|
||||
"Issuer URL": "Issuer URL",
|
||||
"Issuer URL - Tooltip": "Issuer URL - Tooltip",
|
||||
"Link copied to clipboard successfully": "Link copied to clipboard successfully",
|
||||
|
@@ -51,21 +51,21 @@
|
||||
"Bit size": "Taille du bit",
|
||||
"Bit size - Tooltip": "Taille du bit - Infobulle",
|
||||
"Copy private key": "Copier la clé privée",
|
||||
"Copy public key": "Copier la clé publique",
|
||||
"Copy certificate": "Copier le certificate",
|
||||
"Crypto algorithm": "Algorithme de cryptomonnaie",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Télécharger la clé privée",
|
||||
"Download public key": "Télécharger la clé publique",
|
||||
"Edit Cert": "Modifier le certificat",
|
||||
"Download certificate": "Télécharger le certificate",
|
||||
"Edit Cert": "Modifier le certificate",
|
||||
"Expire in years": "Expire dans les années",
|
||||
"Expire in years - Tooltip": "Expire dans les années - infobulle",
|
||||
"New Cert": "New Cert",
|
||||
"Private key": "Clé privée",
|
||||
"Private key - Tooltip": "Clé privée - Infobulle",
|
||||
"Private key copied to clipboard successfully": "Clé privée copiée dans le presse-papiers avec succès",
|
||||
"Public key": "Clé publique",
|
||||
"Public key - Tooltip": "Clé publique - Infobulle",
|
||||
"Public key copied to clipboard successfully": "Clé publique copiée dans le presse-papiers avec succès",
|
||||
"Certificate": "certificate",
|
||||
"Certificate - Tooltip": "certificate - Infobulle",
|
||||
"Certificate copied to clipboard successfully": "Le certificate a été copié avec succès dans le presse-papiers",
|
||||
"Scope": "Périmètre d'application",
|
||||
"Scope - Tooltip": "Scope - Infobulle",
|
||||
"Type": "Type de texte",
|
||||
@@ -426,7 +426,7 @@
|
||||
"Host": "Hôte",
|
||||
"Host - Tooltip": "Unique string-style identifier",
|
||||
"IdP": "IDP",
|
||||
"IdP public key": "Clé publique IdP",
|
||||
"IdP certificate": "Clé publique IdP",
|
||||
"Issuer URL": "URL de l'émetteur",
|
||||
"Issuer URL - Tooltip": "URL de l'émetteur - infobulle",
|
||||
"Link copied to clipboard successfully": "Lien copié dans le presse-papiers avec succès",
|
||||
|
@@ -51,11 +51,11 @@
|
||||
"Bit size": "ビットサイズ",
|
||||
"Bit size - Tooltip": "ビットサイズ - ツールチップ",
|
||||
"Copy private key": "秘密鍵をコピー",
|
||||
"Copy public key": "公開鍵をコピー",
|
||||
"Copy certificate": "証明書をコピーします",
|
||||
"Crypto algorithm": "暗号化アルゴリズム",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "秘密鍵をダウンロード",
|
||||
"Download public key": "公開鍵をダウンロード",
|
||||
"Download certificate": "証明書をダウンロードします",
|
||||
"Edit Cert": "Certを編集",
|
||||
"Expire in years": "有効期限",
|
||||
"Expire in years - Tooltip": "年間有効期限 - ツールチップ",
|
||||
@@ -63,9 +63,9 @@
|
||||
"Private key": "Private key",
|
||||
"Private key - Tooltip": "Private key - Tooltip",
|
||||
"Private key copied to clipboard successfully": "秘密鍵を正常にクリップボードにコピーしました",
|
||||
"Public key": "公開キー",
|
||||
"Public key - Tooltip": "Public key - Tooltip",
|
||||
"Public key copied to clipboard successfully": "公開鍵を正常にクリップボードにコピーしました",
|
||||
"Certificate": "Certificate",
|
||||
"Certificate - Tooltip": "Certificate - Tooltip",
|
||||
"Certificate copied to clipboard successfully": "証明書はクリップボードに正常にコピーされました",
|
||||
"Scope": "スコープ",
|
||||
"Scope - Tooltip": "スコープ → ツールチップ",
|
||||
"Type": "タイプ",
|
||||
@@ -426,7 +426,7 @@
|
||||
"Host": "ホスト",
|
||||
"Host - Tooltip": "Unique string-style identifier",
|
||||
"IdP": "IdP",
|
||||
"IdP public key": "IdP public key",
|
||||
"IdP certificate": "IdP certificate",
|
||||
"Issuer URL": "Issuer URL",
|
||||
"Issuer URL - Tooltip": "Issuer URL - ツールチップ",
|
||||
"Link copied to clipboard successfully": "リンクをクリップボードにコピーしました",
|
||||
|
@@ -51,11 +51,11 @@
|
||||
"Bit size": "Bit size",
|
||||
"Bit size - Tooltip": "Bit size - Tooltip",
|
||||
"Copy private key": "Copy private key",
|
||||
"Copy public key": "Copy public key",
|
||||
"Copy certificate": "Copy certificate",
|
||||
"Crypto algorithm": "Crypto algorithm",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Download private key",
|
||||
"Download public key": "Download public key",
|
||||
"Download certificate": "Download certificate",
|
||||
"Edit Cert": "Edit Cert",
|
||||
"Expire in years": "Expire in years",
|
||||
"Expire in years - Tooltip": "Expire in years - Tooltip",
|
||||
@@ -63,9 +63,9 @@
|
||||
"Private key": "Private key",
|
||||
"Private key - Tooltip": "Private key - Tooltip",
|
||||
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
|
||||
"Public key": "Public key",
|
||||
"Public key - Tooltip": "Public key - Tooltip",
|
||||
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully",
|
||||
"Certificate": "Certificate",
|
||||
"Certificate - Tooltip": "Certificate - Tooltip",
|
||||
"Certificate copied to clipboard successfully": "Certificate copied to clipboard successfully",
|
||||
"Scope": "Scope",
|
||||
"Scope - Tooltip": "Scope - Tooltip",
|
||||
"Type": "Type",
|
||||
@@ -426,7 +426,7 @@
|
||||
"Host": "Host",
|
||||
"Host - Tooltip": "Unique string-style identifier",
|
||||
"IdP": "IdP",
|
||||
"IdP public key": "IdP public key",
|
||||
"IdP certificate": "IdP certificate",
|
||||
"Issuer URL": "Issuer URL",
|
||||
"Issuer URL - Tooltip": "Issuer URL - Tooltip",
|
||||
"Link copied to clipboard successfully": "Link copied to clipboard successfully",
|
||||
|
@@ -51,11 +51,11 @@
|
||||
"Bit size": "Размер бита",
|
||||
"Bit size - Tooltip": "Размер бита - Подсказка",
|
||||
"Copy private key": "Копировать закрытый ключ",
|
||||
"Copy public key": "Копировать открытый ключ",
|
||||
"Copy certificate": "Копирование сертификата",
|
||||
"Crypto algorithm": "Алгоритм крипто",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "Скачать закрытый ключ",
|
||||
"Download public key": "Скачать открытый ключ",
|
||||
"Download certificate": "Скачать сертификат",
|
||||
"Edit Cert": "Изменить сертификат",
|
||||
"Expire in years": "Истекает через годы",
|
||||
"Expire in years - Tooltip": "Истекает через годы - Подсказка",
|
||||
@@ -63,9 +63,9 @@
|
||||
"Private key": "Приватный ключ",
|
||||
"Private key - Tooltip": "Приватный ключ - Подсказка",
|
||||
"Private key copied to clipboard successfully": "Приватный ключ скопирован в буфер обмена",
|
||||
"Public key": "Публичный ключ",
|
||||
"Public key - Tooltip": "Открытый ключ - Подсказка",
|
||||
"Public key copied to clipboard successfully": "Открытый ключ успешно скопирован в буфер обмена",
|
||||
"Certificate": "сертификат",
|
||||
"Certificate - Tooltip": "сертификат - Подсказка",
|
||||
"Certificate copied to clipboard successfully": "Сертификат успешно скопирован в буфер обмена",
|
||||
"Scope": "Сфера охвата",
|
||||
"Scope - Tooltip": "Область применения - Подсказка",
|
||||
"Type": "Тип",
|
||||
@@ -426,7 +426,7 @@
|
||||
"Host": "Хост",
|
||||
"Host - Tooltip": "Unique string-style identifier",
|
||||
"IdP": "ИдП",
|
||||
"IdP public key": "Публичный ключ IdP",
|
||||
"IdP certificate": "Публичный ключ IdP",
|
||||
"Issuer URL": "URL эмитента",
|
||||
"Issuer URL - Tooltip": "URL эмитента - Tooltip",
|
||||
"Link copied to clipboard successfully": "Ссылка скопирована в буфер обмена",
|
||||
|
@@ -51,11 +51,11 @@
|
||||
"Bit size": "位大小",
|
||||
"Bit size - Tooltip": "位大小 - 工具提示",
|
||||
"Copy private key": "复制私钥",
|
||||
"Copy public key": "复制公钥",
|
||||
"Copy certificate": "复制证书",
|
||||
"Crypto algorithm": "加密算法",
|
||||
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
|
||||
"Download private key": "下载私钥",
|
||||
"Download public key": "下载公钥",
|
||||
"Download certificate": "下载证书",
|
||||
"Edit Cert": "编辑证书",
|
||||
"Expire in years": "有效期(年)",
|
||||
"Expire in years - Tooltip": "到期年份-工具提示",
|
||||
@@ -63,9 +63,9 @@
|
||||
"Private key": "私钥",
|
||||
"Private key - Tooltip": "私钥 - 工具提示",
|
||||
"Private key copied to clipboard successfully": "私钥已成功复制到剪贴板",
|
||||
"Public key": "公钥",
|
||||
"Public key - Tooltip": "公钥 - 工具提示",
|
||||
"Public key copied to clipboard successfully": "公钥已成功复制到剪贴板",
|
||||
"Certificate": "证书",
|
||||
"Certificate - Tooltip": "证书 - 工具提示",
|
||||
"Certificate copied to clipboard successfully": "证书已成功复制到剪贴板",
|
||||
"Scope": "用途",
|
||||
"Scope - Tooltip": "范围 - 工具提示",
|
||||
"Type": "类型",
|
||||
@@ -426,7 +426,7 @@
|
||||
"Host": "主机",
|
||||
"Host - Tooltip": "主机",
|
||||
"IdP": "IdP",
|
||||
"IdP public key": "IdP 公钥",
|
||||
"IdP certificate": "IdP 公钥",
|
||||
"Issuer URL": "发行者网址",
|
||||
"Issuer URL - Tooltip": "发行者URL - 工具提示",
|
||||
"Link copied to clipboard successfully": "链接已成功复制到剪贴板",
|
||||
|
Reference in New Issue
Block a user