@cat-factory/gitlab 0.4.26 → 0.4.28

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.
@@ -4,11 +4,17 @@ export interface GitLabIdentityResolverOptions {
4
4
  apiBase?: string;
5
5
  /** Injected for tests; defaults to the global `fetch`. */
6
6
  fetchImpl?: typeof fetch;
7
+ /** Optional sink, warned when group enumeration hits the page cap (see {@link resolveOrgs}). */
8
+ logger?: {
9
+ warn: (message: string) => void;
10
+ };
7
11
  }
8
12
  export declare class GitLabIdentityResolver implements VcsIdentityResolver {
9
13
  private readonly apiBase;
10
14
  private readonly fetchImpl;
15
+ private readonly logger?;
11
16
  constructor(options?: GitLabIdentityResolverOptions);
12
17
  resolveIdentity(token: string): Promise<VcsIdentity>;
18
+ resolveOrgs(token: string): Promise<string[]>;
13
19
  }
14
20
  //# sourceMappingURL=GitLabIdentityResolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GitLabIdentityResolver.d.ts","sourceRoot":"","sources":["../src/GitLabIdentityResolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAiB3E,MAAM,WAAW,6BAA6B;IAC5C,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CACzB;AAED,qBAAa,sBAAuB,YAAW,mBAAmB;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IAExC,YAAY,OAAO,GAAE,6BAAkC,EAGtD;IAEK,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAwBzD;CACF"}
