@eduzz/miau-client 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
1
 
2
- > @eduzz/miau-client@1.0.2 build:types /home/runner/work/eduzz-miau/eduzz-miau/packages/client
2
+ > @eduzz/miau-client@1.0.3 build:types /home/runner/work/eduzz-miau/eduzz-miau/packages/client
3
3
  > tsc --emitDeclarationOnly --outDir dist
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { type RequestHandler } from 'express';
2
- import { type SecretEnv, type Permission } from '@eduzz/miau-types';
2
+ import { type SecretEnv, type Permission, type Resource } from '@eduzz/miau-types';
3
3
  import { type RequestAugmentation } from './middleware';
4
4
  type MiauClientConfig = {
5
5
  apiUrl: string;
@@ -11,8 +11,6 @@ export declare class MiauClient {
11
11
  private jwtToken;
12
12
  private jwksClient;
13
13
  private basicAuthToken;
14
- private permissionsCache;
15
- private permissionsRequests;
16
14
  constructor(config: MiauClientConfig);
17
15
  getEnvironment(): SecretEnv;
18
16
  getPublicKey(kid: string): Promise<string>;
@@ -21,10 +19,11 @@ export declare class MiauClient {
21
19
  requestAugmentation?: RequestAugmentation<T>;
22
20
  fallbackMiddleware?: RequestHandler;
23
21
  }): RequestHandler;
22
+ getResources(): Promise<Resource[]>;
24
23
  getPermissions(targetAppId: string): Promise<Permission>;
25
- private requestPermissions;
26
24
  private getApiJwtUrl;
27
25
  private getPermissionsUrl;
26
+ private getResourcesUrl;
28
27
  private getJwksUrl;
29
28
  }
30
29
  export {};
@@ -0,0 +1,3 @@
1
+ import { type Resource } from '@eduzz/miau-types';
2
+ export declare const wildcardToRegex: (pattern: string) => RegExp;
3
+ export declare const isResourceAllowed: (resource: Resource, resources: Resource[], permittedResources: Resource[]) => boolean;
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js CHANGED
@@ -11870,6 +11870,30 @@ var import_jwks_rsa = __toESM(require_src2());
11870
11870
 
11871
11871
  // src/middleware.ts
11872
11872
  var import_jsonwebtoken = __toESM(require_jsonwebtoken());
