alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
351 lines (279 loc) • 9.18 kB
text/typescript
import { Alepha, ContainerLockedError } from "alepha";
import { describe, expect, it } from "vitest";
import {
InvalidPermissionError,
JwtProvider,
SecurityError,
SecurityProvider,
} from "../index.ts";
describe("SecurityProvider", () => {
it("should check permissions with default role", () => {
const sec = Alepha.create().inject(SecurityProvider);
sec.createPermission("hey");
sec.createRole({
name: "user",
permissions: [{ name: "hey" }],
});
expect(sec.can("user", "hey")).toEqual(true);
expect(sec.can("user", "noop")).toEqual(false);
expect(() => sec.can("noop", "hey")).toThrow(SecurityError);
});
it("should handle duplicate permissions", () => {
const app = Alepha.create();
const security = app.inject(SecurityProvider);
expect(security.getPermissions().length).toEqual(0);
security.createPermission("something:else");
expect(security.getPermissions().length).toEqual(1);
security.createPermission("something:else");
expect(security.getPermissions().length).toEqual(1);
});
it("should throw error for invalid permissions", () => {
const app = Alepha.create();
const security = app.inject(SecurityProvider);
const invalid = [
"",
":",
":hello",
"hello:",
"hello:world:",
"hello::world",
"::hello",
"hello::",
"@@@@@...@@@@",
{ name: "" },
{ name: "*/*zd" },
];
for (const permission of invalid) {
expect(() => security.createPermission(permission)).toThrow(
InvalidPermissionError,
);
}
});
it("should get permissions filtered by user roles", () => {
const app = Alepha.create();
const sec = app.inject(SecurityProvider);
sec.createPermission("hello:hey");
sec.createPermission("hello:ho");
sec.createPermission("hi");
const user = sec.createRole({
name: "user",
permissions: [{ name: "hello:hey" }],
});
sec.createRole({
name: "dude",
permissions: [{ name: "hi" }],
});
sec.createRole({
name: "superuser",
permissions: [{ name: "hello:*" }],
});
expect(sec.getPermissions().length).toEqual(3);
expect(sec.getPermissions({ roles: ["admin"] })).toEqual([
{ group: "hello", name: "hey" },
{ group: "hello", name: "ho" },
{ name: "hi" },
]);
expect(() => sec.getPermissions({ roles: ["xxx"] }).length).toThrow(Error);
expect(sec.getPermissions({ roles: ["superuser"] }).length).toEqual(2);
expect(sec.getPermissions({ roles: ["user"] })).toEqual([
{ group: "hello", name: "hey" },
]);
expect(sec.getPermissions({ roles: ["dude"] })).toEqual([{ name: "hi" }]);
expect(sec.getPermissions({ roles: [user] }).length).toEqual(1);
expect(sec.getPermissions({ roles: [] }).length).toEqual(0);
});
it("should check permissions with groups and wildcards", async () => {
const app = Alepha.create({
env: {
SERVER_SECURITY_ENABLED: true,
},
});
const sec = app.inject(SecurityProvider);
const permission = sec.createPermission("hello:hey");
expect(() => sec.createPermission("@@@@@...@@@@")).toThrow(
InvalidPermissionError,
);
await app.start();
expect(() => sec.createPermission("hello:*")).toThrow(ContainerLockedError);
expect(permission.name).toEqual("hey");
expect(permission.group).toEqual("hello");
sec.createRole({
name: "user",
permissions: [{ name: sec.permissionToString(permission) }],
});
sec.createRole({
name: "superuser",
permissions: [{ name: "hello:*" }],
});
expect(sec.can("user", "hello:hey")).toEqual(true);
expect(sec.can("superuser", "hello:hey")).toEqual(true);
expect(sec.can("superuser", "hello:*")).toEqual(true);
expect(sec.can("superuser", "*")).toEqual(false);
expect(sec.can("admin", "*")).toEqual(true);
expect(sec.can("superuser", "wrong:hey")).toEqual(false);
// Should this be false? It's not a valid permission
expect(sec.can("superuser", "hello:ho")).toEqual(true);
expect(sec.can("user", "noop")).toEqual(false);
});
it("should check permission with ownership", async () => {
const app = Alepha.create();
const sec = app.inject(SecurityProvider);
const user = {
name: "user",
permissions: [{ name: "hello:hey", ownership: true }],
};
const superuser = {
name: "superuser",
permissions: [{ name: "hello:*" }],
};
sec.createPermission("hello:hey");
sec.createRole(user);
sec.createRole(superuser);
expect(sec.checkPermission("hello:hey", "user")).toEqual({
ownership: true,
isAuthorized: true,
});
expect(sec.checkPermission("hello:hey", "user", "superuser")).toEqual({
ownership: false,
isAuthorized: true,
});
expect(sec.checkPermission("hello:hey", "superuser", "user")).toEqual({
ownership: false,
isAuthorized: true,
});
expect(sec.checkPermission("hello:hey", "superuser")).toEqual({
ownership: false,
isAuthorized: true,
});
});
it("should create user from JWT token with default realm", async () => {
const alepha = Alepha.create();
const jwt = alepha.inject(JwtProvider);
const sec = alepha.inject(SecurityProvider);
const userPermission = "hi";
const userRole = {
name: "user",
permissions: [{ name: userPermission }],
};
sec.createPermission(userPermission);
sec.createRole(userRole);
await alepha.start(); // lock!
const token = await jwt.create({
sub: "123",
roles: ["user"],
});
const user = await sec.createUserFromToken(token, {
permission: userPermission,
});
expect(user.id).toEqual("123");
expect(user.roles).toEqual([userRole.name]);
expect(user.token).toEqual(token);
expect(user.realm).toEqual("default");
expect(user.ownership).toEqual(false);
await expect(() =>
sec.createUserFromToken(token, {
permission: "noop",
}),
).rejects.toThrow(SecurityError);
await expect(() =>
jwt.create(
{
sub: "123",
roles: [],
},
"noop",
),
).rejects.toThrow(Error);
const emptyToken = await jwt.create(
{
sub: "123",
roles: [],
},
"default",
);
await expect(() =>
sec.createUserFromToken(emptyToken, { permission: userPermission }),
).rejects.toThrow(SecurityError);
});
it("should create roles and permissions in multiple realms", async () => {
const app = Alepha.create();
const sec = app.inject(SecurityProvider);
sec.createRealm({ name: "customers", roles: [] });
sec.createRealm({ name: "operators", roles: [] });
sec.createPermission("customers:get");
sec.createPermission("operators:get");
sec.createRole(
{
name: "customer",
permissions: [{ name: "customers:get", ownership: true }],
},
"customers",
);
expect(() =>
sec.createRole({ name: "operator", permissions: [] }, "noop"),
).toThrow(Error);
sec.createRole(
{
name: "operator",
permissions: [
{ name: "customers:get" },
{
name: "operators:get",
},
],
},
"operators",
);
expect(sec.getRoles("customers").length).toEqual(1);
expect(sec.getRoles("operators").length).toEqual(1);
expect(sec.getRoles().length).toEqual(2);
const userPermission = "hi";
const userRole = {
name: "user",
permissions: [{ name: userPermission }],
};
sec.createPermission(userPermission);
sec.createRole(userRole);
expect(() =>
sec.createRole(
{ name: "operator", permissions: [{ name: "@@@invalid" }] },
"operators",
),
).toThrow(Error);
await app.start(); // lock!
// add role with invalid permission => should throw
expect(() =>
sec.createRole(
{ name: "operator", permissions: [{ name: "noop" }] },
"operators",
),
).toThrow(Error);
});
it("should support exclude permissions", () => {
const sec = Alepha.create().inject(SecurityProvider);
const group = "message";
sec.createPermission({ name: "new", group });
sec.createPermission({ name: "edit", group });
sec.createPermission({ name: "delete", group });
sec.createRole({
name: "admin",
permissions: [{ name: "*" }],
});
sec.createRole({
name: "admin low tier",
permissions: [{ name: "*", exclude: [`${group}:delete`] }],
});
sec.createRole({
name: "moderator",
permissions: [{ name: `${group}:*` }],
});
sec.createRole({
name: "user",
permissions: [{ name: `${group}:*`, exclude: [`${group}:delete`] }],
});
expect(sec.can("admin", "message:delete")).toEqual(true);
expect(sec.can("admin low tier", "message:delete")).toEqual(false);
expect(sec.can("moderator", "message:delete")).toEqual(true);
expect(sec.can("user", "message:delete")).toEqual(false);
});
});