Compare commits

..

1 Commits

Author SHA1 Message Date
Yang Luo
7e46222ecd Revert "feat: add Casbin editor's checking in model editor (#3166)"
This reverts commit a1b010a406.
2024-09-03 21:58:51 +08:00
22 changed files with 97 additions and 539 deletions

View File

@@ -13,7 +13,7 @@
<a href="https://github.com/casdoor/casdoor/releases/latest">
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/casdoor/casdoor.svg">
</a>
<a href="https://hub.docker.com/r/casbin/casdoor">
<a href="https://hub.docker.com/repository/docker/casbin/casdoor">
<img alt="Docker Image Version (latest semver)" src="https://img.shields.io/badge/Docker%20Hub-latest-brightgreen">
</a>
</p>

View File

@@ -65,7 +65,7 @@ func (c *ApiController) GetOrganizations() {
c.ResponseOk(organizations)
} else {
limit := util.ParseInt(limit)
count, err := object.GetOrganizationCount(owner, organizationName, field, value)
count, err := object.GetOrganizationCount(owner, field, value)
if err != nil {
c.ResponseError(err.Error())
return
@@ -138,7 +138,7 @@ func (c *ApiController) AddOrganization() {
return
}
count, err := object.GetOrganizationCount("", "", "", "")
count, err := object.GetOrganizationCount("", "", "")
if err != nil {
c.ResponseError(err.Error())
return

View File

@@ -71,7 +71,6 @@ func main() {
beego.BConfig.WebConfig.Session.SessionProviderConfig = conf.GetConfigString("redisEndpoint")
}
beego.BConfig.WebConfig.Session.SessionCookieLifeTime = 3600 * 24 * 30
beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600 * 24 * 30
// beego.BConfig.WebConfig.Session.SessionCookieSameSite = http.SameSiteNoneMode
err := logs.SetLogger(logs.AdapterFile, conf.GetConfigString("logConfig"))

View File

@@ -31,17 +31,15 @@ type SigninMethod struct {
}
type SignupItem struct {
Name string `json:"name"`
Visible bool `json:"visible"`
Required bool `json:"required"`
Prompted bool `json:"prompted"`
Type string `json:"type"`
CustomCss string `json:"customCss"`
Label string `json:"label"`
Placeholder string `json:"placeholder"`
Options []string `json:"options"`
Regex string `json:"regex"`
Rule string `json:"rule"`
Name string `json:"name"`
Visible bool `json:"visible"`
Required bool `json:"required"`
Prompted bool `json:"prompted"`
CustomCss string `json:"customCss"`
Label string `json:"label"`
Placeholder string `json:"placeholder"`
Regex string `json:"regex"`
Rule string `json:"rule"`
}
type SigninItem struct {
@@ -80,7 +78,6 @@ type Application struct {
EnableSamlCompress bool `json:"enableSamlCompress"`
EnableSamlC14n10 bool `json:"enableSamlC14n10"`
EnableSamlPostBinding bool `json:"enableSamlPostBinding"`
UseEmailAsSamlNameId bool `json:"useEmailAsSamlNameId"`
EnableWebAuthn bool `json:"enableWebAuthn"`
EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
OrgChoiceMode string `json:"orgChoiceMode"`
@@ -534,7 +531,7 @@ func GetMaskedApplication(application *Application, userId string) *Application
providerItems := []*ProviderItem{}
for _, providerItem := range application.Providers {
if providerItem.Provider != nil && (providerItem.Provider.Category == "OAuth" || providerItem.Provider.Category == "Web3" || providerItem.Provider.Category == "Captcha" || providerItem.Provider.Category == "SAML") {
if providerItem.Provider != nil && (providerItem.Provider.Category == "OAuth" || providerItem.Provider.Category == "Web3" || providerItem.Provider.Category == "Captcha") {
providerItems = append(providerItems, providerItem)
}
}

View File

@@ -79,9 +79,9 @@ type Organization struct {
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`
}
func GetOrganizationCount(owner, name, field, value string) (int64, error) {
func GetOrganizationCount(owner, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "")
return session.Count(&Organization{Name: name})
return session.Count(&Organization{})
}
func GetOrganizations(owner string, name ...string) ([]*Organization, error) {

View File

@@ -65,11 +65,7 @@ func NewSamlResponse(application *Application, user *User, host string, certific
assertion.CreateAttr("IssueInstant", now)
assertion.CreateElement("saml:Issuer").SetText(host)
subject := assertion.CreateElement("saml:Subject")
nameIDValue := user.Name
if application.UseEmailAsSamlNameId {
nameIDValue = user.Email
}
subject.CreateElement("saml:NameID").SetText(nameIDValue)
subject.CreateElement("saml:NameID").SetText(user.Name)
subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation")
subjectConfirmation.CreateAttr("Method", "urn:oasis:names:tc:SAML:2.0:cm:bearer")
subjectConfirmationData := subjectConfirmation.CreateElement("saml:SubjectConfirmationData")
@@ -188,17 +184,17 @@ type NameIDFormat struct {
}
type SingleSignOnService struct {
// XMLName xml.Name
XMLName xml.Name
Binding string `xml:"Binding,attr"`
Location string `xml:"Location,attr"`
}
type Attribute struct {
// XMLName xml.Name
Xmlns string `xml:"xmlns,attr"`
Name string `xml:"Name,attr"`
NameFormat string `xml:"NameFormat,attr"`
FriendlyName string `xml:"FriendlyName,attr"`
Xmlns string `xml:"xmlns,attr"`
Values []string `xml:"AttributeValue"`
}
@@ -390,7 +386,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
}
// NewSamlResponse11 return a saml1.1 response(not 2.0)
func NewSamlResponse11(application *Application, user *User, requestID string, host string) (*etree.Element, error) {
func NewSamlResponse11(user *User, requestID string, host string) (*etree.Element, error) {
samlResponse := &etree.Element{
Space: "samlp",
Tag: "Response",
@@ -434,11 +430,7 @@ func NewSamlResponse11(application *Application, user *User, requestID string, h
// nameIdentifier inside subject
nameIdentifier := subject.CreateElement("saml:NameIdentifier")
// nameIdentifier.CreateAttr("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")
if application.UseEmailAsSamlNameId {
nameIdentifier.SetText(user.Email)
} else {
nameIdentifier.SetText(user.Name)
}
nameIdentifier.SetText(user.Name)
// subjectConfirmation inside subject
subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation")
@@ -447,11 +439,7 @@ func NewSamlResponse11(application *Application, user *User, requestID string, h
attributeStatement := assertion.CreateElement("saml:AttributeStatement")
subjectInAttribute := attributeStatement.CreateElement("saml:Subject")
nameIdentifierInAttribute := subjectInAttribute.CreateElement("saml:NameIdentifier")
if application.UseEmailAsSamlNameId {
nameIdentifierInAttribute.SetText(user.Email)
} else {
nameIdentifierInAttribute.SetText(user.Name)
}
nameIdentifierInAttribute.SetText(user.Name)
subjectConfirmationInAttribute := subjectInAttribute.CreateElement("saml:SubjectConfirmation")
subjectConfirmationInAttribute.CreateElement("saml:ConfirmationMethod").SetText("urn:oasis:names:tc:SAML:1.0:cm:artifact")

View File

@@ -281,7 +281,7 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
return "", "", fmt.Errorf("the application for user %s is not found", userId)
}
samlResponse, err := NewSamlResponse11(application, user, request.RequestID, host)
samlResponse, err := NewSamlResponse11(user, request.RequestID, host)
if err != nil {
return "", "", err
}

View File

@@ -950,17 +950,7 @@ func DeleteUser(user *User) (bool, error) {
return false, err
}
organization, err := GetOrganizationByUser(user)
if err != nil {
return false, err
}
if organization != nil && organization.EnableSoftDeletion {
user.IsDeleted = true
user.DeletedTime = util.GetCurrentTime()
return UpdateUser(user.GetId(), user, []string{"is_deleted", "deleted_time"}, false)
} else {
return deleteUser(user)
}
return deleteUser(user)
}
func GetUserInfo(user *User, scope string, aud string, host string) (*Userinfo, error) {

View File

@@ -271,213 +271,113 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
if oldUser.Owner != newUser.Owner {
item := GetAccountItemByName("Organization", organization)
if item == nil {
newUser.Owner = oldUser.Owner
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Name != newUser.Name {
item := GetAccountItemByName("Name", organization)
if item == nil {
newUser.Name = oldUser.Name
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Id != newUser.Id {
item := GetAccountItemByName("ID", organization)
if item == nil {
newUser.Id = oldUser.Id
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.DisplayName != newUser.DisplayName {
item := GetAccountItemByName("Display name", organization)
if item == nil {
newUser.DisplayName = oldUser.DisplayName
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Avatar != newUser.Avatar {
item := GetAccountItemByName("Avatar", organization)
if item == nil {
newUser.Avatar = oldUser.Avatar
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Type != newUser.Type {
item := GetAccountItemByName("User type", organization)
if item == nil {
newUser.Type = oldUser.Type
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
// The password is *** when not modified
if oldUser.Password != newUser.Password && newUser.Password != "***" {
item := GetAccountItemByName("Password", organization)
if item == nil {
newUser.Password = oldUser.Password
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Email != newUser.Email {
item := GetAccountItemByName("Email", organization)
if item == nil {
newUser.Email = oldUser.Email
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Phone != newUser.Phone {
item := GetAccountItemByName("Phone", organization)
if item == nil {
newUser.Phone = oldUser.Phone
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.CountryCode != newUser.CountryCode {
item := GetAccountItemByName("Country code", organization)
if item == nil {
newUser.CountryCode = oldUser.CountryCode
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Region != newUser.Region {
item := GetAccountItemByName("Country/Region", organization)
if item == nil {
newUser.Region = oldUser.Region
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Location != newUser.Location {
item := GetAccountItemByName("Location", organization)
if item == nil {
newUser.Location = oldUser.Location
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Affiliation != newUser.Affiliation {
item := GetAccountItemByName("Affiliation", organization)
if item == nil {
newUser.Affiliation = oldUser.Affiliation
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Title != newUser.Title {
item := GetAccountItemByName("Title", organization)
if item == nil {
newUser.Title = oldUser.Title
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Homepage != newUser.Homepage {
item := GetAccountItemByName("Homepage", organization)
if item == nil {
newUser.Homepage = oldUser.Homepage
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Bio != newUser.Bio {
item := GetAccountItemByName("Bio", organization)
if item == nil {
newUser.Bio = oldUser.Bio
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Tag != newUser.Tag {
item := GetAccountItemByName("Tag", organization)
if item == nil {
newUser.Tag = oldUser.Tag
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.SignupApplication != newUser.SignupApplication {
item := GetAccountItemByName("Signup application", organization)
if item == nil {
newUser.SignupApplication = oldUser.SignupApplication
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Gender != newUser.Gender {
item := GetAccountItemByName("Gender", organization)
if item == nil {
newUser.Gender = oldUser.Gender
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Birthday != newUser.Birthday {
item := GetAccountItemByName("Birthday", organization)
if item == nil {
newUser.Birthday = oldUser.Birthday
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Education != newUser.Education {
item := GetAccountItemByName("Education", organization)
if item == nil {
newUser.Education = oldUser.Education
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.IdCard != newUser.IdCard {
item := GetAccountItemByName("ID card", organization)
if item == nil {
newUser.IdCard = oldUser.IdCard
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.IdCardType != newUser.IdCardType {
item := GetAccountItemByName("ID card type", organization)
if item == nil {
newUser.IdCardType = oldUser.IdCardType
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties)
newUserPropertiesJson, _ := json.Marshal(newUser.Properties)
if string(oldUserPropertiesJson) != string(newUserPropertiesJson) {
item := GetAccountItemByName("Properties", organization)
if item == nil {
newUser.Properties = oldUser.Properties
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.PreferredMfaType != newUser.PreferredMfaType {
item := GetAccountItemByName("Multi-factor authentication", organization)
if item == nil {
newUser.PreferredMfaType = oldUser.PreferredMfaType
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Groups == nil {
@@ -490,11 +390,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
newUserGroupsJson, _ := json.Marshal(newUser.Groups)
if string(oldUserGroupsJson) != string(newUserGroupsJson) {
item := GetAccountItemByName("Groups", organization)
if item == nil {
newUser.Groups = oldUser.Groups
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Address == nil {
@@ -508,117 +404,65 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
newUserAddressJson, _ := json.Marshal(newUser.Address)
if string(oldUserAddressJson) != string(newUserAddressJson) {
item := GetAccountItemByName("Address", organization)
if item == nil {
newUser.Address = oldUser.Address
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if newUser.FaceIds != nil {
item := GetAccountItemByName("Face ID", organization)
if item == nil {
newUser.FaceIds = oldUser.FaceIds
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.IsAdmin != newUser.IsAdmin {
item := GetAccountItemByName("Is admin", organization)
if item == nil {
newUser.IsAdmin = oldUser.IsAdmin
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.IsForbidden != newUser.IsForbidden {
item := GetAccountItemByName("Is forbidden", organization)
if item == nil {
newUser.IsForbidden = oldUser.IsForbidden
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.IsDeleted != newUser.IsDeleted {
item := GetAccountItemByName("Is deleted", organization)
if item == nil {
newUser.IsDeleted = oldUser.IsDeleted
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.NeedUpdatePassword != newUser.NeedUpdatePassword {
item := GetAccountItemByName("Need update password", organization)
if item == nil {
newUser.NeedUpdatePassword = oldUser.NeedUpdatePassword
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Balance != newUser.Balance {
item := GetAccountItemByName("Balance", organization)
if item == nil {
newUser.Balance = oldUser.Balance
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Score != newUser.Score {
item := GetAccountItemByName("Score", organization)
if item == nil {
newUser.Score = oldUser.Score
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Karma != newUser.Karma {
item := GetAccountItemByName("Karma", organization)
if item == nil {
newUser.Karma = oldUser.Karma
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Language != newUser.Language {
item := GetAccountItemByName("Language", organization)
if item == nil {
newUser.Language = oldUser.Language
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Ranking != newUser.Ranking {
item := GetAccountItemByName("Ranking", organization)
if item == nil {
newUser.Ranking = oldUser.Ranking
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Currency != newUser.Currency {
item := GetAccountItemByName("Currency", organization)
if item == nil {
newUser.Currency = oldUser.Currency
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
if oldUser.Hash != newUser.Hash {
item := GetAccountItemByName("Hash", organization)
if item == nil {
newUser.Hash = oldUser.Hash
} else {
itemsChanged = append(itemsChanged, item)
}
itemsChanged = append(itemsChanged, item)
}
for _, accountItem := range itemsChanged {

View File

@@ -48,10 +48,6 @@ func CorsFilter(ctx *context.Context) {
originHostname := getHostname(origin)
host := removePort(ctx.Request.Host)
if origin == "null" {
origin = ""
}
if strings.HasPrefix(origin, "http://localhost") || strings.HasPrefix(origin, "https://localhost") || strings.HasPrefix(origin, "http://127.0.0.1") || strings.HasPrefix(origin, "http://casdoor-app") || strings.Contains(origin, ".chromiumapp.org") {
setCorsHeaders(ctx, origin)
return

View File

@@ -43,10 +43,6 @@ func getWebBuildFolder() string {
return path
}
if util.FileExist(filepath.Join(frontendBaseDir, "index.html")) {
return frontendBaseDir
}
path = filepath.Join(frontendBaseDir, "web/build")
return path
}

View File

@@ -703,16 +703,6 @@ class ApplicationEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}}>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("application:Use Email as NameID"), i18next.t("application:Use Email as NameID - Tooltip"))} :
</Col>
<Col span={1}>
<Switch checked={this.state.application.useEmailAsSamlNameId} onChange={checked => {
this.updateApplicationField("useEmailAsSamlNameId", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("application:Enable SAML POST binding"), i18next.t("application:Enable SAML POST binding - Tooltip"))} :

View File

@@ -1,97 +0,0 @@
// Copyright 2024 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, {useCallback, useEffect, useRef, useState} from "react";
import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css";
import "codemirror/mode/properties/properties";
import * as Setting from "./Setting";
import IframeEditor from "./IframeEditor";
import {Tabs} from "antd";
const {TabPane} = Tabs;
const CasbinEditor = ({model, onModelTextChange}) => {
const [activeKey, setActiveKey] = useState("advanced");
const iframeRef = useRef(null);
const [localModelText, setLocalModelText] = useState(model.modelText);
const handleModelTextChange = useCallback((newModelText) => {
if (!Setting.builtInObject(model)) {
setLocalModelText(newModelText);
onModelTextChange(newModelText);
}
}, [model, onModelTextChange]);
const syncModelText = useCallback(() => {
return new Promise((resolve) => {
if (activeKey === "advanced" && iframeRef.current) {
const handleSyncMessage = (event) => {
if (event.data.type === "modelUpdate") {
window.removeEventListener("message", handleSyncMessage);
handleModelTextChange(event.data.modelText);
resolve();
}
};
window.addEventListener("message", handleSyncMessage);
iframeRef.current.getModelText();
} else {
resolve();
}
});
}, [activeKey, handleModelTextChange]);
const handleTabChange = (key) => {
syncModelText().then(() => {
setActiveKey(key);
if (key === "advanced" && iframeRef.current) {
iframeRef.current.updateModelText(localModelText);
}
});
};
useEffect(() => {
setLocalModelText(model.modelText);
}, [model.modelText]);
return (
<div style={{height: "100%", width: "100%", display: "flex", flexDirection: "column"}}>
<Tabs activeKey={activeKey} onChange={handleTabChange} style={{flex: "0 0 auto", marginTop: "-10px"}}>
<TabPane tab="Basic Editor" key="basic" />
<TabPane tab="Advanced Editor" key="advanced" />
</Tabs>
<div style={{flex: "1 1 auto", overflow: "hidden"}}>
{activeKey === "advanced" ? (
<IframeEditor
ref={iframeRef}
initialModelText={localModelText}
onModelTextChange={handleModelTextChange}
style={{width: "100%", height: "100%"}}
/>
) : (
<CodeMirror
value={localModelText}
className="full-height-editor no-horizontal-scroll-editor"
options={{mode: "properties", theme: "default"}}
onBeforeChange={(editor, data, value) => {
handleModelTextChange(value);
}}
/>
)}
</div>
</div>
);
};
export default CasbinEditor;

View File

@@ -1,66 +0,0 @@
// Copyright 2024 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, {forwardRef, useEffect, useImperativeHandle, useRef, useState} from "react";
const IframeEditor = forwardRef(({initialModelText, onModelTextChange}, ref) => {
const iframeRef = useRef(null);
const [iframeReady, setIframeReady] = useState(false);
useEffect(() => {
const handleMessage = (event) => {
if (event.origin !== "https://editor.casbin.org") {return;}
if (event.data.type === "modelUpdate") {
onModelTextChange(event.data.modelText);
} else if (event.data.type === "iframeReady") {
setIframeReady(true);
iframeRef.current?.contentWindow.postMessage({
type: "initializeModel",
modelText: initialModelText,
}, "*");
}
};
window.addEventListener("message", handleMessage);
return () => window.removeEventListener("message", handleMessage);
}, [onModelTextChange, initialModelText]);
useImperativeHandle(ref, () => ({
getModelText: () => {
iframeRef.current?.contentWindow.postMessage({type: "getModelText"}, "*");
},
updateModelText: (newModelText) => {
if (iframeReady) {
iframeRef.current?.contentWindow.postMessage({
type: "updateModelText",
modelText: newModelText,
}, "*");
}
},
}));
return (
<iframe
ref={iframeRef}
src="https://editor.casbin.org/model-editor"
frameBorder="0"
width="100%"
height="500px"
title="Casbin Model Editor"
/>
);
});
export default IframeEditor;

View File

@@ -18,7 +18,11 @@ import * as ModelBackend from "./backend/ModelBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting";
import i18next from "i18next";
import ModelEditor from "./CasbinEditor";
import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css";
require("codemirror/mode/properties/properties");
const {Option} = Select;
@@ -143,10 +147,16 @@ class ModelEditPage extends React.Component {
{Setting.getLabel(i18next.t("model:Model text"), i18next.t("model:Model text - Tooltip"))} :
</Col>
<Col span={22}>
<div style={{position: "relative", height: "500px"}} >
<ModelEditor
model={this.state.model}
onModelTextChange={(value) => this.updateModelField("modelText", value)}
<div style={{width: "100%"}} >
<CodeMirror
value={this.state.model.modelText}
options={{mode: "properties", theme: "default"}}
onBeforeChange={(editor, data, value) => {
if (Setting.builtInObject(this.state.model)) {
return;
}
this.updateModelField("modelText", value);
}}
/>
</div>
</Col>

View File

@@ -434,9 +434,10 @@ class SyncerEditPage extends React.Component {
{Setting.getLabel(i18next.t("syncer:Table"), i18next.t("syncer:Table - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.syncer.table} onChange={e => {
this.updateSyncerField("table", e.target.value);
}} />
<Input value={this.state.syncer.table}
disabled={this.state.syncer.type === "Keycloak"} onChange={e => {
this.updateSyncerField("table", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >

View File

@@ -1050,13 +1050,6 @@ class UserEditPage extends React.Component {
<MfaAccountTable
title={i18next.t("user:MFA accounts")}
table={this.state.user.mfaAccounts}
qrUrl={
"casdoor-app://login/into?serverUrl=" + window.location.origin +
"&clientId=" + this.state.application.clientId +
"&organizationName=" + this.state.organizationName +
"&appName=" + this.state.user.signupApplication
}
icon={this.state.user.avatar}
onUpdateTable={(table) => {this.updateUserField("mfaAccounts", table);}}
/>
</Col>

View File

@@ -34,42 +34,25 @@ class CasLogout extends React.Component {
UNSAFE_componentWillMount() {
const params = new URLSearchParams(this.props.location.search);
const logoutInterval = 100;
const logoutTimeOut = (redirectUri) => {
setTimeout(() => {
AuthBackend.getAccount().then((accountRes) => {
if (accountRes.status === "ok") {
AuthBackend.logout().then((logoutRes) => {
if (logoutRes.status === "ok") {
logoutTimeOut(logoutRes.data2);
} else {
Setting.showMessage("error", `${i18next.t("login:Failed to log out")}: ${logoutRes.msg}`);
}
});
} else {
Setting.showMessage("success", i18next.t("application:Logged out successfully"));
this.props.onUpdateAccount(null);
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri);
} else if (params.has("service")) {
Setting.goToLink(params.get("service"));
} else {
Setting.goToLinkSoft(this, `/cas/${this.state.owner}/${this.state.applicationName}/login`);
}
}
});
}, logoutInterval);
};
AuthBackend.logout()
.then((res) => {
if (res.status === "ok") {
logoutTimeOut(res.data2);
Setting.showMessage("success", "Logged out successfully");
this.props.onUpdateAccount(null);
const redirectUri = res.data2;
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri);
} else if (params.has("service")) {
Setting.goToLink(params.get("service"));
} else {
Setting.goToLinkSoft(this, `/cas/${this.state.owner}/${this.state.applicationName}/login`);
}
} else {
Setting.showMessage("error", `${i18next.t("login:Failed to log out")}: ${res.msg}`);
Setting.showMessage("error", `Failed to log out: ${res.msg}`);
}
});
}
render() {

View File

@@ -938,7 +938,7 @@ class LoginPage extends React.Component {
signinItem.label ? Setting.renderSignupLink(application, signinItem.label) :
(
<React.Fragment>
{i18next.t("login:No account?")}&nbsp;
{i18next.t("login:No account?")}
{
Setting.renderSignupLink(application, i18next.t("login:sign up now"))
}

View File

@@ -51,19 +51,3 @@ code {
.custom-link:hover {
color: rgb(64 64 64) !important;
}
.full-height-editor {
height: 100%;
}
.full-height-editor [class*="CodeMirror"] {
height: 100%;
}
.no-horizontal-scroll-editor [class*="CodeMirror-hscrollbar"] {
display: none !important;
}
.no-horizontal-scroll-editor [class*="CodeMirror-scroll"] {
overflow-x: hidden !important;
}

View File

@@ -14,7 +14,7 @@
import React from "react";
import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Image, Input, Popover, QRCode, Row, Table, Tooltip} from "antd";
import {Button, Col, Image, Input, Row, Table, Tooltip} from "antd";
import * as Setting from "../Setting";
import i18next from "i18next";
@@ -23,8 +23,6 @@ class MfaAccountTable extends React.Component {
super(props);
this.state = {
classes: props,
qrUrl: this.props.qrUrl,
icon: this.props.icon,
mfaAccounts: this.props.table !== null ? this.props.table.map((item, index) => {
item.key = index;
return item;
@@ -160,15 +158,6 @@ class MfaAccountTable extends React.Component {
<div>
{this.props.title}&nbsp;&nbsp;&nbsp;&nbsp;
<Button style={{marginRight: "5px"}} type="primary" size="small" onClick={() => this.addRow(table)}>{i18next.t("general:Add")}</Button>
<Popover trigger="focus" content={
<QRCode
value={this.state.qrUrl}
icon={this.state.icon}
bordered={false}
/>
}>
<Button style={{marginLeft: "5px"}} type="primary" size="small">{i18next.t("general:QR Code")}</Button>
</Popover>
</div>
)}
/>

View File

@@ -65,7 +65,7 @@ class SignupTable extends React.Component {
}
addRow(table) {
const row = {name: Setting.getNewRowNameForTable(table, "Please select a signup item"), visible: true, required: true, options: [], rule: "None", customCss: ""};
const row = {name: Setting.getNewRowNameForTable(table, "Please select a signup item"), visible: true, required: true, rule: "None", customCss: ""};
if (table === undefined) {
table = [];
}
@@ -201,25 +201,6 @@ class SignupTable extends React.Component {
);
},
},
{
title: i18next.t("provider:Type"),
dataIndex: "type",
key: "type",
width: "160px",
render: (text, record, index) => {
const options = [
{id: "Input", name: i18next.t("application:Input")},
{id: "Single Choice", name: i18next.t("application:Single Choice")},
{id: "Multiple Choices", name: i18next.t("application:Multiple Choices")},
];
return (
<Select virtual={false} style={{width: "100%"}} value={text} onChange={(value => {
this.updateField(table, index, "type", value);
})} options={options.map(item => Setting.getOption(item.name, item.id))} />
);
},
},
{
title: i18next.t("signup:Label"),
dataIndex: "label",
@@ -280,7 +261,7 @@ class SignupTable extends React.Component {
title: i18next.t("signup:Placeholder"),
dataIndex: "placeholder",
key: "placeholder",
width: "110px",
width: "200px",
render: (text, record, index) => {
if (record.name.startsWith("Text ")) {
return null;
@@ -293,26 +274,6 @@ class SignupTable extends React.Component {
);
},
},
{
title: i18next.t("signup:Options"),
dataIndex: "options",
key: "options",
width: "180px",
render: (text, record, index) => {
if (record.type !== "Single Choice" && record.type !== "Multiple Choices") {
return null;
}
return (
<Select virtual={false} mode="tags" style={{width: "100%"}} value={text}
onChange={(value => {
this.updateField(table, index, "options", value);
})}
options={text?.map((option) => Setting.getOption(option, option))}
/>
);
},
},
{
title: i18next.t("signup:Regex"),
dataIndex: "regex",