Compare commits

...

12 Commits

Author SHA1 Message Date
Yaodong Yu
bfe8e5f3e7 fix: fix response data assignment error (#2129) 2023-07-25 13:52:31 +08:00
Yang Luo
702ee6acd0 Print log for StartLdapServer()'s error 2023-07-25 01:49:43 +08:00
Yaodong Yu
0a9587901a fix: fix response data assignment error in ApplicationEditPage.js (#2126) 2023-07-24 20:09:09 +08:00
Yaodong Yu
577bd6ce58 feat: fix response data assignment error (#2123) 2023-07-24 14:52:30 +08:00
Yaodong Yu
3c4112dd44 refactor: optimize the code to getEnforcer (#2120) 2023-07-24 14:02:34 +08:00
haiwu
b7a37126ad feat: restrict redirectUrls for CAS login (#2118)
* feat: support cas restricted login

* feat: add cas login i18n

* feat: add CheckCasService for all cas api

* feat: gofumpt

* feat: replace 404

* feat: reuse i18n

* feat: delete CheckCasService

* Update token_cas.go

* Update LoginPage.js

* Update token_cas.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-07-24 11:47:31 +08:00
UsherFall
8669d5bb0d chore: hide field of IntranetEndpoint in Tencent COS storage provider (#2117) 2023-07-23 19:02:42 +08:00
Baihhh
aee3ea4981 feat: improve TermsOfUse UI in mobile (#2106)
* style: Mobile interface adaptation

Signed-off-by: baihhh <2542274498@qq.com>

* Update index.css

---------

Signed-off-by: baihhh <2542274498@qq.com>
Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-07-23 15:28:13 +08:00
Yang Luo
516f4b7569 Fix response of /api/get-sorted-users and /api/get-user-count 2023-07-23 14:46:38 +08:00
UsherFall
7d7ca10481 fix: hide fields of minio storage provider (#2115)
* feat: hide field of minio storage provider

* feat: hide field of domain in minio storage provider
2023-07-23 14:40:30 +08:00
UsherFall
a9d4978a0f chore: hide fields of local file system storage provider (#2109)
* style: adjust local file system storage

* style: disable domain when use local file system
2023-07-23 11:48:15 +08:00
Yang Luo
09f40bb5ce Fix id of "/api/get-resource" API 2023-07-23 11:33:48 +08:00
25 changed files with 185 additions and 130 deletions

View File

@@ -183,6 +183,8 @@ func (c *ApiController) DeleteOrganization() {
func (c *ApiController) GetDefaultApplication() {
userId := c.GetSessionUsername()
id := c.Input().Get("id")
redirectUri := c.Input().Get("redirectUri")
typ := c.Input().Get("type")
application, err := object.GetDefaultApplication(id)
if err != nil {
@@ -190,6 +192,14 @@ func (c *ApiController) GetDefaultApplication() {
return
}
if typ == "cas" {
err = object.CheckCasRestrict(application, c.GetAcceptLanguage(), redirectUri)
if err != nil {
c.ResponseError(err.Error())
return
}
}
maskedApplication := object.GetMaskedApplication(application, userId)
c.ResponseOk(maskedApplication)
}

View File

@@ -94,11 +94,10 @@ func (c *ApiController) GetPlan() {
plan.Options = append(plan.Options, option.DisplayName)
}
c.Data["json"] = plan
c.ResponseOk(plan)
} else {
c.Data["json"] = plan
c.ResponseOk(plan)
}
c.ServeJSON()
}
// UpdatePlan

View File

@@ -88,8 +88,7 @@ func (c *ApiController) GetProduct() {
return
}
c.Data["json"] = product
c.ServeJSON()
c.ResponseOk(product)
}
// UpdateProduct

View File

@@ -510,8 +510,7 @@ func (c *ApiController) GetSortedUsers() {
return
}
c.Data["json"] = maskedUsers
c.ServeJSON()
c.ResponseOk(maskedUsers)
}
// GetUserCount
@@ -538,8 +537,7 @@ func (c *ApiController) GetUserCount() {
return
}
c.Data["json"] = count
c.ServeJSON()
c.ResponseOk(count)
}
// AddUserkeys

View File

@@ -34,7 +34,7 @@ func StartLdapServer() {
server.Handle(routes)
err := server.ListenAndServe("0.0.0.0:" + conf.GetConfigString("ldapServerPort"))
if err != nil {
return
log.Printf("StartLdapServer() failed, ErrMsg = %s", err.Error())
}
}

View File

@@ -161,5 +161,8 @@ func modelChangeTrigger(oldName string, newName string) error {
}
func HasRoleDefinition(m model.Model) bool {
if m == nil {
return false
}
return m["g"] != nil
}

View File

@@ -26,42 +26,7 @@ import (
xormadapter "github.com/casdoor/xorm-adapter/v3"
)
func getEnforcer(permission *Permission, permissionIDs ...string) *casbin.Enforcer {
tableName := "permission_rule"
if len(permission.Adapter) != 0 {
adapterObj, err := getCasbinAdapter(permission.Owner, permission.Adapter)
if err != nil {
panic(err)
}
if adapterObj != nil && adapterObj.Table != "" {
tableName = adapterObj.Table
}
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
driverName := conf.GetConfigString("driverName")
dataSourceName := conf.GetConfigRealDataSourceName(driverName)
adapter, err := xormadapter.NewAdapterWithTableName(driverName, dataSourceName, tableName, tableNamePrefix, true)
if err != nil {
panic(err)
}
permissionModel, err := getModel(permission.Owner, permission.Model)
if err != nil {
panic(err)
}
m := model.Model{}
if permissionModel != nil {
m, err = GetBuiltInModel(permissionModel.ModelText)
} else {
m, err = GetBuiltInModel("")
}
if err != nil {
panic(err)
}
func getEnforcer(p *Permission, permissionIDs ...string) *casbin.Enforcer {
// Init an enforcer instance without specifying a model or adapter.
// If you specify an adapter, it will load all policies, which is a
// heavy process that can slow down the application.
@@ -70,14 +35,17 @@ func getEnforcer(permission *Permission, permissionIDs ...string) *casbin.Enforc
panic(err)
}
err = enforcer.InitWithModelAndAdapter(m, nil)
err = p.setEnforcerModel(enforcer)
if err != nil {
panic(err)
}
enforcer.SetAdapter(adapter)
err = p.setEnforcerAdapter(enforcer)
if err != nil {
panic(err)
}
policyFilterV5 := []string{permission.GetId()}
policyFilterV5 := []string{p.GetId()}
if len(permissionIDs) != 0 {
policyFilterV5 = permissionIDs
}
@@ -86,7 +54,7 @@ func getEnforcer(permission *Permission, permissionIDs ...string) *casbin.Enforc
V5: policyFilterV5,
}
if !HasRoleDefinition(m) {
if !HasRoleDefinition(enforcer.GetModel()) {
policyFilter.Ptype = []string{"p"}
}
@@ -98,6 +66,54 @@ func getEnforcer(permission *Permission, permissionIDs ...string) *casbin.Enforc
return enforcer
}
func (p *Permission) setEnforcerAdapter(enforcer *casbin.Enforcer) error {
tableName := "permission_rule"
if len(p.Adapter) != 0 {
adapterObj, err := getCasbinAdapter(p.Owner, p.Adapter)
if err != nil {
return err
}
if adapterObj != nil && adapterObj.Table != "" {
tableName = adapterObj.Table
}
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
driverName := conf.GetConfigString("driverName")
dataSourceName := conf.GetConfigRealDataSourceName(driverName)
casbinAdapter, err := xormadapter.NewAdapterWithTableName(driverName, dataSourceName, tableName, tableNamePrefix, true)
if err != nil {
return err
}
enforcer.SetAdapter(casbinAdapter)
return nil
}
func (p *Permission) setEnforcerModel(enforcer *casbin.Enforcer) error {
permissionModel, err := getModel(p.Owner, p.Model)
if err != nil {
return err
}
// TODO: return error if permissionModel is nil.
m := model.Model{}
if permissionModel != nil {
m, err = GetBuiltInModel(permissionModel.ModelText)
} else {
m, err = GetBuiltInModel("")
}
if err != nil {
return err
}
err = enforcer.InitWithModelAndAdapter(m, nil)
if err != nil {
return err
}
return nil
}
func getPolicies(permission *Permission) [][]string {
var policies [][]string

View File

@@ -16,6 +16,7 @@ package object
import (
"fmt"
"strings"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
@@ -76,6 +77,10 @@ func GetPaginationResources(owner, user string, offset, limit int, field, value,
}
func getResource(owner string, name string) (*Resource, error) {
if !strings.HasPrefix(name, "/") {
name = "/" + name
}
resource := Resource{Owner: owner, Name: name}
existed, err := adapter.Engine.Get(&resource)
if err != nil {

View File

@@ -26,6 +26,7 @@ import (
"time"
"github.com/beevik/etree"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util"
dsig "github.com/russellhaering/goxmldsig"
)
@@ -122,6 +123,13 @@ var stToServiceResponse sync.Map
// pgt is short for proxy granting ticket
var pgtToServiceResponse sync.Map
func CheckCasRestrict(application *Application, lang string, service string) error {
if len(application.RedirectUris) > 0 && !application.IsRedirectUriValid(service) {
return fmt.Errorf(i18n.Translate(lang, "token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), service)
}
return nil
}
func StoreCasTokenForPgt(token *CasAuthenticationSuccess, service, userId string) string {
pgt := fmt.Sprintf("PGT-%s", util.GenerateId())
pgtToServiceResponse.Store(pgt, &CasAuthenticationSuccessWrapper{

View File

@@ -156,7 +156,7 @@ func AuthzFilter(ctx *context.Context) {
urlPath := getUrlPath(ctx.Request.URL.Path)
objOwner, objName := "", ""
if urlPath != "/api/get-app-login" {
if urlPath != "/api/get-app-login" && urlPath != "/api/get-resource" {
objOwner, objName = getObject(ctx)
}

View File

@@ -23,7 +23,7 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
case "AWS S3":
return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "MinIO":
return NewMinIOS3StorageProvider(clientId, clientSecret, region, bucket, endpoint)
return NewMinIOS3StorageProvider(clientId, clientSecret, "_", bucket, endpoint)
case "Aliyun OSS":
return NewAliyunOssStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Tencent Cloud COS":

View File

@@ -129,32 +129,33 @@ class ApplicationEditPage extends React.Component {
return;
}
if (res.grantTypes === null || res.grantTypes === undefined || res.grantTypes.length === 0) {
res.grantTypes = ["authorization_code"];
const application = res.data;
if (application.grantTypes === null || application.grantTypes === undefined || application.grantTypes.length === 0) {
application.grantTypes = ["authorization_code"];
}
if (res.tags === null || res.tags === undefined) {
res.tags = [];
if (application.tags === null || application.tags === undefined) {
application.tags = [];
}
this.setState({
application: res.data,
application: application,
});
this.getCerts(res.organization);
this.getCerts(application.organization);
});
}
getOrganizations() {
OrganizationBackend.getOrganizations("admin")
.then((res) => {
if (res?.status === "error") {
if (res.status === "error") {
this.setState({
isAuthorized: false,
});
} else {
this.setState({
organizations: (res.msg === undefined) ? res : [],
organizations: res.data || [],
});
}
});
@@ -164,7 +165,7 @@ class ApplicationEditPage extends React.Component {
CertBackend.getCerts(owner)
.then((res) => {
this.setState({
certs: (res.msg === undefined) ? res : [],
certs: res.data || [],
});
});
}

View File

@@ -54,7 +54,7 @@ class ChatEditPage extends React.Component {
chat: res.data,
});
this.getUsers(res.organization);
this.getUsers(res.data.organization);
});
}

View File

@@ -58,7 +58,7 @@ class MessageEditPage extends React.Component {
this.setState({
message: res.data,
});
this.getUsers(res.organization);
this.getUsers(res.data.organization);
});
}
@@ -75,7 +75,7 @@ class MessageEditPage extends React.Component {
ChatBackend.getChats("admin")
.then((res) => {
this.setState({
chats: (res.msg === undefined) ? res : [],
chats: res.data || [],
});
});
}

View File

@@ -41,12 +41,12 @@ class PaymentResultPage extends React.Component {
getPayment() {
PaymentBackend.getPayment("admin", this.state.paymentName)
.then((payment) => {
.then((res) => {
this.setState({
payment: payment,
payment: res.data,
});
if (payment.state === "Created") {
if (res.data.state === "Created") {
this.setState({timeout: setTimeout(() => this.getPayment(), 1000)});
}
});

View File

@@ -46,18 +46,18 @@ class PlanEditPage extends React.Component {
getPlan() {
PlanBackend.getPlan(this.state.organizationName, this.state.planName)
.then((plan) => {
if (plan === null) {
.then((res) => {
if (res.data === null) {
this.props.history.push("/404");
return;
}
this.setState({
plan: plan,
plan: res.data,
});
this.getUsers(plan.owner);
this.getRoles(plan.owner);
this.getUsers(this.state.organizationName);
this.getRoles(this.state.organizationName);
});
}

View File

@@ -62,7 +62,7 @@ class PricingEditPage extends React.Component {
this.setState({
pricing: res.data,
});
this.getPlans(res.owner);
this.getPlans(this.state.organizationName);
});
}

View File

@@ -46,14 +46,14 @@ class ProductEditPage extends React.Component {
getProduct() {
ProductBackend.getProduct(this.state.organizationName, this.state.productName)
.then((product) => {
if (product === null) {
.then((res) => {
if (res.data === null) {
this.props.history.push("/404");
return;
}
this.setState({
product: product,
product: res.data,
});
});
}

View File

@@ -527,7 +527,7 @@ class ProviderEditPage extends React.Component {
)
}
{
(this.state.provider.category === "Captcha" && this.state.provider.type === "Default") || this.state.provider.category === "Web3" ? null : (
(this.state.provider.category === "Captcha" && this.state.provider.type === "Default") || (this.state.provider.category === "Web3") || (this.state.provider.category === "Storage" && this.state.provider.type === "Local File System") ? null : (
<React.Fragment>
{
this.state.provider.category === "AI" ? null : (
@@ -616,36 +616,42 @@ class ProviderEditPage extends React.Component {
}
{this.state.provider.category === "Storage" ? (
<div>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Endpoint"), i18next.t("provider:Region endpoint for Internet"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.endpoint} onChange={e => {
this.updateProviderField("endpoint", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.intranetEndpoint} onChange={e => {
this.updateProviderField("intranetEndpoint", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Bucket"), i18next.t("provider:Bucket - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.bucket} onChange={e => {
this.updateProviderField("bucket", e.target.value);
}} />
</Col>
</Row>
{["Local File System"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Endpoint"), i18next.t("provider:Region endpoint for Internet"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.endpoint} onChange={e => {
this.updateProviderField("endpoint", e.target.value);
}} />
</Col>
</Row>
)}
{["Local File System", "MinIO", "Tencent Cloud COS"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.intranetEndpoint} onChange={e => {
this.updateProviderField("intranetEndpoint", e.target.value);
}} />
</Col>
</Row>
)}
{["Local File System"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Bucket"), i18next.t("provider:Bucket - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.bucket} onChange={e => {
this.updateProviderField("bucket", e.target.value);
}} />
</Col>
</Row>
)}
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Path prefix"), i18next.t("provider:Path prefix - Tooltip"))} :
@@ -656,17 +662,19 @@ class ProviderEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.domain} onChange={e => {
this.updateProviderField("domain", e.target.value);
}} />
</Col>
</Row>
{["AWS S3", "MinIO", "Tencent Cloud COS"].includes(this.state.provider.type) ? (
{["MinIO"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.domain} disabled={this.state.provider.type === "Local File System"} onChange={e => {
this.updateProviderField("domain", e.target.value);
}} />
</Col>
</Row>
)}
{["AWS S3", "Tencent Cloud COS"].includes(this.state.provider.type) ? (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :

View File

@@ -56,8 +56,8 @@ class RoleEditPage extends React.Component {
role: res.data,
});
this.getUsers(res.owner);
this.getRoles(res.owner);
this.getUsers(this.state.organizationName);
this.getRoles(this.state.organizationName);
});
}

View File

@@ -61,8 +61,8 @@ class SubscriptionEditPage extends React.Component {
subscription: res.data,
});
this.getUsers(res.owner);
this.getPlanes(res.owner);
this.getUsers(this.state.organizationName);
this.getPlanes(this.state.organizationName);
});
}

View File

@@ -173,7 +173,12 @@ class LoginPage extends React.Component {
this.onUpdateApplication(res.data);
});
} else {
OrganizationBackend.getDefaultApplication("admin", this.state.owner)
let redirectUri = "";
if (this.state.type === "cas") {
const casParams = Util.getCasParameters();
redirectUri = casParams.service;
}
OrganizationBackend.getDefaultApplication("admin", this.state.owner, this.state.type, redirectUri)
.then((res) => {
if (res.status === "ok") {
const application = res.data;
@@ -183,9 +188,9 @@ class LoginPage extends React.Component {
});
} else {
this.onUpdateApplication(null);
Setting.showMessage("error", res.msg);
this.props.history.push("/404");
this.setState({
msg: res.msg,
});
}
});
}

View File

@@ -70,8 +70,8 @@ export function deleteOrganization(organization) {
}).then(res => res.json());
}
export function getDefaultApplication(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-default-application?id=${owner}/${encodeURIComponent(name)}`, {
export function getDefaultApplication(owner, name, type = "", redirectUri = "") {
return fetch(`${Setting.ServerUrl}/api/get-default-application?id=${owner}/${encodeURIComponent(name)}&type=${type}&redirectUri=${redirectUri}`, {
method: "GET",
credentials: "include",
headers: {

View File

@@ -15,6 +15,7 @@
import {Checkbox, Form, Modal} from "antd";
import i18next from "i18next";
import React, {useEffect, useState} from "react";
import * as Setting from "../../Setting";
export const AgreementModal = (props) => {
const {open, onOk, onCancel, application} = props;
@@ -31,14 +32,16 @@ export const AgreementModal = (props) => {
<Modal
title={i18next.t("signup:Terms of Use")}
open={open}
width={"55vw"}
width={Setting.isMobile() ? "100vw" : "55vw"}
closable={false}
okText={i18next.t("signup:Accept")}
cancelText={i18next.t("signup:Decline")}
onOk={onOk}
onCancel={onCancel}
style={{top: Setting.isMobile() ? "5px" : ""}}
maskStyle={{backgroundColor: Setting.isMobile() ? "white" : ""}}
>
<iframe title={"terms"} style={{border: 0, width: "100%", height: "60vh"}} srcDoc={doc} />
<iframe title={"terms"} style={{border: 0, width: "100%", height: Setting.isMobile() ? "80vh" : "60vh"}} srcDoc={doc} />
</Modal>
);
};