deep-jade
deep-jade8mo ago

Define 2 Route with same resource

Is it possible to use same resource but for two routes, i want to make 2 menu in resource definitions but with same resource using antd integrations?
11 Replies
kapa.ai
kapa.ai8mo ago
Hi @M Farhan Zachary. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Yes, it is possible to define two routes with the same resource in refine. You can achieve this by using the identifier property. The identifier allows you to distinguish between different instances of the same resource, enabling you to create multiple paths pointing to the same resource. Here is an example of how you can define two routes with the same resource using the identifier property:
import { Refine } from "@refinedev/core";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { Layout } from "@refinedev/antd";
import { AccountListPage } from "./pages/AccountListPage";
import { ContactListPage } from "./pages/ContactListPage";

export function createCustomerRoutes({ prefix }: { prefix: string }): RouteObject[] {
return [
{
element: (
<Refine
dataProvider={dataProvider(`${location.origin}/api`)}
routerProvider={routerProvider}
resources={[
{
name: 'account',
identifier: `${prefix}-account`,
list: `${prefix}/account`,
show: `${prefix}/account/:id`,
},
{
name: 'contact',
identifier: `${prefix}-contact`,
list: `${prefix}/contact`,
},
]}
>
<Outlet />
</Refine>
),
children: [
{
path: `${prefix}/account`,
element: <AccountListPage />,
},
{
path: `${prefix}/contact`,
element: <ContactListPage />,
},
],
},
];
}
import { Refine } from "@refinedev/core";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { Layout } from "@refinedev/antd";
import { AccountListPage } from "./pages/AccountListPage";
import { ContactListPage } from "./pages/ContactListPage";