1
+ {"version":3,"file":"GitLabIdentityResolver.d.ts","sourceRoot":"","sources":["../src/GitLabIdentityResolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAsB3E,MAAM,WAAW,6BAA6B;IAC5C,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,gGAAgG;IAChG,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAA;CAC7C;AAOD,qBAAa,sBAAuB,YAAW,mBAAmB;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAqC;IAE7D,YAAY,OAAO,GAAE,6BAAkC,EAItD;IAEK,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAwBzD;IAoBK,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA8BlD;CACF"}
@@ -1,10 +1,16 @@
1
1
  import { GITLAB_PUBLIC_API_BASE } from './tokenSource.js';
2
+ // Bound on group pagination for the PAT-login org allowlist: ~1000 groups (mirrors
3
+ // `FetchGitLabClient`'s cap). A user in more groups than this whose only allowlisted group
4
+ // falls past the cap would be wrongly denied, so we `logger.warn` when we truncate.
5
+ const MAX_PAGES = 10;
2
6
  export class GitLabIdentityResolver {
3
7
  apiBase;
4
8
  fetchImpl;
9
+ logger;
5
10
  constructor(options = {}) {
6
11
  this.apiBase = (options.apiBase ?? GITLAB_PUBLIC_API_BASE).replace(/\/+$/, '');
7
12
  this.fetchImpl = options.fetchImpl ?? fetch;
13
+ this.logger = options.logger;
8
14
  }
9
15
  async resolveIdentity(token) {
10
16
  const res = await this.fetchImpl(`${this.apiBase}/user`, {
@@ -31,5 +37,64 @@ export class GitLabIdentityResolver {
31
37
  email: user.email ?? null,
32
38
  };
33
39
  }
40
+ // The GitLab groups the PAT's account is a member of (lowercased full paths), for a hosted
41
+ // facade's PAT-login org allowlist — the GitLab analogue of GitHub's `resolveOrgs`. Without
42
+ // this, a hosted deployment that admits users by group membership (`AUTH_ALLOWED_ORGS`) could
43
+ // NOT admit a GitLab user (the org branch of `isPatIdentityAllowed` is skipped when the
44
+ // resolver can't enumerate orgs), so GitLab couldn't be a primary auth identity there.
45
+ //
46
+ // `min_access_level=10` (Guest) restricts the listing to groups the user actually BELONGS to,
47
+ // never the public groups `/groups` would otherwise return — admission must not widen to a
48
+ // group the user merely can see. The identifier is the group's `full_path` (globally unique,
49
+ // and it encodes subgroups, e.g. `acme/platform`), so an operator lists GitLab group full paths
50
+ // in `AUTH_ALLOWED_ORGS`. Membership is not path-inherited upward, so only the groups the API
51
+ // returns admit — matching GitHub, where only orgs the token can see appear.
52
+ //
53
+ // The account needs a token scope that can read groups (`read_api`/`api`); a narrower token
54
+ // (e.g. `read_user`-only) makes GitLab reject this with a non-2xx, which we surface by throwing
55
+ // so the caller can distinguish "insufficient scope" from "no qualifying group". We follow
56
+ // `Link: rel="next"` pagination up to {@link MAX_PAGES} so a user in many groups is not denied
57
+ // just because their allowlisted group sat on a later page.
58
+ async resolveOrgs(token) {
59
+ const all = [];
60
+ let url = `${this.apiBase}/groups?min_access_level=10&per_page=100`;
61
+ let page = 0;
62
+ for (; url && page < MAX_PAGES; page++) {
63
+ const res = await this.fetchImpl(url, {
64
+ headers: {
65
+ 'private-token': token,
66
+ accept: 'application/json',
67
+ 'user-agent': 'cat-factory',
68
+ },
69
+ });
70
+ if (!res.ok) {
71
+ const text = await res.text().catch(() => '');
72
+ throw new Error(`GitLab /groups failed (HTTP ${res.status}): ${text.slice(0, 200)}`);
73
+ }
74
+ const groups = (await res.json());
75
+ for (const group of groups) {
76
+ const path = group.full_path ?? group.path;
77
+ if (path)
78
+ all.push(path.toLowerCase());
79
+ }
80
+ url = parseNextLink(res.headers.get('link'));
81
+ }
82
+ if (url) {
83
+ this.logger?.warn(`GitLab group listing truncated at MAX_PAGES=${MAX_PAGES} (~${100 * MAX_PAGES} groups) ` +
84
+ 'during PAT-login org admission; a user in more groups may be wrongly denied.');
85
+ }
86
+ return all;
87
+ }
88
+ }
89
+ /** Extract the `rel="next"` URL from a `Link` header, if any (GitLab keyset/offset pagination). */
90
+ function parseNextLink(link) {
91
+ if (!link)
92
+ return undefined;
93
+ for (const part of link.split(',')) {
94
+ const match = part.match(/<([^>]+)>\s*;\s*rel="next"/);
95
+ if (match)
96
+ return match[1];
97
+ }
98
+ return undefined;
34
99
  }
35
100
  //# sourceMappingURL=GitLabIdentityResolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GitLabIdentityResolver.js","sourceRoot":"","sources":["../src/GitLabIdentityResolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAuBzD,MAAM,OAAO,sBAAsB;IAChB,OAAO,CAAQ;IACf,SAAS,CAAc;IAExC,YAAY,OAAO,GAAkC,EAAE;QACrD,IAAI,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,sBAAsB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QAC9E,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAA;IAC7C,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,KAAa;QACjC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,OAAO,OAAO,EAAE;YACvD,OAAO,EAAE;gBACP,eAAe,EAAE,KAAK;gBACtB,MAAM,EAAE,kBAAkB;gBAC1B,YAAY,EAAE,aAAa;aAC5B;SACF,CAAC,CAAA;QACF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;YAC7C,MAAM,IAAI,KAAK,CAAC,6BAA6B,GAAG,CAAC,MAAM,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;QACpF,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAA;QACrD,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;QACD,OAAO;YACL,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,KAAK,EAAE,IAAI,CAAC,QAAQ;YACpB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;YACvB,SAAS,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI;YAClC,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI;SAC1B,CAAA;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"GitLabIdentityResolver.js","sourceRoot":"","sources":["../src/GitLabIdentityResolver.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/gitlab",
3
- "version": "0.4.26",
3
+ "version": "0.4.28",
4
4
  "description": "Opt-in GitLab VCS provider for cat-factory. Implements the provider-neutral VcsClient / webhook / provisioning ports against the GitLab REST v4 API and registers itself via registerVcsProvider('gitlab'). Depends only on @cat-factory/kernel + @cat-factory/contracts.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,8 +24,8 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "@cat-factory/contracts": "0.75.0",
28
- "@cat-factory/kernel": "0.66.0"
27
+ "@cat-factory/kernel": "0.66.1",
28
+ "@cat-factory/contracts": "0.76.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "typescript": "7.0.1-rc",