ntgussoni / blitz-guard Goto Github PK
View Code? Open in Web Editor NEWBlitz Guard - The centralized permission based authorization for Blitz.js
Home Page: https://ntgussoni.github.io/blitz-guard
License: MIT License
Blitz Guard - The centralized permission based authorization for Blitz.js
Home Page: https://ntgussoni.github.io/blitz-guard
License: MIT License
I've migrated one of my applications to Blitz 0.44.4
and now I'm getting some TS errors in the blitz.config.ts
file for BlitzGuardMiddleware
.
I doubt it's critical but figured I'd bring it up.
THIS STEP IS COMING SHORTLY
macOS Monterey | darwin-arm64 | Node: v16.13.1
blitz: 0.44.4 (local)
Package manager: yarn
System:
OS: macOS 12.2
CPU: (10) arm64 Apple M1 Max
Memory: 267.31 MB / 32.00 GB
Shell: 5.8 - /bin/zsh
Binaries:
Node: 16.13.1 - ~/.nvm/versions/node/v16.13.1/bin/node
Yarn: 1.22.17 - ~/.nvm/versions/node/v17.2.0/bin/yarn
npm: 8.1.2 - ~/.nvm/versions/node/v16.13.1/bin/npm
Watchman: Not Found
npmPackages:
@prisma/client: 2.26.0 => 2.26.0
blitz: 0.44.4 => 0.44.4
prisma: 2.26.0 => 2.26.0
react: 18.0.0-beta-149b420f6-20211119 => 18.0.0-beta-149b420f6-20211119
react-dom: 18.0.0-beta-149b420f6-20211119 => 18.0.0-beta-149b420f6-20211119
typescript: 4.5.2 => 4.5.2
TypeScript error
(alias) BlitzGuardMiddleware({ excluded }: configType): (req: any, res: {
blitzCtx: Ctx & {
__securedByGuard: boolean;
};
}, next: () => any) => Promise<any>
import BlitzGuardMiddleware
Type '(req: any, res: { blitzCtx: Ctx & { __securedByGuard: boolean; }; }, next: () => any) => Promise<any>' is not assignable to type 'Middleware'.
Types of parameters 'res' and 'res' are incompatible.
Type 'MiddlewareResponse<Ctx>' is not assignable to type '{ blitzCtx: Ctx & { __securedByGuard: boolean; }; }'.
Types of property 'blitzCtx' are incompatible.
Type 'Ctx' is not assignable to type 'Ctx & { __securedByGuard: boolean; }'.
Property '__securedByGuard' is missing in type 'Ctx' but required in type '{ __securedByGuard: boolean; }'.ts(2322)
middleware.d.ts(7, 9): '__securedByGuard' is declared here.
Blitz config
import { BlitzGuardMiddleware } from "@blitz-guard/core/dist/middleware"
import { BlitzConfig, sessionMiddleware, simpleRolesIsAuthorized } from "blitz"
const config: BlitzConfig = {
poweredByHeader: false,
middleware: [
sessionMiddleware({
cookiePrefix: "dummy",
isAuthorized: simpleRolesIsAuthorized,
}),
BlitzGuardMiddleware({ // <---- Error start
excluded: [
"/api/auth/mutation/login",
"/api/auth/mutation/logout",
"/api/users/queries/getUser",
"/api/guard/queries/getAbility",
],
}), // <---- Error end
],
}
module.exports = config
Got this error:
#12 2.841 npm ERR! Could not resolve dependency:
#12 2.841 npm ERR! peer blitz@"^0.38.6" from @blitz-guard/[email protected]
In blitz-guard/package.json
we require "blitz": "^0.38.6"
. I guess it would be sufficient to add ^0.38.6 || ^2.0.0
or something like this?
We have migrated from 0.3.1 to 0.4.1 (we're very happy about the new authorizePipe!)
When running with blitz dev
everything works fine. Building with blitz build
throws an error:
TypeError: Class extends value undefined is not a constructor or null
The offending code seems to be this:
var GuardAuthorizationError = /** @class */ (function (_super) {
tslib.__extends(GuardAuthorizationError, _super);
function GuardAuthorizationError(_a) {
var ability = _a.ability, resource = _a.resource, reason = _a.reason;
var _this = _super.call(this, reason || "GUARD: UNAUTHORIZED") || this;
_this.name = "GuardAuthorizationError";
_this.rule = { ability: ability, resource: resource };
return _this;
}
return GuardAuthorizationError;
}(blitz.AuthorizationError));
Particularly tslib.__extends(GuardAuthorizationError, _super);
macOS Mojave | darwin-x64 | Node: v14.17.5
blitz: 0.39.0 (global)
blitz: 0.37.0 (local)
Package manager: yarn
System:
OS: macOS Mojave 10.14.6
CPU: (4) x64 Intel(R) Core(TM) i7-7660U CPU @ 2.50GHz
Memory: 2.57 GB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 14.17.5 - ~/.nvm/versions/node/v14.17.5/bin/node
Yarn: 1.22.10 - /usr/local/bin/yarn
npm: 6.14.14 - ~/.nvm/versions/node/v14.17.5/bin/npm
Watchman: Not Found
npmPackages:
@prisma/client: 2.29.1 => 2.29.1
blitz: 0.37.0 => 0.37.0
prisma: 2.29.1 => 2.29.1
react: 0.0.0-experimental-6a589ad71 => 0.0.0-experimental-6a589ad71
react-dom: 0.0.0-experimental-6a589ad71 => 0.0.0-experimental-6a589ad71
typescript: ~4.2 => 4.2.4
Add the blitz guard middleware through the recipe, ask the user if they want it and modify blitz.config.js with jscodeshift
This article describes ability files clearly: https://ntgussoni.github.io/blitz-guard/docs/ability-file
The examples are all based on a user's authorization status or explicit ownership of a resource.
Other common patterns include: (1) permissions are assigned to roles, and roles are assigned to users -- each user's permissions are the net of all permissions added from all roles, with no permissions subtracted by a role; (2) permissions are conditioned on recurring data patterns, e.g. users are granted roles per-tenant, or users have permissions based on direct or indirect ownership of a record, or user A can delegate permissions to other users that are a subset of user A's permissions.
These would be data-driven approaches to ability files: rules that can't be coded in advance, but are applied dynamically at run time based on user enabled / disabled status; user membership in tenants; permissions granted to tenant-specific roles; etc.
Just wondering if there are example of this approach.
If there aren't examples of this, I could probably provide them.
Nice v1 tool.
The current example app serves as an internal testing platform, but it's not something to expose to users or devs.
I would like the ability to add an array of resources or have instead of calling the can
function for each resource for the same ability.
Internally I am not sure how this library works, but I wrote my own function (untested) to do the same thing.
const cans = (
ability: string,
resources: Prisma.ModelName[],
can: _CanType<Prisma.ModelName, any>,
guard?: (args: any, resource: Prisma.ModelName) => Promise<boolean>
): void => {
resources.forEach((resource) =>
guard ? can(ability, resource, (args) => guard(args, resource)) : can(ability, resource)
)
}
and in usage, I would have this
cans("create", mainResources, can)
vs. having to write this
can("create", "Calendar")
can("create", "Event")
can("create", "Group")
can("create", "Media")
Obviously, I tailored the function to my specific use case, but hopefully, it should shed some light on what I would like added as a core feature. I'm more than happy to help with this, so let me know what you think.
When you have many rules, it's usually difficult to debug which one caused the exception.
We could change the abilities' can
and cannot
to also expose an method to add an explanation.
An idea could be:
//app/guard/ability
can('create','article', () => ctx.user.isAuthorized()).reason('user is not authorized');
const res = Guard.can('create','article', ..., ...) // false
res.reason // 'user is not authorized'
// some mutation or query
const res = Guard.can('create','article', ..., ...) // false
res.reason // 'user is not authorized'
Guard.authorize('create','article', createArticleMutation) // raises exception AuthorizationError("user is not authorized")
I am getting a 403 on the getCurrentUser response, eventhough the ability was granted.
It works when I use kebab-case.
"currentUser"
)can("read", "currentUser")
)Guard.authorize("read", "currentUser", getCurrentUser)
)macOS Catalina | darwin-x64 | Node: v12.18.0
blitz: 0.29.2 (global)
blitz: 0.29.2 (local)
Package manager: npm
System:
OS: macOS 10.15.6
CPU: (16) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 18.50 GB / 64.00 GB
Shell: 5.7.1 - /bin/zsh
Binaries:
Node: 12.18.0 - ~/.nvm/versions/node/v12.18.0/bin/node
Yarn: Not Found
npm: 6.14.10 - ~/.nvm/versions/node/v12.18.0/bin/npm
Watchman: Not Found
npmPackages:
@prisma/cli: ~2.14 => 2.14.0
@prisma/client: ~2.14 => 2.14.0
blitz: 0.29.2 => 0.29.2
react: 0.0.0-experimental-3310209d0 => 0.0.0-experimental-3310209d0
react-dom: 0.0.0-experimental-3310209d0 => 0.0.0-experimental-3310209d0
typescript: 4.1.3 => 4.1.3
I'll be able to write a failing test case for this later and maybe submit a PR to fix this issue.
While running blitz install ntgussoni/blitz-guard-recipe
I have received the following error message
(node:15026) UnhandledPromiseRejectionWarning: Error: At least one choice must be selectable
at AutoComplete.reset (/Users/marcusreinhardt/Development/sla-repo/.blitz/recipe-install/node_modules/enquirer/lib/types/array.js:38:13)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at AutoComplete.initialize (/Users/marcusreinhardt/Development/sla-repo/.blitz/recipe-install/node_modules/enquirer/lib/types/array.js:25:5)
at /Users/marcusreinhardt/Development/sla-repo/.blitz/recipe-install/node_modules/enquirer/lib/prompt.js:236:7
(Use `node --trace-warnings ...` to show where the warning was created)
(node:15026) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:15026) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
+–––––––––––––––––––––––+
⎪ Add Ability File ⎪
+–––––––––––––––––––––––+
One file to rule them all, this is the main file where your rules will live. Creating that for you now.
IDENTICAL app/guard/ability.ts
Press ENTER to continue
macOS | darwin-x64 | Node: v14.16.0
blitz: 0.36.4 (global)
blitz: 0.36.4 (local)
Package manager: yarn
System:
OS: macOS 11.4
CPU: (4) x64 Intel(R) Core(TM) i7-7567U CPU @ 3.50GHz
Memory: 510.70 MB / 16.00 GB
Shell: 5.7.1 - /usr/local/bin/zsh
Binaries:
Node: 14.16.0 - /usr/local/bin/node
Yarn: 1.22.5 - ~/.yarn/bin/yarn
npm: 6.9.0 - ~/.config/yarn/global/node_modules/.bin/npm
Watchman: Not Found
npmPackages:
@prisma/client: ~2.20 => 2.20.1
blitz: 0.36.4 => 0.36.4
prisma: ~2.20 => 2.20.1
react: 0.0.0-experimental-6a589ad71 => 0.0.0-experimental-6a589ad71
react-dom: 0.0.0-experimental-6a589ad71 => 0.0.0-experimental-6a589ad71
typescript: ~4.2 => 4.2.4
----
macOS Big Sur | darwin-x64 | Node: v15.11.0
blitz: 0.37.0 (global)
blitz: 0.37.0 (local)
Package manager: npm
System:
OS: macOS 11.4
CPU: (8) x64 Intel(R) Core(TM) i5-8279U CPU @ 2.40GHz
Memory: 727.41 MB / 16.00 GB
Shell: 5.8 - /bin/zsh
Binaries:
Node: 15.11.0 - /usr/local/bin/node
Yarn: 1.22.10 - /usr/local/bin/yarn
npm: 7.6.2 - /usr/local/bin/npm
Watchman: Not Found
npmPackages:
@prisma/client: 2.25.0 => 2.25.0
blitz: 0.37.0 => 0.37.0
prisma: 2.25.0 => 2.25.0
react: 0.0.0-experimental-0eea57724 => 0.0.0-experimental-0eea57724
react-dom: 0.0.0-experimental-0eea57724 => 0.0.0-experimental-0eea57724
typescript: ~4.3.2 => 4.3.2
Please include applicable logs and screenshots that show your problem.
We can ask the user if they want to start with a cannot('manage', 'all')
by default.
Right now Guard.authorize()
just throws an authorization error without any context what makes it hard to debug (#18) and it also hurts UX, because we can not provide detailed info to a user.
So for example I can not check if access was rejected, because user email is not verified or user just not allowed to access this api. Right now I have to use getAbility
query in order to display a correct message for a user.
Right now it will just throw a hardcoded AuthorizationError
. We can extend this error to a custom one with more detailed info. More info about custom blitz errors here.
type GaurdAuthErrorProps = {
ability: string;
resource: string;
reason?: string;
};
class GuardAuthorizationError extends AuthorizationError {
rule;
constructor({ ability, resource, reason }: GaurdAuthErrorProps) {
super();
this.message = reason || "GUARD: UNAUTHORIZED";
this.rule = { ability, resource };
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.