mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
Add new oauth matching function with tests for it
This commit is contained in:
parent
930b84826b
commit
158f86c475
95
tests/oauth-scopes.test.ts
Normal file
95
tests/oauth-scopes.test.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { checkIfOauthIsValid } from "@oauth";
|
||||||
|
import { describe, expect, it } from "bun:test";
|
||||||
|
|
||||||
|
describe("checkIfOauthIsValid", () => {
|
||||||
|
it("should return true when routeScopes and application.scopes are empty", () => {
|
||||||
|
const application = { scopes: "" };
|
||||||
|
const routeScopes: string[] = [];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when routeScopes is empty and application.scopes contains write:* or write", () => {
|
||||||
|
const application = { scopes: "write:*" };
|
||||||
|
const routeScopes: string[] = [];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when routeScopes is empty and application.scopes contains read:* or read", () => {
|
||||||
|
const application = { scopes: "read:*" };
|
||||||
|
const routeScopes: string[] = [];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when routeScopes contains only write: permissions and application.scopes contains write:* or write", () => {
|
||||||
|
const application = { scopes: "write:*" };
|
||||||
|
const routeScopes = ["write:users", "write:posts"];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when routeScopes contains only read: permissions and application.scopes contains read:* or read", () => {
|
||||||
|
const application = { scopes: "read:*" };
|
||||||
|
const routeScopes = ["read:users", "read:posts"];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when routeScopes contains both write: and read: permissions and application.scopes contains write:* or write and read:* or read", () => {
|
||||||
|
const application = { scopes: "write:* read:*" };
|
||||||
|
const routeScopes = ["write:users", "read:posts"];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when routeScopes contains write: permissions but application.scopes does not contain write:* or write", () => {
|
||||||
|
const application = { scopes: "read:*" };
|
||||||
|
const routeScopes = ["write:users", "write:posts"];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when routeScopes contains read: permissions but application.scopes does not contain read:* or read", () => {
|
||||||
|
const application = { scopes: "write:*" };
|
||||||
|
const routeScopes = ["read:users", "read:posts"];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when routeScopes contains both write: and read: permissions but application.scopes does not contain write:* or write and read:* or read", () => {
|
||||||
|
const application = { scopes: "" };
|
||||||
|
const routeScopes = ["write:users", "read:posts"];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when routeScopes contains a mix of valid and invalid permissions and application.scopes contains all the required permissions", () => {
|
||||||
|
const application = { scopes: "write:* read:*" };
|
||||||
|
const routeScopes = ["write:users", "invalid:permission", "read:posts"];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when routeScopes contains a mix of valid and invalid permissions but application.scopes does not contain all the required permissions", () => {
|
||||||
|
const application = { scopes: "write:*" };
|
||||||
|
const routeScopes = ["write:users", "invalid:permission", "read:posts"];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when routeScopes contains a mix of valid write and read permissions and application.scopes contains all the required permissions", () => {
|
||||||
|
const application = { scopes: "write:* read:posts" };
|
||||||
|
const routeScopes = ["write:users", "read:posts"];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when routeScopes contains a mix of valid write and read permissions but application.scopes does not contain all the required permissions", () => {
|
||||||
|
const application = { scopes: "write:*" };
|
||||||
|
const routeScopes = ["write:users", "read:posts"];
|
||||||
|
const result = checkIfOauthIsValid(application as any, routeScopes);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
61
utils/oauth.ts
Normal file
61
utils/oauth.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { Application } from "@prisma/client";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an OAuth application is valid for a route
|
||||||
|
* @param application The OAuth application
|
||||||
|
* @param routeScopes The scopes required for the route
|
||||||
|
* @returns Whether the OAuth application is valid for the route
|
||||||
|
*/
|
||||||
|
export const checkIfOauthIsValid = (
|
||||||
|
application: Application,
|
||||||
|
routeScopes: string[]
|
||||||
|
) => {
|
||||||
|
if (routeScopes.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasAllWriteScopes =
|
||||||
|
application.scopes.split(" ").includes("write:*") ||
|
||||||
|
application.scopes.split(" ").includes("write");
|
||||||
|
|
||||||
|
const hasAllReadScopes =
|
||||||
|
application.scopes.split(" ").includes("read:*") ||
|
||||||
|
application.scopes.split(" ").includes("read");
|
||||||
|
|
||||||
|
if (hasAllWriteScopes && hasAllReadScopes) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let nonMatchedScopes = routeScopes;
|
||||||
|
|
||||||
|
if (hasAllWriteScopes) {
|
||||||
|
// Filter out all write scopes as valid
|
||||||
|
nonMatchedScopes = routeScopes.filter(
|
||||||
|
scope => !scope.startsWith("write:")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasAllReadScopes) {
|
||||||
|
// Filter out all read scopes as valid
|
||||||
|
nonMatchedScopes = routeScopes.filter(
|
||||||
|
scope => !scope.startsWith("read:")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are still scopes left, check if they match
|
||||||
|
// If there are no scopes left, return true
|
||||||
|
if (nonMatchedScopes.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are scopes left, check if they match
|
||||||
|
if (
|
||||||
|
nonMatchedScopes.every(scope =>
|
||||||
|
application.scopes.split(" ").includes(scope)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue