Browse Source

Add "add permission" button

HEAD
Yang Luo 3 years ago
parent
commit
31c00a847b
17 changed files with 345 additions and 8 deletions
  1. +75
    -0
      casdoor/adapter.go
  2. +95
    -0
      casdoor/permission_adapter.go
  3. +2
    -0
      conf/app.conf
  4. +56
    -0
      controllers/permission.go
  5. +2
    -0
      main.go
  6. +1
    -1
      object/adapter.go
  7. +6
    -0
      routers/router.go
  8. +1
    -1
      storage/storage_test.go
  9. +7
    -0
      web/src/App.js
  10. +9
    -2
      web/src/FileTree.js
  11. +1
    -1
      web/src/FileTreePage.js
  12. +1
    -1
      web/src/HomePage.js
  13. +34
    -0
      web/src/PermissionUtil.js
  14. +1
    -1
      web/src/StoreEditPage.js
  15. +49
    -0
      web/src/backend/PermissionBackend.js
  16. +2
    -0
      web/src/locales/en/data.json
  17. +3
    -1
      web/src/locales/zh/data.json

+ 75
- 0
casdoor/adapter.go View File

@@ -0,0 +1,75 @@
package casdoor

import (
"runtime"

"github.com/astaxie/beego"
_ "github.com/go-sql-driver/mysql"
"xorm.io/xorm"
)

var adapter *Adapter = nil
var CasdoorOrganization string

type Session struct {
SessionKey string `xorm:"char(64) notnull pk"`
SessionData []uint8 `xorm:"blob"`
SessionExpiry int `xorm:"notnull"`
}

func InitCasdoorAdapter() {
casdoorDbName := beego.AppConfig.String("casdoorDbName")
if casdoorDbName == "" {
return
}

adapter = NewAdapter(beego.AppConfig.String("driverName"), beego.AppConfig.String("dataSourceName"), beego.AppConfig.String("casdoorDbName"))

CasdoorOrganization = beego.AppConfig.String("casdoorOrganization")
}

// Adapter represents the MySQL adapter for policy storage.
type Adapter struct {
driverName string
dataSourceName string
dbName string
Engine *xorm.Engine
}

// finalizer is the destructor for Adapter.
func finalizer(a *Adapter) {
err := a.Engine.Close()
if err != nil {
panic(err)
}
}

// NewAdapter is the constructor for Adapter.
func NewAdapter(driverName string, dataSourceName string, dbName string) *Adapter {
a := &Adapter{}
a.driverName = driverName
a.dataSourceName = dataSourceName
a.dbName = dbName

// Open the DB, create it if not existed.
a.open()

// Call the destructor when the object is released.
runtime.SetFinalizer(a, finalizer)

return a
}

func (a *Adapter) open() {
Engine, err := xorm.NewEngine(a.driverName, a.dataSourceName+a.dbName)
if err != nil {
panic(err)
}

a.Engine = Engine
}

func (a *Adapter) close() {
a.Engine.Close()
a.Engine = nil
}

+ 95
- 0
casdoor/permission_adapter.go View File

@@ -0,0 +1,95 @@
package casdoor

import (
"github.com/casbin/casbase/util"
"xorm.io/core"
)

