(Casbin) Query String Bypasses Access Control

Hi, I currently have ID based access to my app working (following the example in the docs here: https://refine.dev/docs/advanced-tutorials/access-control/#example). When a user attempts to access a prohibited page via direct URL (i.e /users), it prevents access as expected. However, the presence of the query string (/users?pageSize=10&current=1) bypasses this, and allows the user to view the page. Is there any way I can accommodate this or remove this default query string altogether? Correction: direct URL allows a prohibited user to access a page regardless.
28 Replies
kapa.ai
kapa.ai11mo ago
Hi @avangeous. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Hmm, I don't know enough to give you a confident answer yet. The information extracts provided do not contain specific details on how to handle query strings in the context of access control with Refine. However, Refine provides a flexible accessControlProvider API that allows you to manage access control throughout your app. You might be able to extend your access control logic to handle query strings in the URL. For more information, you can refer to the Access Control Provider documentation (https://refine.dev/docs/api-reference/core/providers/accessControl-provider). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai11mo ago
<@1122927158348763206> kapa.ai is still learning and improving, please let us how it did by reacting below
fascinating-indigo
fascinating-indigo11mo ago
Nevermind, correction: direct URL allows a prohibited user to access a page regardless. @kapa.ai How can I prevent a user from accessing a page from a direct URL link?
kapa.ai
kapa.ai11mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- In Refine, you can prevent a user from accessing a page from a direct URL link by implementing access control logic in the accessControlProvider. This provider should implement an asynchronous method named can which is used to check if the desired access will be granted. Here's an example of how you might set up your accessControlProvider:
export const accessControlProvider = {
can: async ({ resource, action, params }) => {
if (resource === "posts" && action === "edit") {
return {
can: false,
reason: "Unauthorized",
};
}

return { can: true };
},
};
export const accessControlProvider = {
can: async ({ resource, action, params }) => {
if (resource === "posts" && action === "edit") {
return {
can: false,
reason: "Unauthorized",
};
}

return { can: true };
},
};
In this example, if a user tries to edit a post, the can function will return false, and the user will be unauthorized to perform the action. You can also use the params argument to access additional information about the resource, such as its name or metadata:
const resourceName = params?.resource?.name;
const anyUsefulMeta = params?.resource?.meta?.yourUsefulMeta;

export const accessControlProvider = {
can: async ({ resource, action, params }) => {
if (
resourceName === "posts" &&
anyUsefulMeta === true &&
action === "edit"
) {
return {
can: false,
reason: "Unauthorized",
};
}
},
};
const resourceName = params?.resource?.name;
const anyUsefulMeta = params?.resource?.meta?.yourUsefulMeta;

export const accessControlProvider = {
can: async ({ resource, action, params }) => {
if (
resourceName === "posts" &&
anyUsefulMeta === true &&
action === "edit"
) {
return {
can: false,
reason: "Unauthorized",
};
}
},
};
In this case, the can function checks not only the resource and action, but also the name of the resource and some useful metadata. You can find more information about the accessControlProvider in the Refine documentation (https://refine.dev/docs/api-reference/core/providers/accessControl-provider/). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai11mo ago
<@1122927158348763206> kapa.ai is still learning and improving, please let us how it did by reacting below
fascinating-indigo
fascinating-indigo11mo ago
@kapa.ai In the context of Casbin, do I have to explicitly deny a user ID type from 'show' access?
kapa.ai
kapa.ai11mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- In the context of Casbin, you don't necessarily have to explicitly deny a user ID type from 'show' access. Casbin works on the principle of explicit permission, which means that if a permission is not explicitly granted, it is implicitly denied. However, if you want to explicitly deny a user ID type from 'show' access, you can do so by defining a policy in your Casbin model. Here's an example of how you might do this:
export const adapter = new MemoryAdapter(`
p, userID, resource, show, deny
`);
export const adapter = new MemoryAdapter(`
p, userID, resource, show, deny
`);
In this example, the user with the ID 'userID' is explicitly denied 'show' access to the 'resource'. Remember, the order of policies also matters in Casbin. If there is a 'deny' policy after an 'allow' policy for the same user and resource, the 'deny' policy will override the 'allow' policy. For more information on how Casbin works, you can refer to the Casbin documentation (https://casbin.org/docs/how-it-works). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai11mo ago
<@1122927158348763206> kapa.ai is still learning and improving, please let us how it did by reacting below
fascinating-indigo
fascinating-indigo11mo ago
@kapa.ai Hmm, explicitly denying a userID from 'show' access still allows them to view the page. Is this common?
kapa.ai
kapa.ai11mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- If a user is still able to view a page despite being explicitly denied 'show' access, it could be due to a few reasons: 1. Order of Policies: In Casbin, the order of policies matters. If there is an 'allow' policy for the same user and resource before the 'deny' policy, the 'allow' policy will take precedence. 2. Incorrect Policy Definition: The policy might not be correctly defined in the Casbin model or adapter. Make sure the policy is correctly defined and matches the exact user ID and resource. 3. Caching or State Issues: If you're using a client-side framework like React, there could be caching or state issues that are causing the old permissions to persist. Make sure your application state is correctly updated when the permissions change. 4. Incorrect Usage of can Function: The can function in your accessControlProvider might not be correctly checking the permissions. Make sure you're correctly using the can function to check if the user has 'show' access. Here's an example of how you might use the can function with Casbin:
accessControlProvider={{
can: async ({ resource, action }) => {
const enforcer = await newEnforcer(model, adapter);
const can = await enforcer.enforce(
"userID",
resource,
action,
);
return { can };
},
}}
accessControlProvider={{
can: async ({ resource, action }) => {
const enforcer = await newEnforcer(model, adapter);
const can = await enforcer.enforce(
"userID",
resource,
action,
);
return { can };
},
}}
In this example, the can function uses the Casbin enforce method to check if the user with the ID 'userID' has the specified action access to the resource. For more information on how to use the can function and the accessControlProvider, you can refer to the Refine documentation (https://refine.dev/docs/api-reference/core/providers/accessControl-provider/). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai11mo ago
<@1122927158348763206> kapa.ai is still learning and improving, please let us how it did by reacting below
fascinating-indigo
fascinating-indigo11mo ago
@kapa.ai Is there a way for me to reset the cache/state on logout, or prior to login?
kapa.ai
kapa.ai11mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Yes, you can reset the cache/state on logout or prior to login using the useLogout hook provided by Refine. This hook returns a mutate function that you can call to trigger the logout process. Here's an example of how you might use the useLogout hook:
import { useLogout } from "@refinedev/core";

const { mutate: logout } = useLogout();

logout();
import { useLogout } from "@refinedev/core";

const { mutate: logout } = useLogout();

logout();
In this example, calling logout() will trigger the logout process. If you want to perform some additional actions during the logout process, such as resetting the cache or state, you can do so in the logout method of your authProvider:
import type { AuthBindings } from "@refinedev/core";

const authProvider: AuthBindings = {
// ---
logout: async () => {
// Reset cache or state here
// ---
return {
success: true,
redirectTo: "/login",
};
},
};
import type { AuthBindings } from "@refinedev/core";

const authProvider: AuthBindings = {
// ---
logout: async () => {
// Reset cache or state here
// ---
return {
success: true,
redirectTo: "/login",
};
},
};
In this example, the logout method of the authProvider resets the cache or state before returning. For more information on how to use the useLogout hook and the authProvider, you can refer to the Refine documentation (https://refine.dev/docs/api-reference/core/hooks/authentication/useLogout). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai11mo ago
<@1122927158348763206> kapa.ai is still learning and improving, please let us how it did by reacting below
fascinating-indigo
fascinating-indigo11mo ago
Tried accessing the page from incognito and it still allows a prohibited user to view a given page. 1. Order of Policies: No duplicate policies. 2. Incorrect Policy Definition: Page is hidden as expected from the navbar, just doesn't prevent direct URL access. 3. Caching or State Issues: Fresh login from an incognito tab still has this issue. 4. Incorrect Usage of can Function: Possible? But my version is virtually identical to the Github Casbin example.
optimistic-gold
optimistic-gold11mo ago
Hi @avangeous, There has been some corrections and a lot of talk here. what is the latest situation, can you give me a summary?
fascinating-indigo
fascinating-indigo11mo ago
Yeah no problem, TLDR
1) I’m using Casbin for access control, following the documentation example 2) it works correctly in that users of a certain ID don’t see the links to the pages I’ve blocked them from viewing in the navbar 3) it doesn’t work when I directly go to the link of that page (the user is able to view it) E.g ‘user’ doesn’t have access to page /admin. They can’t see it in the navbar, but if I go directly to https://[url]/admin, they’re able to access it
optimistic-gold
optimistic-gold11mo ago
Unfortunately, there is currently no direct connection between the access control provider and the routes in the current system. The connection is established based on resources and actions. I will discuss this with the core team to explore the possibility of supporting it out-of-the-box. Perhaps a component like <Authorized> could be created. In the meantime, you can use https://refine.dev/docs/api-reference/core/hooks/accessControl/useCan/ to check if the user has permission for that page. If not, you can manually redirect the user. Alternatively, you can handle this check in the authProvider.check function.
fascinating-indigo
fascinating-indigo11mo ago
I see, thanks! Not sure if I’ll go down that route since the performance degradation is significant enough to make note of inside the docs. The <authorized> component sounds like a good idea, I’ve been using <Authenticated> for the login side and it’s been working great
optimistic-gold
optimistic-gold11mo ago
you right. some kind of cache mechanism needed. If we request to API on every route change, it would be bad
fascinating-indigo
fascinating-indigo11mo ago
For sure, slight tangent but is there an easy way to follow new features? Just the GitHub channel?
optimistic-gold
optimistic-gold11mo ago
yes currently only guthub
fascinating-indigo
fascinating-indigo11mo ago
👍👍 Cool thanks!
optimistic-gold
optimistic-gold11mo ago
@avangeous hi again. you can wrap page component with this https://refine.dev/docs/api-reference/core/components/accessControl/can-access/#basic-usage and you can give <Redirect> component to fallback prop
fascinating-indigo
fascinating-indigo11mo ago
😮 awesome, ill try that now im assuming its isolated from Authenticated? (i.e if i have if wrapped in Authenticated right now i should wrap it in both)
optimistic-gold
optimistic-gold11mo ago
<Authenticated> uses useIsAuthenticated hook. it means uses authProvider.check function. <CanAccess> uses useCan hook. it means accessControlProvider.can function. yeh its isolated but it depends on your accessControl and authProvider implementation of course
fascinating-indigo
fascinating-indigo11mo ago
looks like it works, thank you! 😃
optimistic-gold
optimistic-gold11mo ago
🚀 I'm glad to hear that