Compare commits

...

8 Commits

Author SHA1 Message Date
Resulte Lee
8da50b7893 feat: extend managed accounts for get-account api (#1068)
* feat: add get-extend-account api

* feat: extend managed accounts for get-account api

* fix go-linter err

* Use GetApplicationsByOrganizationName
2022-08-30 00:57:27 +08:00
Gucheng Wang
2394c8e2b4 Make sure newStaticBaseUrl is not empty 2022-08-29 21:27:47 +08:00
Gucheng Wang
c62983d734 Use conf.GetConfigString() 2022-08-29 21:26:00 +08:00
conghuhu
5948782cdd fix: fix eslint error in webstorm (#1073) 2022-08-29 15:23:51 +08:00
conghuhu
674d1619dd fix: fix hot update error #1071 (#1072) 2022-08-29 13:45:31 +08:00
conghuhu
11b8b65ca0 feat: update antd and react to latest (#1069) 2022-08-28 23:14:04 +08:00
q1anx1
411d76798d fix: fix upload file security issue (#1063)
* fix: fix upload file security issue

* fix: fix
2022-08-25 11:34:09 +08:00
Yixiang Zhao
7b0b426a76 feat: check model grammar when saving and provide a ACL model as init data (#1062)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-08-24 17:21:05 +08:00
16 changed files with 3841 additions and 5583 deletions

View File

@@ -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) {

View File

@@ -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",

View File

@@ -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
}

View File

@@ -19,7 +19,7 @@ import (
"fmt"
"os"
"github.com/astaxie/beego"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
"github.com/duo-labs/webauthn/webauthn"
)
@@ -27,6 +27,7 @@ import (
func InitDb() {
existed := initBuiltInOrganization()
if !existed {
initBuiltInModel()
initBuiltInPermission()
initBuiltInProvider()
initBuiltInUser()
@@ -38,8 +39,6 @@ func InitDb() {
initWebAuthn()
}
var staticBaseUrl = beego.AppConfig.String("staticBaseUrl")
func initBuiltInOrganization() bool {
organization := getOrganization("admin", "built-in")
if organization != nil {
@@ -52,10 +51,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"},
@@ -105,7 +104,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{},
@@ -135,7 +134,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",
@@ -239,6 +238,33 @@ func initWebAuthn() {
gob.Register(webauthn.SessionData{})
}
func initBuiltInModel() {
model := GetModel("built-in/model-built-in")
if model != nil {
return
}
model = &Model{
Owner: "built-in",
Name: "model-built-in",
CreatedTime: util.GetCurrentTime(),
DisplayName: "Built-in Model",
IsEnabled: true,
ModelText: `[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act`,
}
AddModel(model)
}
func initBuiltInPermission() {
permission := GetPermission("built-in/permission-built-in")
if permission != nil {
@@ -253,6 +279,7 @@ func initBuiltInPermission() {
Users: []string{"built-in/admin"},
Roles: []string{},
Domains: []string{},
Model: "model-built-in",
ResourceType: "Application",
Resources: []string{"app-built-in"},
Actions: []string{"Read", "Write", "Admin"},

View File

@@ -17,6 +17,7 @@ package object
import (
"fmt"
"github.com/casbin/casbin/v2/model"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
)
@@ -85,13 +86,19 @@ func GetModel(id string) *Model {
return getModel(owner, name)
}
func UpdateModel(id string, model *Model) bool {
func UpdateModel(id string, modelObj *Model) bool {
owner, name := util.GetOwnerAndNameFromId(id)
if getModel(owner, name) == nil {
return false
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(model)
// check model grammar
_, err := model.NewModelFromString(modelObj.ModelText)
if err != nil {
panic(err)
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(modelObj)
if err != nil {
panic(err)
}

View File

@@ -28,8 +28,8 @@ import (
"time"
"github.com/RobotsAndPencils/go-saml"
"github.com/astaxie/beego"
"github.com/beevik/etree"
"github.com/casdoor/casdoor/conf"
"github.com/golang-jwt/jwt/v4"
dsig "github.com/russellhaering/goxmldsig"
uuid "github.com/satori/go.uuid"
@@ -181,7 +181,7 @@ func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, e
block, _ := pem.Decode([]byte(cert.Certificate))
certificate := base64.StdEncoding.EncodeToString(block.Bytes)
origin := beego.AppConfig.String("origin")
origin := conf.GetConfigString("origin")
originFrontend, originBackend := getOriginFromHost(host)
if origin != "" {
originBackend = origin

View File

@@ -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

View File

@@ -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,7 +27,7 @@ import (
func GetWebAuthnObject(host string) *webauthn.WebAuthn {
var err error
origin := beego.AppConfig.String("origin")
origin := conf.GetConfigString("origin")
if origin == "" {
_, origin = getOriginFromHost(host)
}
@@ -38,7 +38,7 @@ func GetWebAuthnObject(host string) *webauthn.WebAuthn {
}
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
// RPIcon: "https://duo.com/logo.png", // Optional icon URL for your site

View File

@@ -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) {

View File

@@ -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": {

17
web/babel.config.json Normal file
View 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"
}
]
]
}

View File

@@ -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": {
@@ -61,9 +61,12 @@
]
},
"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"
},

View File

@@ -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;

View File

@@ -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>)

View File

@@ -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.

File diff suppressed because it is too large Load Diff