type Permission struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`

Users []string `xorm:"mediumtext" json:"users"`
Roles []string `xorm:"mediumtext" json:"roles"`
Domains []string `xorm:"mediumtext" json:"domains"`

Model string `xorm:"varchar(100)" json:"model"`
ResourceType string `xorm:"varchar(100)" json:"resourceType"`
Resources []string `xorm:"mediumtext" json:"resources"`
Actions []string `xorm:"mediumtext" json:"actions"`
Effect string `xorm:"varchar(100)" json:"effect"`
IsEnabled bool `json:"isEnabled"`

Submitter string `xorm:"varchar(100)" json:"submitter"`
Approver string `xorm:"varchar(100)" json:"approver"`
ApproveTime string `xorm:"varchar(100)" json:"approveTime"`
State string `xorm:"varchar(100)" json:"state"`
}

func GetPermissions(owner string) []*Permission {
permissions := []*Permission{}
err := adapter.Engine.Desc("created_time").Find(&permissions, &Permission{Owner: owner})
if err != nil {
panic(err)
}

return permissions
}

func getPermission(owner string, name string) *Permission {
if owner == "" || name == "" {
return nil
}

permission := Permission{Owner: owner, Name: name}
existed, err := adapter.Engine.Get(&permission)
if err != nil {
panic(err)
}

if existed {
return &permission
} else {
return nil
}
}

func GetPermission(id string) *Permission {
owner, name := util.GetOwnerAndNameFromId(id)
return getPermission(owner, name)
}

func UpdatePermission(id string, permission *Permission) bool {
owner, name := util.GetOwnerAndNameFromId(id)
oldPermission := getPermission(owner, name)
if oldPermission == nil {
return false
}

affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(permission)
if err != nil {
panic(err)
}

return affected != 0
}

func AddPermission(permission *Permission) bool {
affected, err := adapter.Engine.Insert(permission)
if err != nil {
panic(err)
}

return affected != 0
}

func DeletePermission(permission *Permission) bool {
affected, err := adapter.Engine.ID(core.PK{permission.Owner, permission.Name}).Delete(&Permission{})
if err != nil {
panic(err)
}

return affected != 0
}

+ 2
- 0
conf/app.conf View File

@@ -3,6 +3,7 @@ httpport = 14000
runmode = dev
SessionOn = true
copyrequestbody = true
driverName = mysql
dataSourceName = root:123@tcp(localhost:3306)/
dbName = casbase
redisEndpoint =
@@ -10,5 +11,6 @@ landingFolder = casbase-landing
casdoorEndpoint = http://localhost:8000
clientId = af6b5aa958822fb9dc33
clientSecret = 8bc3010c1c951c8d876b1f311a901ff8deeb93bc
casdoorDbName = casdoor
casdoorOrganization = "casbin"
casdoorApplication = "app-casbase"

+ 56
- 0
controllers/permission.go View File

@@ -0,0 +1,56 @@
package controllers

import (
"encoding/json"

"github.com/casbin/casbase/casdoor"
)

func (c *ApiController) GetPermissions() {
owner := c.Input().Get("owner")

c.Data["json"] = casdoor.GetPermissions(owner)
c.ServeJSON()
}

func (c *ApiController) GetPermission() {
id := c.Input().Get("id")

c.Data["json"] = casdoor.GetPermission(id)
c.ServeJSON()
}

func (c *ApiController) UpdatePermission() {
id := c.Input().Get("id")

var permission casdoor.Permission
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permission)
if err != nil {
panic(err)
}

c.Data["json"] = casdoor.UpdatePermission(id, &permission)
c.ServeJSON()
}

func (c *ApiController) AddPermission() {
var permission casdoor.Permission
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permission)
if err != nil {
panic(err)
}

c.Data["json"] = casdoor.AddPermission(&permission)
c.ServeJSON()
}

func (c *ApiController) DeletePermission() {
var permission casdoor.Permission
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permission)
if err != nil {
panic(err)
}

c.Data["json"] = casdoor.DeletePermission(&permission)
c.ServeJSON()
}

+ 2
- 0
main.go View File

@@ -4,12 +4,14 @@ import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/plugins/cors"
_ "github.com/astaxie/beego/session/redis"
"github.com/casbin/casbase/casdoor"
"github.com/casbin/casbase/object"
"github.com/casbin/casbase/routers"
)

func main() {
object.InitAdapter()
casdoor.InitCasdoorAdapter()

beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
AllowOrigins: []string{"*"},


+ 1
- 1
object/adapter.go View File

@@ -21,7 +21,7 @@ func InitConfig() {
}

func InitAdapter() {
adapter = NewAdapter("mysql", beego.AppConfig.String("dataSourceName"))
adapter = NewAdapter(beego.AppConfig.String("driverName"), beego.AppConfig.String("dataSourceName"))
}

// Adapter represents the MySQL adapter for policy storage.


+ 6
- 0
routers/router.go View File

@@ -55,4 +55,10 @@ func initAPI() {
beego.Router("/api/update-file", &controllers.ApiController{}, "POST:UpdateFile")
beego.Router("/api/add-file", &controllers.ApiController{}, "POST:AddFile")
beego.Router("/api/delete-file", &controllers.ApiController{}, "POST:DeleteFile")

beego.Router("/api/get-permissions", &controllers.ApiController{}, "GET:GetPermissions")
beego.Router("/api/get-permission", &controllers.ApiController{}, "GET:GetPermission")
beego.Router("/api/update-permission", &controllers.ApiController{}, "POST:UpdatePermission")
beego.Router("/api/add-permission", &controllers.ApiController{}, "POST:AddPermission")
beego.Router("/api/delete-permission", &controllers.ApiController{}, "POST:DeletePermission")
}

+ 1
- 1
storage/storage_test.go View File

@@ -3,5 +3,5 @@ package storage
import "testing"

func TestStorage(t *testing.T) {
ListObjects("casbase")
ListObjects("casbase", "")
}

+ 7
- 0
web/src/App.js View File

@@ -228,6 +228,13 @@ class App extends Component {
</Link>
</Menu.Item>
);
res.push(
<Menu.Item key="/resources">
<a target="_blank" rel="noreferrer" href={Setting.getMyProfileUrl(this.state.account).replace("/account", "/permissions")}>
{i18next.t("general:Permissions")}
</a>
</Menu.Item>
);
// res.push(
// <Menu.Item key="/clustering">
// <Link to="/clustering">


+ 9
- 2
web/src/FileTree.js View File

@@ -1,11 +1,12 @@
import React from "react";
import {Button, Col, Empty, Input, Popconfirm, Row, Spin, Tooltip, Tree, Upload} from 'antd';
import {CloudUploadOutlined, createFromIconfontCN, DeleteOutlined, DownloadOutlined, FolderAddOutlined} from "@ant-design/icons";
import {CloudUploadOutlined, createFromIconfontCN, DeleteOutlined, DownloadOutlined, FileDoneOutlined, FolderAddOutlined} from "@ant-design/icons";
import * as Setting from "./Setting";
import * as FileBackend from "./backend/FileBackend";
import DocViewer, { DocViewerRenderers } from "react-doc-viewer";
import FileViewer from 'react-file-viewer';
import i18next from "i18next";
import * as PermissionUtil from "./PermissionUtil";

import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css";
@@ -286,10 +287,16 @@ class FileTree extends React.Component {
okText="OK"
cancelText="Cancel"
>
<Button icon={<DeleteOutlined />} size="small" />
<Button style={{marginRight: "5px"}} icon={<DeleteOutlined />} size="small" />
</Popconfirm>
</span>
</Tooltip>
<Tooltip title={i18next.t("store:Add Permission")}>
<Button icon={<FileDoneOutlined />} size="small" onClick={(e) => {
PermissionUtil.addPermission(this.props.account, this.props.store, file);
e.stopPropagation();
}} />
</Tooltip>
</div>
}>
{`${file.title} (${Setting.getFriendlyFileSize(file.size)})`}


+ 1
- 1
web/src/FileTreePage.js View File

@@ -37,7 +37,7 @@ class FileTreePage extends React.Component {
}
return (
<FileTree store={this.state.store} />
<FileTree account={this.props.account} store={this.state.store} />
);
}
}


+ 1
- 1
web/src/HomePage.js View File

@@ -36,7 +36,7 @@ class HomePage extends React.Component {
}
return (
<FileTree store={this.state.store} />
<FileTree account={this.props.account} store={this.state.store} />
);
}
}


+ 34
- 0
web/src/PermissionUtil.js View File

@@ -0,0 +1,34 @@
import * as PermissionBackend from "./backend/PermissionBackend";
import * as Setting from "./Setting";
import moment from "moment";
export function addPermission(account, store, file) {
const randomName = Setting.getRandomName();
const newPermission = {
owner: account.owner,
name: `permission_${randomName}`,
createdTime: moment().format(),
displayName: `New Permission - ${randomName}`,
users: [],
roles: [],
domains: [store.name],
model: "Default",
resourceType: "TreeNode",
resources: [file.key],
actions: ["Read"],
effect: "Allow",
isEnabled: true,
submitter: account.name,
approver: "",
approveTime: "",
state: "Pending",
};
PermissionBackend.addPermission(newPermission)
.then((res) => {
Setting.openLink(Setting.getMyProfileUrl(account).replace("/account", `/permissions/${newPermission.owner}/${newPermission.name}`));
}
)
.catch(error => {
Setting.showMessage("error", `Permission failed to add: ${error}`);
});
}

+ 1
- 1
web/src/StoreEditPage.js View File

@@ -99,7 +99,7 @@ class StoreEditPage extends React.Component {
{i18next.t("store:File tree")}:
</Col>
<Col span={22} >
<FileTree store={this.state.store} />
<FileTree account={this.props.account} store={this.state.store} />
</Col>
</Row>
</Card>


+ 49
- 0
web/src/backend/PermissionBackend.js View File

@@ -0,0 +1,49 @@
import * as Setting from "../Setting";
export function getGlobalPermissions() {
return fetch(`${Setting.ServerUrl}/api/get-global-permissions`, {
method: "GET",
credentials: "include"
}).then(res => res.json());
}
export function getPermissions(owner) {
return fetch(`${Setting.ServerUrl}/api/get-permissions?owner=${owner}`, {
method: "GET",
credentials: "include"
}).then(res => res.json());
}
export function getPermission(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-permission?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET",
credentials: "include"
}).then(res => res.json());
}
export function updatePermission(owner, name, permission) {
let newPermission = Setting.deepCopy(permission);
return fetch(`${Setting.ServerUrl}/api/update-permission?id=${owner}/${encodeURIComponent(name)}`, {
method: 'POST',
credentials: 'include',
body: JSON.stringify(newPermission),
}).then(res => res.json());
}
export function addPermission(permission) {
let newPermission = Setting.deepCopy(permission);
return fetch(`${Setting.ServerUrl}/api/add-permission`, {
method: 'POST',
credentials: 'include',
body: JSON.stringify(newPermission),
}).then(res => res.json());
}
export function deletePermission(permission) {
let newPermission = Setting.deepCopy(permission);
return fetch(`${Setting.ServerUrl}/api/delete-permission`, {
method: 'POST',
credentials: 'include',
body: JSON.stringify(newPermission),
}).then(res => res.json());
}

+ 2
- 0
web/src/locales/en/data.json View File

@@ -18,6 +18,7 @@
"Loading...": "Loading...",
"Name": "Name",
"No.": "No.",
"Permissions": "Permissions",
"Preview": "Preview",
"Result": "Result",
"Save": "Save",
@@ -29,6 +30,7 @@
"Wordsets": "Wordsets"
},
"store": {
"Add Permission": "Add Permission",
"Bucket": "Bucket",
"Delete": "Delete",
"Domain": "Domain",


+ 3
- 1
web/src/locales/zh/data.json View File

@@ -18,6 +18,7 @@
"Loading...": "加载中...",
"Name": "名称",
"No.": "序号",
"Permissions": "我的权限",
"Preview": "预览",
"Result": "结果",
"Save": "保存",
@@ -29,6 +30,7 @@
"Wordsets": "我的词汇集"
},
"store": {
"Add Permission": "添加权限",
"Bucket": "Bucket",
"Delete": "删除",
"Domain": "域名",
@@ -71,4 +73,4 @@
"Vectorset": "向量集",
"Words": "词汇表"
}
}
}

Loading…
Cancel
Save