11873
+
11874
+ // src/functions.ts
11875
+ var wildcardToRegex = (pattern) => {
11876
+ const escaped = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&");
11877
+ const withWildcards = escaped.replace(/\*/g, "[^/]+");
11878
+ const regexStr = `^${withWildcards}$`;
11879
+ return new RegExp(regexStr);
11880
+ };
11881
+ var isResourceAllowed = (resource, resources, permittedResources) => {
11882
+ const hasExactMatch = resources.some((res) => {
11883
+ return res.method.toLowerCase() === resource.method.toLowerCase() && res.path.toLowerCase() === resource.path.toLowerCase();
11884
+ });
11885
+ if (hasExactMatch) {
11886
+ return permittedResources.some((perm) => {
11887
+ return perm.method.toLowerCase() === resource.method.toLowerCase() && perm.path.toLowerCase() === resource.path.toLowerCase();
11888
+ });
11889
+ }
11890
+ return permittedResources.some((permission) => {
11891
+ const permissionRegex = wildcardToRegex(permission.path);
11892
+ return permission.method.toLowerCase() === resource.method.toLowerCase() && permissionRegex.test(resource.path.toLowerCase());
11893
+ });
11894
+ };
11895
+
11896
+ // src/middleware.ts
11873
11897
  var HttpError = class _HttpError extends Error {
11874
11898
  constructor(status, name, message) {
11875
11899
  super(message);
@@ -11878,12 +11902,6 @@ var HttpError = class _HttpError extends Error {
11878
11902
  Object.setPrototypeOf(this, _HttpError.prototype);
11879
11903
  }
11880
11904
  };
11881
- var wildcardToRegex = (pattern) => {
11882
- const escaped = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&");
11883
- const withWildcards = escaped.replace(/\*/g, "[^/]+");
11884
- const regexStr = `^${withWildcards}$`;
11885
- return new RegExp(regexStr);
11886
- };
11887
11905
  var miauMiddleware = (miauClient, requestAugmentation, fallbackMiddleware) => {
11888
11906
  return async (req, res, next) => {
11889
11907
  try {
@@ -11908,14 +11926,20 @@ var miauMiddleware = (miauClient, requestAugmentation, fallbackMiddleware) => {
11908
11926
  `Secret environment ${secret.environment} does not match client environment ${miauClient.getEnvironment()}`
11909
11927
  );
11910
11928
  }
11929
+ const resources = await miauClient.getResources();
11930
+ if (!resources) {
11931
+ throw new HttpError(401, "Unauthorized", "No resources found for this application");
11932
+ }
11911
11933
  const permission = await miauClient.getPermissions(application.id);
11912
11934
  if (!permission) {
11913
11935
  throw new HttpError(401, "Unauthorized", "No permissions found for this application");
11914
11936
  }
11915
- const resources = permission?.resources || [];
11916
- const isAllowed = resources.some((resource) => {
11917
- return resource.method.toLowerCase() === req.method.toLowerCase() && wildcardToRegex(resource.path.toLowerCase()).test(req.path.toLowerCase());
11918
- });
11937
+ const permittedResources = permission?.resources || [];
11938
+ if (!permittedResources.length) {
11939
+ throw new HttpError(403, "Forbidden", "No resources are permitted for this application");
11940
+ }
11941
+ const resource = { protocol: "http", method: req.method, path: req.path };
11942
+ const isAllowed = isResourceAllowed(resource, resources, permittedResources);
11919
11943
  if (!isAllowed) {
11920
11944
  throw new HttpError(403, "Forbidden", `You do not have permission to access ${req.method} ${req.path}`);
11921
11945
  }
@@ -11938,6 +11962,7 @@ var miauMiddleware = (miauClient, requestAugmentation, fallbackMiddleware) => {
11938
11962
  };
11939
11963
 
11940
11964
  // src/MiauClient.ts
11965
+ var requestsCache = /* @__PURE__ */ new Map();
11941
11966
  var reusableFetch = async (input, init) => {
11942
11967
  return new Promise(async (resolve, reject) => {
11943
11968
  try {
@@ -11950,16 +11975,36 @@ var reusableFetch = async (input, init) => {
11950
11975
  }
11951
11976
  });
11952
11977
  };
11978
+ var cacheableFetch = async (input, init) => {
11979
+ const cacheKeyData = JSON.stringify({ input, init });
11980
+ const cacheKey = import_node_crypto.default.createHash("sha256").update(cacheKeyData).digest("hex");
11981
+ const cachedResponse = requestsCache.get(cacheKey);
11982
+ if (cachedResponse) {
11983
+ const { data, headers } = await cachedResponse;
11984
+ const expiresAt = expiresHeaderToUnixtime(headers.get("Expires"));
11985
+ if (expiresAt > Date.now()) {
11986
+ return data;
11987
+ }
11988
+ requestsCache.delete(cacheKey);
11989
+ }
11990
+ const newRequest = reusableFetch(input, init);
11991
+ requestsCache.set(cacheKey, newRequest);
11992
+ return (await newRequest).data;
11993
+ };
11994
+ var expiresHeaderToUnixtime = (expires) => {
11995
+ return expires ? new Date(expires).getTime() : Date.now() + 6e4;
11996
+ };
11953
11997
  var MiauClient = class {
11954
11998
  constructor(config) {
11955
- this.permissionsCache = /* @__PURE__ */ new Map();
11956
- this.permissionsRequests = /* @__PURE__ */ new Map();
11957
11999
  this.getApiJwtUrl = () => {
11958
12000
  return `${this.config.apiUrl}/v1/jwt`;
11959
12001
  };
11960
12002
  this.getPermissionsUrl = () => {
11961
12003
  return `${this.config.apiUrl}/v1/permissions`;
11962
12004
  };
12005
+ this.getResourcesUrl = () => {
12006
+ return `${this.config.apiUrl}/v1/resources`;
12007
+ };
11963
12008
  this.getJwksUrl = () => {
11964
12009
  return `${this.config.apiUrl}/v1/jwks.json`;
11965
12010
  };
@@ -12008,35 +12053,15 @@ var MiauClient = class {
12008
12053
  middleware(config) {
12009
12054
  return miauMiddleware(this, config?.requestAugmentation, config?.fallbackMiddleware);
12010
12055
  }
12011
- async getPermissions(targetAppId) {
12012
- if (this.permissionsCache.has(targetAppId)) {
12013
- const { data, expiresAt } = this.permissionsCache.get(targetAppId);
12014
- if (expiresAt > Date.now()) {
12015
- this.permissionsRequests.delete(targetAppId);
12016
- return data;
12017
- }
12018
- }
12019
- const request = await this.requestPermissions(targetAppId);
12020
- if (request !== void 0) {
12021
- const { data, headers } = request;
12022
- this.permissionsCache.set(targetAppId, {
12023
- data,
12024
- expiresAt: headers.has("Expires") ? new Date(headers.get("Expires")).getTime() : Date.now() + 6e4
12025
- // Default to 1 minute if no Expires header
12026
- });
12027
- return data;
12028
- }
12029
- throw new Error(`Failed to fetch permissions for app Id: ${targetAppId}`);
12056
+ async getResources() {
12057
+ return cacheableFetch(this.getResourcesUrl(), {
12058
+ headers: { Authorization: `Basic ${this.basicAuthToken}` }
12059
+ });
12030
12060
  }
12031
- async requestPermissions(sourceAppId) {
12032
- if (this.permissionsRequests.has(sourceAppId)) {
12033
- return this.permissionsRequests.get(sourceAppId);
12034
- }
12035
- const request = reusableFetch(`${this.getPermissionsUrl()}/${sourceAppId}`, {
12061
+ async getPermissions(targetAppId) {
12062
+ return cacheableFetch(`${this.getPermissionsUrl()}/${targetAppId}`, {
12036
12063
  headers: { Authorization: `Basic ${this.basicAuthToken}` }
12037
12064
  });
12038
- this.permissionsRequests.set(sourceAppId, request);
12039
- return request;
12040
12065
  }
12041
12066
  };
12042
12067
  // Annotate the CommonJS export names for ESM import in node: