Compare commits

..

10 Commits

Author SHA1 Message Date
Baihhh
5e4ba4f338 feat: add authorize button and defaultValue (#2152)
Signed-off-by: baihhh <2542274498@qq.com>
2023-07-27 23:55:35 +08:00
Yang Luo
ca47af2ee1 Make post_logout_redirect_uri optional for logout 2023-07-27 23:26:30 +08:00
Ilya Sulimanov
59da104463 fix: update ldap admin pwd only if changed (#2146)
* fix ldap pwd update

* fix: linter

* fix: simplify check
2023-07-27 17:49:15 +08:00
Yaodong Yu
c5bb916651 fix: fix response data in PricingPage.js (#2143) 2023-07-27 10:46:31 +08:00
WintBit
e98264f957 fix: application fails to call /api/get-resources (#2139)
just like other apis, resource.go.GetResources() no longer calls ApiController.RequireSignedInUser() to auth or check
2023-07-26 17:19:00 +08:00
June
6a952952a8 fix: unmask application for org admin (#2138)
* feat: unmask application with user admin

* Update application.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-07-26 17:17:49 +08:00
Yang Luo
ba8a0f36be Support custom actions in permission edit page 2023-07-26 14:49:45 +08:00
June
b5e9084e5d feat: en/decodeURI in permission/role name (#2137) 2023-07-26 13:08:35 +08:00
June
55d5ae10f2 fix: fix infinite loop in containsRole() (#2136) 2023-07-25 20:53:08 +08:00
Yang Luo
6986dad295 Use arg to control createDatabaseForPostgres() 2023-07-25 18:36:15 +08:00
20 changed files with 180 additions and 88 deletions

View File

@@ -290,10 +290,11 @@ func (c *ApiController) Logout() {
c.ResponseOk(user, application.HomepageUrl)
return
} else {
if redirectUri == "" {
c.ResponseError(c.T("general:Missing parameter") + ": post_logout_redirect_uri")
return
}
// "post_logout_redirect_uri" has been made optional, see: https://github.com/casdoor/casdoor/issues/2151
// if redirectUri == "" {
// c.ResponseError(c.T("general:Missing parameter") + ": post_logout_redirect_uri")
// return
// }
if accessToken == "" {
c.ResponseError(c.T("general:Missing parameter") + ": id_token_hint")
return

View File

@@ -52,14 +52,6 @@ func (c *ApiController) GetResources() {
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
userObj, ok := c.RequireSignedInUser()
if !ok {
return
}
if userObj.IsAdmin {
user = ""
}
if limit == "" || page == "" {
resources, err := object.GetResources(owner, user)
if err != nil {

View File

@@ -26,9 +26,10 @@ import (
// @Title GetWebhooks
// @Tag Webhook API
// @Description get webhooks
// @Param owner query string true "The owner of webhooks"
// @Param owner query string built-in/admin true "The owner of webhooks"
// @Success 200 {array} object.Webhook The Response object
// @router /get-webhooks [get]
// @Security test_apiKey
func (c *ApiController) GetWebhooks() {
owner := c.Input().Get("owner")
limit := c.Input().Get("pageSize")
@@ -71,7 +72,7 @@ func (c *ApiController) GetWebhooks() {
// @Title GetWebhook
// @Tag Webhook API
// @Description get webhook
// @Param id query string true "The id ( owner/name ) of the webhook"
// @Param id query string built-in/admin true "The id ( owner/name ) of the webhook"
// @Success 200 {object} object.Webhook The Response object
// @router /get-webhook [get]
func (c *ApiController) GetWebhook() {
@@ -90,7 +91,7 @@ func (c *ApiController) GetWebhook() {
// @Title UpdateWebhook
// @Tag Webhook API
// @Description update webhook
// @Param id query string true "The id ( owner/name ) of the webhook"
// @Param id query string built-in/admin true "The id ( owner/name ) of the webhook"
// @Param body body object.Webhook true "The details of the webhook"
// @Success 200 {object} controllers.Response The Response object
// @router /update-webhook [post]

View File

@@ -39,7 +39,7 @@ func getCreateDatabaseFlag() bool {
func main() {
createDatabase := getCreateDatabaseFlag()
object.InitAdapter()
object.InitAdapter(createDatabase)
object.CreateTables(createDatabase)
object.DoMigration()

View File

@@ -42,15 +42,17 @@ func InitConfig() {
beego.BConfig.WebConfig.Session.SessionOn = true
InitAdapter()
InitAdapter(true)
CreateTables(true)
DoMigration()
}
func InitAdapter() {
err := createDatabaseForPostgres(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))
if err != nil {
panic(err)
func InitAdapter(createDatabase bool) {
if createDatabase {
err := createDatabaseForPostgres(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))
if err != nil {
panic(err)
}
}
adapter = NewAdapter(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))

View File

@@ -281,14 +281,21 @@ func GetApplication(id string) (*Application, error) {
}
func GetMaskedApplication(application *Application, userId string) *Application {
if isUserIdGlobalAdmin(userId) {
return application
}
if application == nil {
return nil
}
if userId != "" {
if isUserIdGlobalAdmin(userId) {
return application
}
user, _ := GetUser(userId)
if user != nil && user.IsApplicationAdmin(application) {
return application
}
}
if application.ClientSecret != "" {
application.ClientSecret = "***"
}

View File

@@ -135,12 +135,18 @@ func GetMaskedLdaps(ldaps []*Ldap, errs ...error) ([]*Ldap, error) {
}
func UpdateLdap(ldap *Ldap) (bool, error) {
if l, err := GetLdap(ldap.Id); err != nil {
var l *Ldap
var err error
if l, err = GetLdap(ldap.Id); err != nil {
return false, nil
} else if l == nil {
return false, nil
}
if ldap.Password == "***" {
ldap.Password = l.Password
}
affected, err := adapter.Engine.ID(ldap.Id).Cols("owner", "server_name", "host",
"port", "enable_ssl", "username", "password", "base_dn", "filter", "filter_fields", "auto_sync").Update(ldap)
if err != nil {

View File

@@ -112,7 +112,7 @@ func getPermission(owner string, name string) (*Permission, error) {
}
func GetPermission(id string) (*Permission, error) {
owner, name := util.GetOwnerAndNameFromId(id)
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
return getPermission(owner, name)
}
@@ -149,7 +149,7 @@ func UpdatePermission(id string, permission *Permission) (bool, error) {
return false, err
}
owner, name := util.GetOwnerAndNameFromId(id)
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
oldPermission, err := getPermission(owner, name)
if oldPermission == nil {
return false, nil

View File

@@ -82,12 +82,12 @@ func getRole(owner string, name string) (*Role, error) {
}
func GetRole(id string) (*Role, error) {
owner, name := util.GetOwnerAndNameFromId(id)
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
return getRole(owner, name)
}
func UpdateRole(id string, role *Role) (bool, error) {
owner, name := util.GetOwnerAndNameFromId(id)
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
oldRole, err := getRole(owner, name)
if err != nil {
return false, err
@@ -391,10 +391,13 @@ func GetAncestorRoles(roleIds ...string) ([]*Role, error) {
// containsRole is a helper function to check if a roles is related to any role in the given list roles
func containsRole(role *Role, roleMap map[string]*Role, visited map[string]bool, roleIds ...string) bool {
if isContain, ok := visited[role.GetId()]; ok {
roleId := role.GetId()
if isContain, ok := visited[roleId]; ok {
return isContain
}
visited[role.GetId()] = false
for _, subRole := range role.Roles {
if util.HasString(roleIds, subRole) {
return true

View File

@@ -860,3 +860,11 @@ func AddUserkeys(user *User, isAdmin bool) (bool, error) {
return UpdateUser(user.GetId(), user, []string{}, isAdmin)
}
func (user *User) IsApplicationAdmin(application *Application) bool {
if user == nil {
return false
}
return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin
}

View File

@@ -69,7 +69,7 @@ func getObject(ctx *context.Context) (string, string) {
// query == "?id=built-in/admin"
id := ctx.Input.Query("id")
if id != "" {
return util.GetOwnerAndNameFromId(id)
return util.GetOwnerAndNameFromIdNoCheck(id)
}
owner := ctx.Input.Query("owner")

View File

@@ -17,6 +17,10 @@
// @Title Casdoor API
// @Description Documentation of Casdoor API
// @Contact admin@casbin.org
// @SecurityDefinition test_apiKey apiKey Authorization header
// @Schemes http,https
// @ExternalDocs Find out more about casdoor
// @ExternalDocsUrl https://casdoor.org/
package routers
import (

View File

@@ -9,6 +9,10 @@
}
},
"basePath": "/",
"schemes": [
"http",
"https"
],
"paths": {
"/.well-known/jwks": {
"get": {
@@ -3682,7 +3686,8 @@
"name": "id",
"description": "The id ( owner/name ) of the webhook",
"required": true,
"type": "string"
"type": "string",
"default": "built-in/admin"
}
],
"responses": {
@@ -3708,7 +3713,8 @@
"name": "owner",
"description": "The owner of webhooks",
"required": true,
"type": "string"
"type": "string",
"default": "built-in/admin"
}
],
"responses": {
@@ -3721,7 +3727,12 @@
}
}
}
}
},
"security": [
{
"test_apiKey": []
}
]
}
},
"/api/health": {
@@ -5170,7 +5181,8 @@
"name": "id",
"description": "The id ( owner/name ) of the webhook",
"required": true,
"type": "string"
"type": "string",
"default": "built-in/admin"
},
{
"in": "body",
@@ -5419,14 +5431,6 @@
}
},
"definitions": {
"1225.0xc000333110.false": {
"title": "false",
"type": "object"
},
"1260.0xc000333140.false": {
"title": "false",
"type": "object"
},
"LaravelResponse": {
"title": "LaravelResponse",
"type": "object"
@@ -5480,10 +5484,16 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/1225.0xc000333110.false"
"additionalProperties": {
"description": "support string | class | List\u003cclass\u003e and os on",
"type": "string"
}
},
"data2": {
"$ref": "#/definitions/1260.0xc000333140.false"
"additionalProperties": {
"description": "support string | class | List\u003cclass\u003e and os on",
"type": "string"
}
},
"msg": {
"type": "string"
@@ -5525,10 +5535,6 @@
"title": "object",
"type": "object"
},
"object.\u0026{197582 0xc000ace360 false}": {
"title": "\u0026{197582 0xc000ace360 false}",
"type": "object"
},
"object.AccountItem": {
"title": "AccountItem",
"type": "object",
@@ -5722,7 +5728,7 @@
"title": "CasbinRequest",
"type": "array",
"items": {
"$ref": "#/definitions/object.\u0026{197582 0xc000ace360 false}"
"$ref": "#/definitions/object.CasbinRequest"
}
},
"object.Cert": {
@@ -7765,15 +7771,21 @@
"type": "object",
"properties": {
"contentType": {
"type": "string"
"default": "application/json",
"type": "string",
"example": "application/json"
},
"createdTime": {
"type": "string"
"default": "2023-07-27T17:09:12+08:00",
"type": "string",
"example": "2023-07-27T17:09:12+08:00"
},
"events": {
"type": "array",
"items": {
"type": "string"
"type": "string",
"default": "test",
"example": "test"
}
},
"headers": {
@@ -7783,25 +7795,39 @@
}
},
"isEnabled": {
"type": "boolean"
"default": true,
"type": "boolean",
"example": true
},
"isUserExtended": {
"type": "boolean"
"default": true,
"type": "boolean",
"example": true
},
"method": {
"type": "string"
"default": "POST",
"type": "string",
"example": "POST"
},
"name": {
"type": "string"
"default": "test",
"type": "string",
"example": "test"
},
"organization": {
"type": "string"
"default": "built-in",
"type": "string",
"example": "built-in"
},
"owner": {
"type": "string"
"default": "built-in",
"type": "string",
"example": "built-in"
},
"url": {
"type": "string"
"default": "https://example.com/callback",
"type": "string",
"example": "https://example.com/callback"
}
}
},
@@ -7866,5 +7892,16 @@
"title": "Engine",
"type": "object"
}
},
"securityDefinitions": {
"test_apiKey": {
"type": "apiKey",
"name": "Authorization",
"in": "header"
}
},
"externalDocs": {
"description": "Find out more about casdoor",
"url": "https://casdoor.org/"
}
}

View File

@@ -6,6 +6,9 @@ info:
contact:
email: admin@casbin.org
basePath: /
schemes:
- http
- https
paths:
/.well-known/jwks:
get:
@@ -2401,6 +2404,7 @@ paths:
description: The id ( owner/name ) of the webhook
required: true
type: string
default: built-in/admin
responses:
"200":
description: The Response object
@@ -2418,6 +2422,7 @@ paths:
description: The owner of webhooks
required: true
type: string
default: built-in/admin
responses:
"200":
description: The Response object
@@ -2425,6 +2430,8 @@ paths:
type: array
items:
$ref: '#/definitions/object.Webhook'
security:
- test_apiKey: []
/api/health:
get:
tags:
@@ -3381,6 +3388,7 @@ paths:
description: The id ( owner/name ) of the webhook
required: true
type: string
default: built-in/admin
- in: body
name: body
description: The details of the webhook
@@ -3541,12 +3549,6 @@ paths:
schema:
$ref: '#/definitions/controllers.Response'
definitions:
1225.0xc000333110.false:
title: "false"
type: object
1260.0xc000333140.false:
title: "false"
type: object
LaravelResponse:
title: LaravelResponse
type: object
@@ -3586,9 +3588,13 @@ definitions:
type: object
properties:
data:
$ref: '#/definitions/1225.0xc000333110.false'
additionalProperties:
description: support string | class | List<class> and os on
type: string
data2:
$ref: '#/definitions/1260.0xc000333140.false'
additionalProperties:
description: support string | class | List<class> and os on
type: string
msg:
type: string
name:
@@ -3615,9 +3621,6 @@ definitions:
object:
title: object
type: object
object.&{197582 0xc000ace360 false}:
title: '&{197582 0xc000ace360 false}'
type: object
object.AccountItem:
title: AccountItem
type: object
@@ -3749,7 +3752,7 @@ definitions:
title: CasbinRequest
type: array
items:
$ref: '#/definitions/object.&{197582 0xc000ace360 false}'
$ref: '#/definitions/object.CasbinRequest'
object.Cert:
title: Cert
type: object
@@ -5123,31 +5126,51 @@ definitions:
type: object
properties:
contentType:
default: application/json
type: string
example: application/json
createdTime:
default: "2023-07-27T17:09:12+08:00"
type: string
example: "2023-07-27T17:09:12+08:00"
events:
default: '[ddd]'
type: array
items:
type: string
headers:
default: '[]'
type: array
items:
$ref: '#/definitions/object.Header'
isEnabled:
default: true
type: boolean
example: true
isUserExtended:
default: true
type: boolean
example: true
method:
default: POST
type: string
example: POST
name:
default: test
type: string
example: test
organization:
default: built-in
type: string
example: built-in
owner:
default: built-in
type: string
example: built-in
url:
default: https://example.com/callback
type: string
example: https://example.com/callback
protocol.CredentialAssertion:
title: CredentialAssertion
type: object
@@ -5192,3 +5215,11 @@ definitions:
xorm.Engine:
title: Engine
type: object
securityDefinitions:
test_apiKey:
type: apiKey
name: Authorization
in: header
externalDocs:
description: Find out more about casdoor
url: https://casdoor.org/

View File

@@ -30,7 +30,7 @@ class PermissionEditPage extends React.Component {
this.state = {
classes: props,
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
permissionName: props.match.params.permissionName,
permissionName: decodeURIComponent(props.match.params.permissionName),
permission: null,
organizations: [],
model: null,
@@ -320,7 +320,7 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("permission:Actions"), i18next.t("permission:Actions - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.permission.actions} onChange={(value => {
<Select virtual={false} mode={(this.state.permission.resourceType === "Custom") ? "tags" : "multiple"} style={{width: "100%"}} value={this.state.permission.actions} onChange={(value => {
this.updatePermissionField("actions", value);
})}
options={[
@@ -449,7 +449,7 @@ class PermissionEditPage extends React.Component {
if (willExist) {
this.props.history.push("/permissions");
} else {
this.props.history.push(`/permissions/${this.state.permission.owner}/${this.state.permission.name}`);
this.props.history.push(`/permissions/${this.state.permission.owner}/${encodeURIComponent(this.state.permission.name)}`);
}
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);

View File

@@ -128,7 +128,7 @@ class PermissionListPage extends BaseListPage {
...this.getColumnSearchProps("name"),
render: (text, record, index) => {
return (
<Link to={`/permissions/${record.owner}/${text}`}>
<Link to={`/permissions/${record.owner}/${encodeURIComponent(text)}`}>
{text}
</Link>
);
@@ -336,7 +336,7 @@ class PermissionListPage extends BaseListPage {
render: (text, record, index) => {
return (
<div>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/permissions/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/permissions/${record.owner}/${encodeURIComponent(record.name)}`)}>{i18next.t("general:Edit")}</Button>
<PopconfirmModal
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
onConfirm={() => this.deletePermission(index)}

View File

@@ -148,7 +148,7 @@ class PlanListPage extends BaseListPage {
...this.getColumnSearchProps("role"),
render: (text, record, index) => {
return (
<Link to={`/roles/${text}`}>
<Link to={`/roles/${encodeURIComponent(text)}`}>
{text}
</Link>
);

View File

@@ -26,7 +26,7 @@ class RoleEditPage extends React.Component {
this.state = {
classes: props,
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
roleName: props.match.params.roleName,
roleName: decodeURIComponent(props.match.params.roleName),
role: null,
organizations: [],
users: [],
@@ -225,7 +225,7 @@ class RoleEditPage extends React.Component {
if (willExist) {
this.props.history.push("/roles");
} else {
this.props.history.push(`/roles/${this.state.role.owner}/${this.state.role.name}`);
this.props.history.push(`/roles/${this.state.role.owner}/${encodeURIComponent(this.state.role.name)}`);
}
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);

View File

@@ -121,7 +121,7 @@ class RoleListPage extends BaseListPage {
...this.getColumnSearchProps("name"),
render: (text, record, index) => {
return (
<Link to={`/roles/${record.owner}/${record.name}`}>
<Link to={`/roles/${record.owner}/${encodeURIComponent(record.name)}`}>
{text}
</Link>
);
@@ -213,7 +213,7 @@ class RoleListPage extends BaseListPage {
render: (text, record, index) => {
return (
<div>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/roles/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/roles/${record.owner}/${encodeURIComponent(record.name)}`)}>{i18next.t("general:Edit")}</Button>
<PopconfirmModal
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
onConfirm={() => this.deleteRole(index)}

View File

@@ -85,17 +85,17 @@ class PricingPage extends React.Component {
}
PricingBackend.getPricing(this.state.owner, pricingName)
.then((result) => {
if (result.status === "error") {
Setting.showMessage("error", result.msg);
.then((res) => {
if (res.status === "error") {
Setting.showMessage("error", res.msg);
return;
}
this.setState({
loading: false,
pricing: result,
pricing: res.data,
});
this.onUpdatePricing(result);
this.onUpdatePricing(res.data);
});
}