mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-08 17:10:27 +08:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
6035b98653 | |||
e158b58ffa | |||
a399184cfc | |||
2f9f946c87 | |||
d8b60f838e | |||
7599e2715a | |||
35676455bc | |||
8128671c8c | |||
ee54dec3b3 | |||
d278bc9651 | |||
b23bd0b189 | |||
409be85264 | |||
0395b7e1a9 | |||
4536fd0636 | |||
af9ae7dbb7 | |||
e266696b32 | |||
e108d26ec7 | |||
349ce7f1d4 | |||
8da50b7893 | |||
2394c8e2b4 | |||
c62983d734 | |||
5948782cdd | |||
674d1619dd | |||
11b8b65ca0 | |||
411d76798d |
@ -128,6 +128,12 @@ p, *, *, GET, /api/get-release, *, *
|
||||
}
|
||||
|
||||
func IsAllowed(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
||||
if conf.IsDemoMode() {
|
||||
if !isAllowedInDemoMode(subOwner, subName, method, urlPath, objOwner, objName) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
res, err := Enforcer.Enforce(subOwner, subName, method, urlPath, objOwner, objName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -135,3 +141,22 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
||||
if method == "POST" {
|
||||
if urlPath == "/api/login" || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/send-verification-code" {
|
||||
return true
|
||||
} else if urlPath == "/api/update-user" {
|
||||
// Allow ordinary users to update their own information
|
||||
if subOwner == objOwner && subName == objName && !(subOwner == "built-in" && subName == "admin") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// If method equals GET
|
||||
return true
|
||||
}
|
||||
|
4
build.sh
4
build.sh
@ -4,8 +4,8 @@ curl www.google.com -o /dev/null --connect-timeout 5 2 > /dev/null
|
||||
if [ $? == 0 ]
|
||||
then
|
||||
echo "Successfully connected to Google, no need to use Go proxy"
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o server .
|
||||
else
|
||||
echo "Google is blocked, Go proxy is enabled: GOPROXY=https://goproxy.cn,direct"
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOPROXY=https://goproxy.cn,direct go build -ldflags="-w -s" -o server .
|
||||
export GOPROXY="https://goproxy.cn,direct"
|
||||
fi
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o server .
|
||||
|
@ -16,4 +16,5 @@ verificationCodeTimeout = 10
|
||||
initScore = 2000
|
||||
logPostOnly = true
|
||||
origin =
|
||||
staticBaseUrl = "https://cdn.casbin.org"
|
||||
staticBaseUrl = "https://cdn.casbin.org"
|
||||
isDemoMode = false
|
||||
|
14
conf/conf.go
14
conf/conf.go
@ -28,7 +28,15 @@ func GetConfigString(key string) string {
|
||||
if value, ok := os.LookupEnv(key); ok {
|
||||
return value
|
||||
}
|
||||
return beego.AppConfig.String(key)
|
||||
|
||||
res := beego.AppConfig.String(key)
|
||||
if res == "" {
|
||||
if key == "staticBaseUrl" {
|
||||
res = "https://cdn.casbin.org"
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func GetConfigBool(key string) (bool, error) {
|
||||
@ -72,3 +80,7 @@ func GetBeegoConfDataSourceName() string {
|
||||
|
||||
return dataSourceName
|
||||
}
|
||||
|
||||
func IsDemoMode() bool {
|
||||
return strings.ToLower(GetConfigString("isDemoMode")) == "true"
|
||||
}
|
||||
|
@ -269,6 +269,11 @@ func (c *ApiController) GetAccount() {
|
||||
return
|
||||
}
|
||||
|
||||
managedAccounts := c.Input().Get("managedAccounts")
|
||||
if managedAccounts == "1" {
|
||||
user = object.ExtendManagedAccountsWithUser(user)
|
||||
}
|
||||
|
||||
organization := object.GetMaskedOrganization(object.GetOrganizationByUser(user))
|
||||
resp := Response{
|
||||
Status: "ok",
|
||||
|
@ -121,6 +121,7 @@ func (idp *DingTalkIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
type DingTalkUserResponse struct {
|
||||
Nick string `json:"nick"`
|
||||
OpenId string `json:"openId"`
|
||||
UnionId string `json:"unionId"`
|
||||
AvatarUrl string `json:"avatarUrl"`
|
||||
Email string `json:"email"`
|
||||
Errmsg string `json:"message"`
|
||||
@ -162,6 +163,7 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
Id: dtUserInfo.OpenId,
|
||||
Username: dtUserInfo.Nick,
|
||||
DisplayName: dtUserInfo.Nick,
|
||||
UnionId: dtUserInfo.UnionId,
|
||||
Email: dtUserInfo.Email,
|
||||
AvatarUrl: dtUserInfo.AvatarUrl,
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ type UserInfo struct {
|
||||
Id string
|
||||
Username string
|
||||
DisplayName string
|
||||
UnionId string
|
||||
Email string
|
||||
AvatarUrl string
|
||||
}
|
||||
|
@ -362,3 +362,34 @@ func IsAllowOrigin(origin string) bool {
|
||||
|
||||
return allowOrigin
|
||||
}
|
||||
|
||||
func getApplicationMap(organization string) map[string]*Application {
|
||||
applications := GetApplicationsByOrganizationName("admin", organization)
|
||||
|
||||
applicationMap := make(map[string]*Application)
|
||||
for _, application := range applications {
|
||||
applicationMap[application.Name] = application
|
||||
}
|
||||
|
||||
return applicationMap
|
||||
}
|
||||
|
||||
func ExtendManagedAccountsWithUser(user *User) *User {
|
||||
if user.ManagedAccounts == nil || len(user.ManagedAccounts) == 0 {
|
||||
return user
|
||||
}
|
||||
|
||||
applicationMap := getApplicationMap(user.Owner)
|
||||
|
||||
var managedAccounts []ManagedAccount
|
||||
for _, managedAccount := range user.ManagedAccounts {
|
||||
application := applicationMap[managedAccount.Application]
|
||||
if application != nil {
|
||||
managedAccount.SigninUrl = application.SigninUrl
|
||||
managedAccounts = append(managedAccounts, managedAccount)
|
||||
}
|
||||
}
|
||||
user.ManagedAccounts = managedAccounts
|
||||
|
||||
return user
|
||||
}
|
||||
|
@ -302,6 +302,10 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
|
||||
}
|
||||
|
||||
if isHit {
|
||||
containsAsterisk := ContainsAsterisk(userId, permission.Users)
|
||||
if containsAsterisk {
|
||||
return true, err
|
||||
}
|
||||
enforcer := getEnforcer(permission)
|
||||
allowed, err = enforcer.Enforce(userId, application.Name, "read")
|
||||
break
|
||||
|
@ -16,10 +16,28 @@
|
||||
|
||||
package object
|
||||
|
||||
import "github.com/go-gomail/gomail"
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/go-gomail/gomail"
|
||||
)
|
||||
|
||||
func getDialer(provider *Provider) *gomail.Dialer {
|
||||
dialer := &gomail.Dialer{}
|
||||
if provider.Type == "SUBMAIL" {
|
||||
dialer = gomail.NewDialer(provider.Host, provider.Port, provider.AppId, provider.ClientSecret)
|
||||
dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
} else {
|
||||
dialer = gomail.NewDialer(provider.Host, provider.Port, provider.ClientId, provider.ClientSecret)
|
||||
}
|
||||
|
||||
dialer.SSL = !provider.DisableSsl
|
||||
|
||||
return dialer
|
||||
}
|
||||
|
||||
func SendEmail(provider *Provider, title string, content string, dest string, sender string) error {
|
||||
dialer := gomail.NewDialer(provider.Host, provider.Port, provider.ClientId, provider.ClientSecret)
|
||||
dialer := getDialer(provider)
|
||||
|
||||
message := gomail.NewMessage()
|
||||
message.SetAddressHeader("From", provider.ClientId, sender)
|
||||
@ -32,8 +50,7 @@ func SendEmail(provider *Provider, title string, content string, dest string, se
|
||||
|
||||
// DailSmtpServer Dail Smtp server
|
||||
func DailSmtpServer(provider *Provider) error {
|
||||
dialer := gomail.NewDialer(provider.Host, provider.Port, provider.ClientId, provider.ClientSecret)
|
||||
dialer.SSL = !provider.DisableSsl
|
||||
dialer := getDialer(provider)
|
||||
|
||||
sender, err := dialer.Dial()
|
||||
if err != nil {
|
||||
|
@ -19,12 +19,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/duo-labs/webauthn/webauthn"
|
||||
)
|
||||
|
||||
func InitDb() {
|
||||
MigratePermissionRule()
|
||||
|
||||
existed := initBuiltInOrganization()
|
||||
if !existed {
|
||||
initBuiltInModel()
|
||||
@ -39,8 +41,6 @@ func InitDb() {
|
||||
initWebAuthn()
|
||||
}
|
||||
|
||||
var staticBaseUrl = beego.AppConfig.String("staticBaseUrl")
|
||||
|
||||
func initBuiltInOrganization() bool {
|
||||
organization := getOrganization("admin", "built-in")
|
||||
if organization != nil {
|
||||
@ -53,10 +53,10 @@ func initBuiltInOrganization() bool {
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Built-in Organization",
|
||||
WebsiteUrl: "https://example.com",
|
||||
Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", staticBaseUrl),
|
||||
Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", conf.GetConfigString("staticBaseUrl")),
|
||||
PasswordType: "plain",
|
||||
PhonePrefix: "86",
|
||||
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", staticBaseUrl),
|
||||
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
|
||||
Tags: []string{},
|
||||
AccountItems: []*AccountItem{
|
||||
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||
@ -106,7 +106,7 @@ func initBuiltInUser() {
|
||||
Type: "normal-user",
|
||||
Password: "123",
|
||||
DisplayName: "Admin",
|
||||
Avatar: fmt.Sprintf("%s/img/casbin.svg", staticBaseUrl),
|
||||
Avatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
|
||||
Email: "admin@example.com",
|
||||
Phone: "12345678910",
|
||||
Address: []string{},
|
||||
@ -136,7 +136,7 @@ func initBuiltInApplication() {
|
||||
Name: "app-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Casdoor",
|
||||
Logo: fmt.Sprintf("%s/img/casdoor-logo_1185x256.png", staticBaseUrl),
|
||||
Logo: fmt.Sprintf("%s/img/casdoor-logo_1185x256.png", conf.GetConfigString("staticBaseUrl")),
|
||||
HomepageUrl: "https://casdoor.org",
|
||||
Organization: "built-in",
|
||||
Cert: "cert-built-in",
|
||||
@ -278,7 +278,7 @@ func initBuiltInPermission() {
|
||||
Name: "permission-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Built-in Permission",
|
||||
Users: []string{"built-in/admin"},
|
||||
Users: []string{"built-in/*"},
|
||||
Roles: []string{},
|
||||
Domains: []string{},
|
||||
Model: "model-built-in",
|
||||
|
@ -43,6 +43,11 @@ type OidcDiscovery struct {
|
||||
}
|
||||
|
||||
func getOriginFromHost(host string) (string, string) {
|
||||
origin := conf.GetConfigString("origin")
|
||||
if origin != "" {
|
||||
return origin, origin
|
||||
}
|
||||
|
||||
protocol := "https://"
|
||||
if strings.HasPrefix(host, "localhost") {
|
||||
protocol = "http://"
|
||||
@ -58,12 +63,6 @@ func getOriginFromHost(host string) (string, string) {
|
||||
func GetOidcDiscovery(host string) OidcDiscovery {
|
||||
originFrontend, originBackend := getOriginFromHost(host)
|
||||
|
||||
origin := conf.GetConfigString("origin")
|
||||
if origin != "" {
|
||||
originFrontend = origin
|
||||
originBackend = origin
|
||||
}
|
||||
|
||||
// Examples:
|
||||
// https://login.okta.com/.well-known/openid-configuration
|
||||
// https://auth0.auth0.com/.well-known/openid-configuration
|
||||
|
@ -16,6 +16,7 @@ package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"xorm.io/core"
|
||||
@ -207,3 +208,44 @@ func GetPermissionsBySubmitter(owner string, submitter string) []*Permission {
|
||||
|
||||
return permissions
|
||||
}
|
||||
|
||||
func MigratePermissionRule() {
|
||||
models := []*Model{}
|
||||
err := adapter.Engine.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 = adapter.Engine.Exec(sql)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ContainsAsterisk(userId string, users []string) bool {
|
||||
containsAsterisk := false
|
||||
group, _ := util.GetOwnerAndNameFromId(userId)
|
||||
for _, user := range users {
|
||||
permissionGroup, permissionUserName := util.GetOwnerAndNameFromId(user)
|
||||
if permissionGroup == group && permissionUserName == "*" {
|
||||
containsAsterisk = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return containsAsterisk
|
||||
}
|
||||
|
@ -48,6 +48,7 @@ type Provider struct {
|
||||
DisableSsl bool `json:"disableSsl"`
|
||||
Title string `xorm:"varchar(100)" json:"title"`
|
||||
Content string `xorm:"varchar(1000)" json:"content"`
|
||||
Receiver string `xorm:"varchar(100)" json:"receiver"`
|
||||
|
||||
RegionId string `xorm:"varchar(100)" json:"regionId"`
|
||||
SignName string `xorm:"varchar(100)" json:"signName"`
|
||||
|
@ -28,7 +28,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/RobotsAndPencils/go-saml"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/beevik/etree"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
dsig "github.com/russellhaering/goxmldsig"
|
||||
@ -176,16 +175,12 @@ type Attribute struct {
|
||||
}
|
||||
|
||||
func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, error) {
|
||||
//_, originBackend := getOriginFromHost(host)
|
||||
cert := getCertByApplication(application)
|
||||
block, _ := pem.Decode([]byte(cert.Certificate))
|
||||
certificate := base64.StdEncoding.EncodeToString(block.Bytes)
|
||||
|
||||
origin := beego.AppConfig.String("origin")
|
||||
originFrontend, originBackend := getOriginFromHost(host)
|
||||
if origin != "" {
|
||||
originBackend = origin
|
||||
}
|
||||
|
||||
d := IdpEntityDescriptor{
|
||||
XMLName: xml.Name{
|
||||
Local: "md:EntityDescriptor",
|
||||
|
@ -70,10 +70,12 @@ func GenerateSamlLoginUrl(id, relayState string) (string, string, error) {
|
||||
}
|
||||
|
||||
func buildSp(provider *Provider, samlResponse string) (*saml2.SAMLServiceProvider, error) {
|
||||
origin := conf.GetConfigString("origin")
|
||||
|
||||
certStore := dsig.MemoryX509CertificateStore{
|
||||
Roots: []*x509.Certificate{},
|
||||
}
|
||||
origin := conf.GetConfigString("origin")
|
||||
|
||||
certEncodedData := ""
|
||||
if samlResponse != "" {
|
||||
certEncodedData = parseSamlResponse(samlResponse, provider.Type)
|
||||
|
@ -103,6 +103,11 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
|
||||
}
|
||||
|
||||
func UploadFileSafe(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffer) (string, string, error) {
|
||||
// check fullFilePath is there security issue
|
||||
if strings.Contains(fullFilePath, "..") {
|
||||
return "", "", fmt.Errorf("the fullFilePath: %s is not allowed", fullFilePath)
|
||||
}
|
||||
|
||||
var fileUrl string
|
||||
var objectKey string
|
||||
var err error
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
)
|
||||
@ -67,11 +66,7 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
||||
refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours) * time.Hour)
|
||||
|
||||
user.Password = ""
|
||||
origin := conf.GetConfigString("origin")
|
||||
_, originBackend := getOriginFromHost(host)
|
||||
if origin != "" {
|
||||
originBackend = origin
|
||||
}
|
||||
|
||||
name := util.GenerateId()
|
||||
jti := fmt.Sprintf("%s/%s", application.Owner, name)
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/duo-labs/webauthn/webauthn"
|
||||
"xorm.io/core"
|
||||
@ -527,11 +526,8 @@ func GetUserInfo(userId string, scope string, aud string, host string) (*Userinf
|
||||
if user == nil {
|
||||
return nil, fmt.Errorf("the user: %s doesn't exist", userId)
|
||||
}
|
||||
origin := conf.GetConfigString("origin")
|
||||
|
||||
_, originBackend := getOriginFromHost(host)
|
||||
if origin != "" {
|
||||
originBackend = origin
|
||||
}
|
||||
|
||||
resp := Userinfo{
|
||||
Sub: user.Id,
|
||||
|
@ -137,6 +137,15 @@ func SetUserOAuthProperties(organization *Organization, user *User, providerType
|
||||
user.Email = userInfo.Email
|
||||
}
|
||||
}
|
||||
|
||||
if userInfo.UnionId != "" {
|
||||
propertyName := fmt.Sprintf("oauth_%s_unionId", providerType)
|
||||
setUserProperty(user, propertyName, userInfo.UnionId)
|
||||
if providerType == "DingTalk" && user.DingTalk == "" {
|
||||
user.DingTalk = userInfo.UnionId
|
||||
}
|
||||
}
|
||||
|
||||
if userInfo.AvatarUrl != "" {
|
||||
propertyName := fmt.Sprintf("oauth_%s_avatarUrl", providerType)
|
||||
setUserProperty(user, propertyName, userInfo.AvatarUrl)
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/duo-labs/webauthn/protocol"
|
||||
"github.com/duo-labs/webauthn/webauthn"
|
||||
)
|
||||
@ -27,20 +27,17 @@ import (
|
||||
func GetWebAuthnObject(host string) *webauthn.WebAuthn {
|
||||
var err error
|
||||
|
||||
origin := beego.AppConfig.String("origin")
|
||||
if origin == "" {
|
||||
_, origin = getOriginFromHost(host)
|
||||
}
|
||||
_, originBackend := getOriginFromHost(host)
|
||||
|
||||
localUrl, err := url.Parse(origin)
|
||||
localUrl, err := url.Parse(originBackend)
|
||||
if err != nil {
|
||||
panic("error when parsing origin:" + err.Error())
|
||||
}
|
||||
|
||||
webAuthn, err := webauthn.New(&webauthn.Config{
|
||||
RPDisplayName: beego.AppConfig.String("appname"), // Display Name for your site
|
||||
RPDisplayName: conf.GetConfigString("appname"), // Display Name for your site
|
||||
RPID: strings.Split(localUrl.Host, ":")[0], // Generally the domain name for your site, it's ok because splits cannot return empty array
|
||||
RPOrigin: origin, // The origin URL for WebAuthn requests
|
||||
RPOrigin: originBackend, // The origin URL for WebAuthn requests
|
||||
// RPIcon: "https://duo.com/logo.png", // Optional icon URL for your site
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -47,7 +47,7 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
|
||||
|
||||
sender := organization.DisplayName
|
||||
title := provider.Title
|
||||
code := getRandomCode(5)
|
||||
code := getRandomCode(6)
|
||||
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
|
||||
content := fmt.Sprintf(provider.Content, code)
|
||||
|
||||
@ -63,7 +63,7 @@ func SendVerificationCodeToPhone(organization *Organization, user *User, provide
|
||||
return errors.New("please set a SMS provider first")
|
||||
}
|
||||
|
||||
code := getRandomCode(5)
|
||||
code := getRandomCode(6)
|
||||
if err := SendSms(provider, code, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -19,14 +19,14 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/context"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
var (
|
||||
oldStaticBaseUrl = "https://cdn.casbin.org"
|
||||
newStaticBaseUrl = beego.AppConfig.String("staticBaseUrl")
|
||||
newStaticBaseUrl = conf.GetConfigString("staticBaseUrl")
|
||||
)
|
||||
|
||||
func StaticFilter(ctx *context.Context) {
|
||||
|
@ -4,12 +4,18 @@
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"parser": "@babel/eslint-parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"requireConfigFile": false,
|
||||
"babelOptions": {
|
||||
"babelrc": false,
|
||||
"configFile": false,
|
||||
"presets": ["@babel/preset-react"]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
@ -91,6 +97,7 @@
|
||||
"react/jsx-key": "error",
|
||||
"no-console": "error",
|
||||
"eqeqeq": "error",
|
||||
"keyword-spacing": "error",
|
||||
|
||||
"react/prop-types": "off",
|
||||
"react/display-name": "off",
|
||||
|
6
web/.stylelintrc.json
Normal file
6
web/.stylelintrc.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": [
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-recommended-less"
|
||||
]
|
||||
}
|
17
web/babel.config.json
Normal file
17
web/babel.config.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"edge": "17",
|
||||
"firefox": "60",
|
||||
"chrome": "67",
|
||||
"safari": "11.1"
|
||||
},
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.6.5"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
@ -3,35 +3,35 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.6.2",
|
||||
"@craco/craco": "^6.1.1",
|
||||
"@crowdin/cli": "^3.6.4",
|
||||
"@ant-design/icons": "^4.7.0",
|
||||
"@craco/craco": "^6.4.5",
|
||||
"@crowdin/cli": "^3.7.10",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"antd": "^4.15.5",
|
||||
"antd": "^4.22.8",
|
||||
"codemirror": "^5.61.1",
|
||||
"copy-to-clipboard": "^3.3.1",
|
||||
"core-js": "^3.21.1",
|
||||
"craco-less": "^1.17.1",
|
||||
"core-js": "^3.25.0",
|
||||
"craco-less": "^2.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"i18n-iso-countries": "^7.0.0",
|
||||
"i18next": "^19.8.9",
|
||||
"moment": "^2.29.1",
|
||||
"qs": "^6.10.2",
|
||||
"react": "^17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-app-polyfill": "^3.0.0",
|
||||
"react-codemirror2": "^7.2.1",
|
||||
"react-cropper": "^2.1.7",
|
||||
"react-device-detect": "^1.14.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-device-detect": "^2.2.2",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-github-corner": "^2.5.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-highlight-words": "^0.17.0",
|
||||
"react-highlight-words": "^0.18.0",
|
||||
"react-i18next": "^11.8.7",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-router-dom": "^5.3.3",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-social-login-buttons": "^3.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
@ -41,7 +41,8 @@
|
||||
"eject": "craco eject",
|
||||
"crowdin:sync": "crowdin upload && crowdin download",
|
||||
"preinstall": "node -e \"if (process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('Use yarn for installing: https://yarnpkg.com/en/docs/install')\"",
|
||||
"fix": "eslint --fix ."
|
||||
"fix": "eslint --fix src/**/*.{js,jsx,ts,tsx}",
|
||||
"lint:css": "stylelint src/**/*.{css,less} --fix"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
@ -61,15 +62,24 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.13",
|
||||
"@babel/eslint-parser": "^7.18.9",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-plugin-react": "^7.30.1",
|
||||
"eslint": "8.22.0",
|
||||
"eslint-plugin-react": "^7.31.1",
|
||||
"husky": "^4.3.8",
|
||||
"lint-staged": "^13.0.3"
|
||||
"lint-staged": "^13.0.3",
|
||||
"stylelint": "^14.11.0",
|
||||
"stylelint-config-recommended-less": "^1.0.4",
|
||||
"stylelint-config-standard": "^28.0.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{js,jsx,css,sass,ts,tsx}": [
|
||||
"yarn fix"
|
||||
"src/**/*.{css,less}": [
|
||||
"stylelint --fix"
|
||||
],
|
||||
"src/**/*.{js,jsx,ts,tsx}": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
|
@ -527,7 +527,7 @@ class App extends Component {
|
||||
}
|
||||
|
||||
renderRouter() {
|
||||
return(
|
||||
return (
|
||||
<div>
|
||||
<Switch>
|
||||
<Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...props} />)} />
|
||||
@ -595,7 +595,7 @@ class App extends Component {
|
||||
// theme="dark"
|
||||
mode={(Setting.isMobile() && this.isStartPages()) ? "inline" : "horizontal"}
|
||||
selectedKeys={[`${this.state.selectedMenuKey}`]}
|
||||
style={{lineHeight: "64px", width: "80%", position: "absolute"}}
|
||||
style={{lineHeight: "64px", width: "80%", position: "absolute", left: "145px"}}
|
||||
>
|
||||
{
|
||||
this.renderMenu()
|
||||
@ -618,7 +618,7 @@ class App extends Component {
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return(
|
||||
return (
|
||||
<div>
|
||||
<Header style={{padding: "0", marginBottom: "3px"}}>
|
||||
{
|
||||
@ -746,6 +746,7 @@ class App extends Component {
|
||||
const organization = this.state.account.organization;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div style={{display: "none"}} id="CasdoorApplicationName" value={this.state.account.signupApplication} />
|
||||
<Helmet>
|
||||
<title>{organization.displayName}</title>
|
||||
<link rel="icon" href={organization.favicon} />
|
||||
|
@ -1,6 +1,8 @@
|
||||
@import '~antd/dist/antd.less';
|
||||
/* stylelint-disable at-rule-name-case */
|
||||
/* stylelint-disable selector-class-pattern */
|
||||
@import "~antd/dist/antd.less";
|
||||
|
||||
@StaticBaseUrl:"https://cdn.casbin.org";
|
||||
@StaticBaseUrl: "https://cdn.casbin.org";
|
||||
|
||||
.App {
|
||||
text-align: center;
|
||||
@ -69,8 +71,8 @@
|
||||
}
|
||||
|
||||
.content-warp-card {
|
||||
box-shadow: 0 1px 5px 0 rgba(51, 51, 51, 0.14);
|
||||
margin: 5px 5px 5px 5px;
|
||||
box-shadow: 0 1px 5px 0 rgb(51 51 51 / 14%);
|
||||
margin: 5px;
|
||||
flex: 1;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
@ -39,101 +39,102 @@ class BaseListPage extends React.Component {
|
||||
this.fetch({pagination});
|
||||
}
|
||||
|
||||
getColumnSearchProps = dataIndex => ({
|
||||
filterDropdown: ({setSelectedKeys, selectedKeys, confirm, clearFilters}) => (
|
||||
<div style={{padding: 8}}>
|
||||
<Input
|
||||
ref={node => {
|
||||
this.searchInput = node;
|
||||
}}
|
||||
placeholder={`Search ${dataIndex}`}
|
||||
value={selectedKeys[0]}
|
||||
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
|
||||
onPressEnter={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
|
||||
style={{marginBottom: 8, display: "block"}}
|
||||
/>
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
|
||||
icon={<SearchOutlined />}
|
||||
size="small"
|
||||
style={{width: 90}}
|
||||
>
|
||||
getColumnSearchProps = dataIndex => ({
|
||||
filterDropdown: ({setSelectedKeys, selectedKeys, confirm, clearFilters}) => (
|
||||
<div style={{padding: 8}}>
|
||||
<Input
|
||||
ref={node => {
|
||||
this.searchInput = node;
|
||||
}}
|
||||
placeholder={`Search ${dataIndex}`}
|
||||
value={selectedKeys[0]}
|
||||
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
|
||||
onPressEnter={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
|
||||
style={{marginBottom: 8, display: "block"}}
|
||||
/>
|
||||
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => this.handleSearch(selectedKeys, confirm, dataIndex)}
|
||||
icon={<SearchOutlined />}
|
||||
size="small"
|
||||
style={{width: 90}}
|
||||
>
|
||||
Search
|
||||
</Button>
|
||||
<Button onClick={() => this.handleReset(clearFilters)} size="small" style={{width: 90}}>
|
||||
</Button>
|
||||
<Button onClick={() => this.handleReset(clearFilters)} size="small" style={{width: 90}}>
|
||||
Reset
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
confirm({closeDropdown: false});
|
||||
this.setState({
|
||||
searchText: selectedKeys[0],
|
||||
searchedColumn: dataIndex,
|
||||
});
|
||||
}}
|
||||
>
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
confirm({closeDropdown: false});
|
||||
this.setState({
|
||||
searchText: selectedKeys[0],
|
||||
searchedColumn: dataIndex,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Filter
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
),
|
||||
filterIcon: filtered => <SearchOutlined style={{color: filtered ? "#1890ff" : undefined}} />,
|
||||
onFilter: (value, record) =>
|
||||
record[dataIndex]
|
||||
? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
|
||||
: "",
|
||||
onFilterDropdownVisibleChange: visible => {
|
||||
if (visible) {
|
||||
setTimeout(() => this.searchInput.select(), 100);
|
||||
}
|
||||
},
|
||||
render: text =>
|
||||
this.state.searchedColumn === dataIndex ? (
|
||||
<Highlighter
|
||||
highlightStyle={{backgroundColor: "#ffc069", padding: 0}}
|
||||
searchWords={[this.state.searchText]}
|
||||
autoEscape
|
||||
textToHighlight={text ? text.toString() : ""}
|
||||
/>
|
||||
) : (
|
||||
text
|
||||
),
|
||||
filterIcon: filtered => <SearchOutlined style={{color: filtered ? "#1890ff" : undefined}} />,
|
||||
onFilter: (value, record) =>
|
||||
record[dataIndex]
|
||||
? record[dataIndex].toString().toLowerCase().includes(value.toLowerCase())
|
||||
: "",
|
||||
onFilterDropdownVisibleChange: visible => {
|
||||
if (visible) {
|
||||
setTimeout(() => this.searchInput.select(), 100);
|
||||
}
|
||||
},
|
||||
render: text =>
|
||||
this.state.searchedColumn === dataIndex ? (
|
||||
<Highlighter
|
||||
highlightStyle={{backgroundColor: "#ffc069", padding: 0}}
|
||||
searchWords={[this.state.searchText]}
|
||||
autoEscape
|
||||
textToHighlight={text ? text.toString() : ""}
|
||||
/>
|
||||
) : (
|
||||
text
|
||||
),
|
||||
});
|
||||
|
||||
handleSearch = (selectedKeys, confirm, dataIndex) => {
|
||||
this.fetch({searchText: selectedKeys[0], searchedColumn: dataIndex, pagination: this.state.pagination});
|
||||
};
|
||||
|
||||
handleReset = clearFilters => {
|
||||
clearFilters();
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
};
|
||||
|
||||
handleTableChange = (pagination, filters, sorter) => {
|
||||
this.fetch({
|
||||
sortField: sorter.field,
|
||||
sortOrder: sorter.order,
|
||||
pagination,
|
||||
...filters,
|
||||
searchText: this.state.searchText,
|
||||
searchedColumn: this.state.searchedColumn,
|
||||
});
|
||||
};
|
||||
|
||||
handleSearch = (selectedKeys, confirm, dataIndex) => {
|
||||
this.fetch({searchText: selectedKeys[0], searchedColumn: dataIndex, pagination: this.state.pagination});
|
||||
};
|
||||
|
||||
handleReset = clearFilters => {
|
||||
clearFilters();
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
};
|
||||
|
||||
handleTableChange = (pagination, filters, sorter) => {
|
||||
this.fetch({
|
||||
sortField: sorter.field,
|
||||
sortOrder: sorter.order,
|
||||
pagination,
|
||||
...filters,
|
||||
searchText: this.state.searchText,
|
||||
searchedColumn: this.state.searchedColumn,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
this.renderTable(this.state.data)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
this.renderTable(this.state.data)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseListPage;
|
||||
|
@ -38,7 +38,7 @@ class ManagedAccountTable extends React.Component {
|
||||
}
|
||||
|
||||
addRow(table) {
|
||||
const row = {application: "", username: "", password: "", signinUrl: ""};
|
||||
const row = {application: "", username: "", password: ""};
|
||||
if (table === undefined || table === null) {
|
||||
table = [];
|
||||
}
|
||||
@ -69,16 +69,11 @@ class ManagedAccountTable extends React.Component {
|
||||
key: "application",
|
||||
render: (text, record, index) => {
|
||||
const items = this.props.applications;
|
||||
const signinUrlMap = new Map();
|
||||
for (const application of items) {
|
||||
signinUrlMap.set(application.name, application.signinUrl);
|
||||
}
|
||||
return (
|
||||
<Select virtual={false} style={{width: "100%"}}
|
||||
value={text}
|
||||
onChange={value => {
|
||||
this.updateField(table, index, "application", value);
|
||||
this.updateField(table, index, "signinUrl", signinUrlMap.get(value));
|
||||
}} >
|
||||
{
|
||||
items.map((item, index) => <Option key={index} value={item.name}>{item.name}</Option>)
|
||||
|
@ -88,7 +88,7 @@ class ModelListPage extends BaseListPage {
|
||||
...this.getColumnSearchProps("name"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/models/${text}`}>
|
||||
<Link to={`/models/${record.owner}/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
|
@ -34,7 +34,6 @@ class ProviderEditPage extends React.Component {
|
||||
providerName: props.match.params.providerName,
|
||||
provider: null,
|
||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||
testEmail: this.props.account["email"] !== undefined ? this.props.account["email"] : "",
|
||||
};
|
||||
}
|
||||
|
||||
@ -131,6 +130,9 @@ class ProviderEditPage extends React.Component {
|
||||
} else if (this.state.provider.category === "SMS" && this.state.provider.type === "Huawei Cloud SMS") {
|
||||
text = i18next.t("provider:Channel No.");
|
||||
tooltip = i18next.t("provider:Channel No. - Tooltip");
|
||||
} else if (this.state.provider.category === "Email" && this.state.provider.type === "SUBMAIL") {
|
||||
text = i18next.t("provider:App ID");
|
||||
tooltip = i18next.t("provider:App ID - Tooltip");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -199,9 +201,12 @@ class ProviderEditPage extends React.Component {
|
||||
this.updateProviderField("type", "GitHub");
|
||||
} else if (value === "Email") {
|
||||
this.updateProviderField("type", "Default");
|
||||
this.updateProviderField("host", "smtp.example.com");
|
||||
this.updateProviderField("port", 465);
|
||||
this.updateProviderField("disableSsl", false);
|
||||
this.updateProviderField("title", "Casdoor Verification Code");
|
||||
this.updateProviderField("content", "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes.");
|
||||
this.updateProviderField("receiver", this.props.account.email);
|
||||
} else if (value === "SMS") {
|
||||
this.updateProviderField("type", "Aliyun SMS");
|
||||
} else if (value === "Storage") {
|
||||
@ -536,7 +541,7 @@ class ProviderEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("provider:Email Content"), i18next.t("provider:Email Content - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<TextArea autoSize={{minRows: 1, maxRows: 100}} value={this.state.provider.content} onChange={e => {
|
||||
<TextArea autoSize={{minRows: 3, maxRows: 100}} value={this.state.provider.content} onChange={e => {
|
||||
this.updateProviderField("content", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@ -546,19 +551,16 @@ class ProviderEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("provider:Test Email"), i18next.t("provider:Test Email - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={4} >
|
||||
<Input value={this.state.testEmail}
|
||||
placeHolder = {i18next.t("user:Input your email")}
|
||||
onChange={e => {
|
||||
this.setState({testEmail: e.target.value});
|
||||
}} />
|
||||
<Input value={this.state.provider.receiver} placeholder = {i18next.t("user:Input your email")} onChange={e => {
|
||||
this.updateProviderField("receiver", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary"
|
||||
onClick={() => ProviderEditTestEmail.connectSmtpServer(this.state.provider)} >
|
||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary" onClick={() => ProviderEditTestEmail.connectSmtpServer(this.state.provider)} >
|
||||
{i18next.t("provider:Test Connection")}
|
||||
</Button>
|
||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary"
|
||||
disabled={!Setting.isValidEmail(this.state.testEmail)}
|
||||
onClick={() => ProviderEditTestEmail.sendTestEmail(this.state.provider, this.state.testEmail)} >
|
||||
disabled={!Setting.isValidEmail(this.state.provider.receiver)}
|
||||
onClick={() => ProviderEditTestEmail.sendTestEmail(this.state.provider, this.state.provider.receiver)} >
|
||||
{i18next.t("provider:Send Test Email")}
|
||||
</Button>
|
||||
</Row>
|
||||
|
@ -56,8 +56,12 @@ export const ResetModal = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
let placeHolder = "";
|
||||
if (destType === "email") {placeHolder = i18next.t("user:Input your email");} else if (destType === "phone") {placeHolder = i18next.t("user:Input your phone number");}
|
||||
let placeholder = "";
|
||||
if (destType === "email") {
|
||||
placeholder = i18next.t("user:Input your email");
|
||||
} else if (destType === "phone") {
|
||||
placeholder = i18next.t("user:Input your phone number");
|
||||
}
|
||||
|
||||
return (
|
||||
<Row>
|
||||
@ -80,7 +84,7 @@ export const ResetModal = (props) => {
|
||||
<Input
|
||||
addonBefore={destType === "email" ? i18next.t("user:New Email") : i18next.t("user:New phone")}
|
||||
prefix={destType === "email" ? <MailOutlined /> : <PhoneOutlined />}
|
||||
placeholder={placeHolder}
|
||||
placeholder={placeholder}
|
||||
onChange={e => setDest(e.target.value)}
|
||||
/>
|
||||
</Row>
|
||||
|
@ -450,9 +450,9 @@ export function trim(str, ch) {
|
||||
let start = 0;
|
||||
let end = str.length;
|
||||
|
||||
while(start < end && str[start] === ch) {++start;}
|
||||
while (start < end && str[start] === ch) {++start;}
|
||||
|
||||
while(end > start && str[end - 1] === ch) {--end;}
|
||||
while (end > start && str[end - 1] === ch) {--end;}
|
||||
|
||||
return (start > 0 || end < str.length) ? str.substring(start, end) : str;
|
||||
}
|
||||
@ -633,6 +633,7 @@ export function getProviderTypeOptions(category) {
|
||||
return (
|
||||
[
|
||||
{id: "Default", name: "Default"},
|
||||
{id: "SUBMAIL", name: "SUBMAIL"},
|
||||
]
|
||||
);
|
||||
} else if (category === "SMS") {
|
||||
|
@ -50,8 +50,12 @@ class AuthCallback extends React.Component {
|
||||
// Casdoor's own login page, so "code" is not necessary
|
||||
if (realRedirectUri === null) {
|
||||
const samlRequest = innerParams.get("SAMLRequest");
|
||||
// cas don't use 'redirect_url', it is called 'service'
|
||||
const casService = innerParams.get("service");
|
||||
if (samlRequest !== null && samlRequest !== undefined && samlRequest !== "") {
|
||||
return "saml";
|
||||
} else if (casService !== null && casService !== undefined && casService !== "") {
|
||||
return "cas";
|
||||
}
|
||||
return "login";
|
||||
}
|
||||
@ -97,6 +101,7 @@ class AuthCallback extends React.Component {
|
||||
const providerName = innerParams.get("provider");
|
||||
const method = innerParams.get("method");
|
||||
const samlRequest = innerParams.get("SAMLRequest");
|
||||
const casService = innerParams.get("service");
|
||||
|
||||
const redirectUri = `${window.location.origin}/callback`;
|
||||
|
||||
@ -111,6 +116,31 @@ class AuthCallback extends React.Component {
|
||||
redirectUri: redirectUri,
|
||||
method: method,
|
||||
};
|
||||
|
||||
if (this.getResponseType() === "cas") {
|
||||
// user is using casdoor as cas sso server, and wants the ticket to be acquired
|
||||
AuthBackend.loginCas(body, {"service": casService}).then((res) => {
|
||||
if (res.status === "ok") {
|
||||
let msg = "Logged in successfully.";
|
||||
if (casService === "") {
|
||||
// If service was not specified, Casdoor must display a message notifying the client that it has successfully initiated a single sign-on session.
|
||||
msg += "Now you can visit apps protected by Casdoor.";
|
||||
}
|
||||
Util.showMessage("success", msg);
|
||||
|
||||
if (casService !== "") {
|
||||
const st = res.data;
|
||||
const newUrl = new URL(casService);
|
||||
newUrl.searchParams.append("ticket", st);
|
||||
window.location.href = newUrl.toString();
|
||||
}
|
||||
} else {
|
||||
Util.showMessage("error", `Failed to log in: ${res.msg}`);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
// OAuth
|
||||
const oAuthParams = Util.getOAuthGetParameters(innerParams);
|
||||
const concatChar = oAuthParams?.redirectUri?.includes("?") ? "&" : "?";
|
||||
AuthBackend.login(body, oAuthParams)
|
||||
|
@ -584,9 +584,9 @@ class SignupPage extends React.Component {
|
||||
{i18next.t("signup:Have account?")}
|
||||
<a onClick={() => {
|
||||
const linkInStorage = sessionStorage.getItem("signinUrl");
|
||||
if(linkInStorage !== null && linkInStorage !== "") {
|
||||
if (linkInStorage !== null && linkInStorage !== "") {
|
||||
Setting.goToLink(linkInStorage);
|
||||
}else{
|
||||
} else {
|
||||
Setting.goToLogin(this, application);
|
||||
}
|
||||
}}>
|
||||
|
@ -1,14 +1,28 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
font-family:
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
"Fira Sans",
|
||||
"Droid Sans",
|
||||
"Helvetica Neue",
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
font-family:
|
||||
source-code-pro,
|
||||
Menlo,
|
||||
Monaco,
|
||||
Consolas,
|
||||
"Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
||||
@ -17,12 +31,14 @@ code {
|
||||
background-size: 130px, 27px;
|
||||
width: 130px;
|
||||
height: 27px;
|
||||
/*background: rgba(0, 0, 0, 0.2);*/
|
||||
margin: 17px 10px 16px 20px;
|
||||
margin: 17px 0 16px 15px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ant-table.ant-table-middle .ant-table-title, .ant-table.ant-table-middle .ant-table-footer, .ant-table.ant-table-middle thead > tr > th, .ant-table.ant-table-middle tbody > tr > td {
|
||||
.ant-table.ant-table-middle .ant-table-title,
|
||||
.ant-table.ant-table-middle .ant-table-footer,
|
||||
.ant-table.ant-table-middle thead > tr > th,
|
||||
.ant-table.ant-table-middle tbody > tr > td {
|
||||
padding: 1px 8px !important;
|
||||
}
|
||||
|
||||
|
@ -16,18 +16,19 @@ import "core-js/es";
|
||||
import "react-app-polyfill/ie9";
|
||||
import "react-app-polyfill/stable";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import {createRoot} from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
import {BrowserRouter} from "react-router-dom";
|
||||
|
||||
ReactDOM.render(
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
const container = document.getElementById("root");
|
||||
|
||||
const app = createRoot(container);
|
||||
|
||||
app.render(<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>);
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
|
9191
web/yarn.lock
9191
web/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user