| @@ -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 | |||
| } | |||
| @@ -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 | |||
| } | |||
| @@ -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" | |||
| @@ -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() | |||
| } | |||
| @@ -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{"*"}, | |||
| @@ -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. | |||
| @@ -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") | |||
| } | |||
| @@ -3,5 +3,5 @@ package storage | |||
| import "testing" | |||
| func TestStorage(t *testing.T) { | |||
| ListObjects("casbase") | |||
| ListObjects("casbase", "") | |||
| } | |||
| @@ -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"> | |||
| @@ -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)})`} | |||
| @@ -37,7 +37,7 @@ class FileTreePage extends React.Component { | |||
| } | |||
| return ( | |||
| <FileTree store={this.state.store} /> | |||
| <FileTree account={this.props.account} store={this.state.store} /> | |||
| ); | |||
| } | |||
| } | |||
| @@ -36,7 +36,7 @@ class HomePage extends React.Component { | |||
| } | |||
| return ( | |||
| <FileTree store={this.state.store} /> | |||
| <FileTree account={this.props.account} store={this.state.store} /> | |||
| ); | |||
| } | |||
| } | |||
| @@ -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}`); | |||
| }); | |||
| } | |||
| @@ -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> | |||
| @@ -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()); | |||
| } | |||
| @@ -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", | |||
| @@ -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": "词汇表" | |||
| } | |||
| } | |||
| } | |||