export function createCustomerRoutes({ prefix }: { prefix: string }): RouteObject[] {
return [
{
element: (
<Refine
dataProvider={dataProvider(`${location.origin}/api`)}
routerProvider={routerProvider}
resources={[
{
name: 'account',
identifier: `${prefix}-account`,
list: `${prefix}/account`,
show: `${prefix}/account/:id`,
},
{
name: 'contact',
identifier: `${prefix}-contact`,
list: `${prefix}/contact`,
},
]}
>
<Outlet />
</Refine>
),
children: [
{
path: `${prefix}/account`,
element: <AccountListPage />,
},
{
path: `${prefix}/contact`,
element: <ContactListPage />,
},
],
},
];
}
In this example, the identifier property is used to distinguish between different instances of the account and contact resources. This allows you to create multiple paths pointing to the same resource. For more information, you can refer to the identifier section of the <Refine/> component documentation (https://refine.dev/docs/api-reference/core/components/refine-config/#identifier). Additionally, you can use the resource prop in your buttons to ensure they point to the correct resource paths. For example:
import { CloneButton } from "@refinedev/antd";

const MyCloneComponent = () => {
return <CloneButton resource="categories" recordItemId="1" />;
};
import { CloneButton } from "@refinedev/antd";

const MyCloneComponent = () => {
return <CloneButton resource="categories" recordItemId="1" />;
};
For more details, refer to the Clone Button documentation (https://refine.dev/docs/ui-integrations/ant-design/components/buttons/clone-button/#resource). By using the identifier property and the resource prop, you can effectively manage multiple routes for the same resource in your refine application. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@757407865391808603> kapa.ai is still learning and improving, please let us know how it did by reacting below
deep-jade
deep-jadeOP8mo ago
after that, how to restrict the second route to be accesible by one role only? @kapa.ai
kapa.ai
kapa.ai8mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To restrict the second route to be accessible by one role only, you can use the accessControlProvider along with the CanAccess component provided by refine. This allows you to define access control policies and enforce them on specific routes. Here is an example of how you can achieve this: 1. Define the accessControlProvider: Create an accessControlProvider that specifies the access rules for different roles.
import { newEnforcer } from "casbin";
import { CanParams, CanReturnType } from "@refinedev/core";
import { adapter, model } from "../casbin/accessControl";
import { authProvider } from "./authProvider";

export const accessControlProvider = {
can: async ({ resource, action }: CanParams): Promise<CanReturnType> => {
const role = await authProvider.getPermissions?.();
const enforcer = await newEnforcer(model, adapter);
const can = await enforcer.enforce(role, resource, action);

return Promise.resolve({ can });
},
};
import { newEnforcer } from "casbin";
import { CanParams, CanReturnType } from "@refinedev/core";
import { adapter, model } from "../casbin/accessControl";
import { authProvider } from "./authProvider";

export const accessControlProvider = {
can: async ({ resource, action }: CanParams): Promise<CanReturnType> => {
const role = await authProvider.getPermissions?.();
const enforcer = await newEnforcer(model, adapter);
const can = await enforcer.enforce(role, resource, action);

return Promise.resolve({ can });
},
};
2. Wrap the routes with CanAccess: Use the CanAccess component to wrap the routes you want to protect. This component will check the access control rules defined in the accessControlProvider.
import { Refine, CanAccess } from "@refinedev/core";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { Layout } from "@refinedev/antd";
import { AccountListPage } from "./pages/AccountListPage";
import { ContactListPage } from "./pages/ContactListPage";
import { accessControlProvider } from "./providers/accessControlProvider";
export function createCustomerRoutes({ prefix }: { prefix: string }): RouteObject[] {
return [
{
element: (
<Refine
dataProvider={dataProvider(`${location.origin}/api`)}
routerProvider={routerProvider}
authProvider={authProvider}
accessControlProvider={accessControlProvider}
resources={[
{
name: 'account',
identifier: `${prefix}-account`,
list: `${prefix}/account`,
show: `${prefix}/account/:id`,
},
{
name: 'contact',
identifier: `${prefix}}-contact`,
list: `${prefix}/contact`,
},
]}
>
<Outlet />
</Refine>
),
children: [
{
path: `${prefix}/account`,
element: <AccountListPage />,
},
{
path: `${prefix}/contact`,
element: (
<CanAccess resource="contact" action="list" fallback={<div>Unauthorized!</div>}>
<ContactListPage />
</CanAccess>
),
},
],
},
];
}
import { Refine, CanAccess } from "@refinedev/core";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { Layout } from "@refinedev/antd";
import { AccountListPage } from "./pages/AccountListPage";
import { ContactListPage } from "./pages/ContactListPage";
import { accessControlProvider } from "./providers/accessControlProvider";
export function createCustomerRoutes({ prefix }: { prefix: string }): RouteObject[] {
return [
{
element: (
<Refine
dataProvider={dataProvider(`${location.origin}/api`)}
routerProvider={routerProvider}
authProvider={authProvider}
accessControlProvider={accessControlProvider}
resources={[
{
name: 'account',
identifier: `${prefix}-account`,
list: `${prefix}/account`,
show: `${prefix}/account/:id`,
},
{
name: 'contact',
identifier: `${prefix}}-contact`,
list: `${prefix}/contact`,
},
]}
>
<Outlet />
</Refine>
),
children: [
{
path: `${prefix}/account`,
element: <AccountListPage />,
},
{
path: `${prefix}/contact`,
element: (
<CanAccess resource="contact" action="list" fallback={<div>Unauthorized!</div>}>
<ContactListPage />
</CanAccess>
),
},
],
},
];
}
In this example, the CanAccess component is used to wrap the ContactListPage route. The resource and action props are passed to CanAccess to specify the resource and action being checked. If the user does not have the required permissions, the fallback component (in this case, a simple "Unauthorized!" message) will be displayed. 3. Define the access control rules: In your accessControl file, define the rules for different roles. For example:
export const adapter = new MemoryAdapter(`
p, admin, contact, (list)|(create)
p, editor, account, (list)|(create)
`);
export const adapter = new MemoryAdapter(`
p, admin, contact, (list)|(create)
p, editor, account, (list)|(create)
`);
In this example, only users with the admin role can access the contact resource, while users with the editor role can access the account resource. By following these steps ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@757407865391808603> kapa.ai is still learning and improving, please let us know how it did by reacting below
deep-jade
deep-jadeOP8mo ago
i want to control it using accesControl.ts, before i just define if some role can have access to a resources,
p, ktu, pengajuan, (list)|(create)
p, ktu, pengajuan/*, (show)
p, ktu, pengajuan/subunit_id, field, deny
p, ktu, pengajuan, (list)|(create)
p, ktu, pengajuan/*, (show)
p, ktu, pengajuan/subunit_id, field, deny
i want to control it using accesControl.ts, before i just define if some role can have access to a resources, i want to control it using accesControl.ts, before i just define if some role can have access to a resources,
p, ktu, pengajuan, (list)|(create)
p, ktu, pengajuan/*, (show)
p, ktu, pengajuan/subunit_id, field, deny
p, ktu, pengajuan, (list)|(create)
p, ktu, pengajuan/*, (show)
p, ktu, pengajuan/subunit_id, field, deny
but how to do that if i have two path with same resources?
{
name: "pengajuan",
identifier: "pengajuan-peninjauan",
list: "/pengajuan-peninjauan",
create: "/pengajuan-peninjauan/create",
show: "/pengajuan-peninjauan/show/:id",
edit: "/pengajuan-peninjauan/edit/:id",
meta: {
canDelete: true,
label: "Pengajuan",
icon: <FileSearchOutlined />,
},
},
{
name: "pengajuan",
identifier: "pengajuan-proses",
list: "/pengajuan-proses",
create: "/pengajuan-proses/create",
show: "/pengajuan-proses/show/:id",
edit: "/pengajuan-proses/edit/:id",
meta: {
canDelete: true,
label: "Berkas",
icon: <FileDoneOutlined />,
},
},
{
name: "pengajuan",
identifier: "pengajuan-peninjauan",
list: "/pengajuan-peninjauan",
create: "/pengajuan-peninjauan/create",
show: "/pengajuan-peninjauan/show/:id",
edit: "/pengajuan-peninjauan/edit/:id",
meta: {
canDelete: true,
label: "Pengajuan",
icon: <FileSearchOutlined />,
},
},
{
name: "pengajuan",
identifier: "pengajuan-proses",
list: "/pengajuan-proses",
create: "/pengajuan-proses/create",
show: "/pengajuan-proses/show/:id",
edit: "/pengajuan-proses/edit/:id",
meta: {
canDelete: true,
label: "Berkas",
icon: <FileDoneOutlined />,
},
},
@kapa.ai
kapa.ai
kapa.ai8mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To control access for two paths with the same resource using accessControl.ts, you can leverage the identifier property to distinguish between the different instances of the resource. This way, you can define specific access control rules for each path. Here is how you can achieve this: 1. Define the resources with identifiers: Define your resources with unique identifiers in your Refine configuration.
import { Refine } from "@refinedev/core";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { Layout } from "@refinedev/antd";
import { FileSearchOutlined, FileDoneOutlined } from "@ant-design/icons";
import { accessControlProvider } from "./accessControlProvider";
const App: React.FC = () => {
return (
<BrowserRouter>
<Refine
dataProvider={dataProvider("<https://api.fake-rest.refine.dev">)}
routerProvider={routerProvider}
accessControlProvider={accessControlProvider}
resources={[
{
name: "pengajuan",
identifier: "pengajuan-peninjauan",
list: "/pengajuan-peninjauan",
create: "/pengajuan-peninjauan/create",
show: "/pengajuan-peninjauan/show/:id",
edit: "/pengajuan-peninjauan/edit/:id",
meta: {
canDelete: true,
label: "Pengajuan",
icon: <FileSearchOutlined />,
},
},
{
name: "pengajuan",
identifier: "pengajuan-proses",
list: "/pengajuan-proses",
create: "/pengajuan-proses/create",
show: "/pengajuan-proses/show/:id",
edit: "/pengajuan-proses/edit/:id",
meta: {
canDelete: true,
label: "Berkas",
icon: <FileDoneOutlined />,
},
},
]}
>
<Outlet />
</Refine>
</BrowserRouter>
);
};

export default App;
import { Refine } from "@refinedev/core";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { Layout } from "@refinedev/antd";
import { FileSearchOutlined, FileDoneOutlined } from "@ant-design/icons";
import { accessControlProvider } from "./accessControlProvider";
const App: React.FC = () => {
return (
<BrowserRouter>
<Refine
dataProvider={dataProvider("<https://api.fake-rest.refine.dev">)}
routerProvider={routerProvider}
accessControlProvider={accessControlProvider}
resources={[
{
name: "pengajuan",
identifier: "pengajuan-peninjauan",
list: "/pengajuan-peninjauan",
create: "/pengajuan-peninjauan/create",
show: "/pengajuan-peninjauan/show/:id",
edit: "/pengajuan-peninjauan/edit/:id",
meta: {
canDelete: true,
label: "Pengajuan",
icon: <FileSearchOutlined />,
},
},
{
name: "pengajuan",
identifier: "pengajuan-proses",
list: "/pengajuan-proses",
create: "/pengajuan-proses/create",
show: "/pengajuan-proses/show/:id",
edit: "/pengajuan-proses/edit/:id",
meta: {
canDelete: true,
label: "Berkas",
icon: <FileDoneOutlined />,
},
},
]}
>
<Outlet />
</Refine>
</BrowserRouter>
);
};

export default App;
2. Define access control rules in accessControl.ts: Use the identifier property to specify different access control rules for each path.
import { AccessControlProvider } from "@refinedev/core";
import { newEnforcer } from "casbin";
import { model, adapter } from "./accessControl";

export const accessControlProvider: AccessControlProvider = {
can: async ({ resource, action, params }) => {
const enforcer = await newEnforcer(model, adapter);
const can = await enforcer.enforce("ktu", resource, action);

return { can };
},
};
// Example Casbin model and policy
export const model = `
[request_definition]
r = sub, res, act

[policy_definition]
p = sub, res, act, eft

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && r.res == p.res && r.act == p.act
`;

export const adapter = new MemoryAdapter(`
p, ktu, pengajuan-peninjauan, (list)|(create)|(show)
p, ktu, pengajuan-proses, (list)|(create)|(show)
`);
import { AccessControlProvider } from "@refinedev/core";
import { newEnforcer } from "casbin";
import { model, adapter } from "./accessControl";

export const accessControlProvider: AccessControlProvider = {
can: async ({ resource, action, params }) => {
const enforcer = await newEnforcer(model, adapter);
const can = await enforcer.enforce("ktu", resource, action);

return { can };
},
};
// Example Casbin model and policy
export const model = `
[request_definition]
r = sub, res, act

[policy_definition]
p = sub, res, act, eft

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && r.res == p.res && r.act == p.act
`;

export const adapter = new MemoryAdapter(`
p, ktu, pengajuan-peninjauan, (list)|(create)|(show)
p, ktu, pengajuan-proses, (list)|(create)|(show)
`);
In this example, the accessControlProvider uses the identifier property to distinguish between the pengajuan-peninjauan and pengajuan-proses resources. The Casbin model and policy are defined to grant access to the ktu role for specific actions on each resource. By following these steps, you can control access to different paths with the ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@757407865391808603> kapa.ai is still learning and improving, please let us know how it did by reacting below
deep-jade
deep-jadeOP8mo ago
i can't do that, it still uses the name instead of identifier to distungish it @kapa.ai
kapa.ai
kapa.ai8mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- To ensure that the accessControlProvider uses the identifier instead of the name to distinguish between resources, you need to make sure that the identifier is correctly passed and checked in your access control logic. Here’s how you can achieve this: 1. Define the resources with identifiers: Define your resources with unique identifiers in your Refine configuration.
import { Refine } from "@refinedev/core";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { Layout } from "@refinedev/antd";
import { FileSearchOutlined, FileDoneOutlined } from "@ant-design/icons";
import { accessControlProvider } from "./accessControlProvider";
const App: React.FC = () => {
return (
<BrowserRouter>
<Refine
dataProvider={{ dataProvider("<https://api.fake-rest.refine.dev">) }}
routerProvider={{ routerProvider }}
accessControlProvider={{ accessControlProvider }}
resources={[
{
name: "pengajuan",
identifier: "pengajuan-peninjauan",
list: "/pengajuan-peninjauan",
create: "/pengajuan-peninjauan/create",
show: "/pengajuan-peninjauan/show/:id",
edit: "/pengajuan-peninjauan/edit/:id",
meta: {
canDelete: true,
label: "Pengajuan",
icon: <FileSearchOutlined />,
},
},
{
name: "pengajuan",
identifier: "pengajuan-proses",
list: "/pengajuan-proses",
create: "/pengajuan-proses/create",
show: "/pengajuan-proses/show/:id",
edit: "/pengajuan-proses/edit/:id",
meta: {
canDelete: true,
label: "Berkas",
icon: <FileDoneOutlined />,
},
},
]}
>
<Outlet />
</Refine>
</BrowserRouter>
);
};

export default App;
import { Refine } from "@refinedev/core";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { Layout } from "@refinedev/antd";
import { FileSearchOutlined, FileDoneOutlined } from "@ant-design/icons";
import { accessControlProvider } from "./accessControlProvider";
const App: React.FC = () => {
return (
<BrowserRouter>
<Refine
dataProvider={{ dataProvider("<https://api.fake-rest.refine.dev">) }}
routerProvider={{ routerProvider }}
accessControlProvider={{ accessControlProvider }}
resources={[
{
name: "pengajuan",
identifier: "pengajuan-peninjauan",
list: "/pengajuan-peninjauan",
create: "/pengajuan-peninjauan/create",
show: "/pengajuan-peninjauan/show/:id",
edit: "/pengajuan-peninjauan/edit/:id",
meta: {
canDelete: true,
label: "Pengajuan",
icon: <FileSearchOutlined />,
},
},
{
name: "pengajuan",
identifier: "pengajuan-proses",
list: "/pengajuan-proses",
create: "/pengajuan-proses/create",
show: "/pengajuan-proses/show/:id",
edit: "/pengajuan-proses/edit/:id",
meta: {
canDelete: true,
label: "Berkas",
icon: <FileDoneOutlined />,
},
},
]}
>
<Outlet />
</Refine>
</BrowserRouter>
);
};

export default App;
2. Define access control rules in accessControl.ts: Use the identifier property to specify different access control rules for each path.
import { AccessControlProvider } from "@refinedev/core";
import { newEnforcer } from "casbin";
import { model, adapter } from "./accessControl";

export const accessControlProvider: AccessControlProvider = {
can: async ({ resource, action, params }) => {
const enforcer = await newEnforcer(model, adapter);
const can = await enforcer.enforce("ktu", resource, action);

return { can };
},
};
// Example Casbin model and policy
export const model = `
[request_definition]
r = sub, res, act

[policy_definition]
p = sub, res, act, eft

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && r.res == p.res && r.act == p.act
`;

export const adapter = new MemoryAdapter(`
p, ktu, pengajuan-peninjauan, (list)|(create)|(show)
p, ktu, pengajuan-proses, (list)|(create)|(show)
`);
import { AccessControlProvider } from "@refinedev/core";
import { newEnforcer } from "casbin";
import { model, adapter } from "./accessControl";

export const accessControlProvider: AccessControlProvider = {
can: async ({ resource, action, params }) => {
const enforcer = await newEnforcer(model, adapter);
const can = await enforcer.enforce("ktu", resource, action);

return { can };
},
};
// Example Casbin model and policy
export const model = `
[request_definition]
r = sub, res, act

[policy_definition]
p = sub, res, act, eft

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = r.sub == p.sub && r.res == p.res && r.act == p.act
`;

export const adapter = new MemoryAdapter(`
p, ktu, pengajuan-peninjauan, (list)|(create)|(show)
p, ktu, pengajuan-proses, (list)|(create)|(show)
`);
In this example, the accessControlProvider uses the identifier property to distinguish between the pengajuan-peninjauan and pengajuan-proses resources. The Casbin model and policy are defined to grant access to the ktu role for specific actions on each resource. By following these steps, you can control access to different paths with the same ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai8mo ago
<@757407865391808603> kapa.ai is still learning and improving, please let us know how it did by reacting below

Did you find this page helpful?