Refactor policies document
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<83cc00d864e6388ef2156203499da849>>
|
||||
* @generated SignedSource<<76057cc6f78c32b15c6e580c7a0c30da>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -124,13 +124,6 @@ return {
|
||||
"name": "expiresAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "lastUsedAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -194,12 +187,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "14e698e7b6edf0afe9cf3398986cd529",
|
||||
"cacheID": "cf6f6f621bdcb1bd24a2dd94f856729f",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "APIKeysPageQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query APIKeysPageQuery {\n viewer {\n ...PersonalAPIKeyListFragment\n id\n }\n}\n\nfragment PersonalAPIKeyListFragment on Identity {\n id\n personalAPIKeys(first: 1000) {\n edges {\n node {\n id\n name\n createdAt\n expiresAt\n lastUsedAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
|
||||
"text": "query APIKeysPageQuery {\n viewer {\n ...PersonalAPIKeyListFragment\n id\n }\n}\n\nfragment PersonalAPIKeyListFragment on Identity {\n id\n personalAPIKeys(first: 1000) {\n edges {\n node {\n id\n ...PersonalAPIKeyRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment PersonalAPIKeyRowFragment on PersonalAPIKey {\n id\n name\n createdAt\n expiresAt\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<875eae6f5455a17a1bb8e0590c7acdb0>>
|
||||
* @generated SignedSource<<faeeb8835f77a5251a0311dbac21bfde>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -12,7 +12,6 @@ import { ConcreteRequest } from 'relay-runtime';
|
||||
export type CreatePersonalAPIKeyInput = {
|
||||
expiresAt: any;
|
||||
name: string;
|
||||
organizationIds: ReadonlyArray<string>;
|
||||
};
|
||||
export type PersonalAPIKeyListCreateMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
@@ -25,7 +24,6 @@ export type PersonalAPIKeyListCreateMutation$data = {
|
||||
readonly createdAt: any;
|
||||
readonly expiresAt: any;
|
||||
readonly id: string;
|
||||
readonly lastUsedAt: any | null | undefined;
|
||||
readonly name: string;
|
||||
};
|
||||
};
|
||||
@@ -98,13 +96,6 @@ v3 = {
|
||||
"kind": "ScalarField",
|
||||
"name": "expiresAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "lastUsedAt",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
@@ -187,16 +178,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "5b1f6109730bd9f261afd6635d568b59",
|
||||
"cacheID": "1ec09abe9f3a61e13ba2deacf1a273fb",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PersonalAPIKeyListCreateMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation PersonalAPIKeyListCreateMutation(\n $input: CreatePersonalAPIKeyInput!\n) {\n createPersonalAPIKey(input: $input) {\n personalAPIKeyEdge {\n node {\n id\n name\n createdAt\n expiresAt\n lastUsedAt\n }\n }\n token\n }\n}\n"
|
||||
"text": "mutation PersonalAPIKeyListCreateMutation(\n $input: CreatePersonalAPIKeyInput!\n) {\n createPersonalAPIKey(input: $input) {\n personalAPIKeyEdge {\n node {\n id\n name\n createdAt\n expiresAt\n }\n }\n token\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "c7e932ad41ff2740e8687b7edb904431";
|
||||
(node as any).hash = "86df62e5e5f2d9f347f41648d7af9c32";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<e518a98137eb07e37499d945991b15ed>>
|
||||
* @generated SignedSource<<bdb7bb502fae96e1eb5b8ae644f8bebf>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -15,11 +15,8 @@ export type PersonalAPIKeyListFragment$data = {
|
||||
readonly personalAPIKeys: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly createdAt: any;
|
||||
readonly expiresAt: any;
|
||||
readonly id: string;
|
||||
readonly lastUsedAt: any | null | undefined;
|
||||
readonly name: string;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PersonalAPIKeyRowFragment">;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
@@ -86,32 +83,9 @@ return {
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "expiresAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "lastUsedAt",
|
||||
"storageKey": null
|
||||
"kind": "FragmentSpread",
|
||||
"name": "PersonalAPIKeyRowFragment"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
@@ -171,6 +145,6 @@ return {
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "70fa6b4e568fc1598b8dc23a030428b5";
|
||||
(node as any).hash = "eac59db5cdb5d77813236a90e7fb4d94";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* @generated SignedSource<<7a6fb678e9147fd1810e02bd09d5cef1>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RevealPersonalAPIKeyTokenInput = {
|
||||
tokenId: string;
|
||||
};
|
||||
export type PersonalAPIKeyListRevealTokenMutation$variables = {
|
||||
input: RevealPersonalAPIKeyTokenInput;
|
||||
};
|
||||
export type PersonalAPIKeyListRevealTokenMutation$data = {
|
||||
readonly revealPersonalAPIKeyToken: {
|
||||
readonly token: string;
|
||||
} | null | undefined;
|
||||
};
|
||||
export type PersonalAPIKeyListRevealTokenMutation = {
|
||||
response: PersonalAPIKeyListRevealTokenMutation$data;
|
||||
variables: PersonalAPIKeyListRevealTokenMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "RevealPersonalAPIKeyTokenPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "revealPersonalAPIKeyToken",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "token",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PersonalAPIKeyListRevealTokenMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "PersonalAPIKeyListRevealTokenMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "25ffba7c8a69e22dcc46b56a6c8633ed",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PersonalAPIKeyListRevealTokenMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation PersonalAPIKeyListRevealTokenMutation(\n $input: RevealPersonalAPIKeyTokenInput!\n) {\n revealPersonalAPIKeyToken(input: $input) {\n token\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "7dad7ebd6e8086a01b2bac8aa80c532e";
|
||||
|
||||
export default node;
|
||||
@@ -1,92 +0,0 @@
|
||||
/**
|
||||
* @generated SignedSource<<84eef5bca22c2ad2b73bbe068767e35a>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RevokePersonalAPIKeyInput = {
|
||||
tokenId: string;
|
||||
};
|
||||
export type PersonalAPIKeyListRevokeMutation$variables = {
|
||||
input: RevokePersonalAPIKeyInput;
|
||||
};
|
||||
export type PersonalAPIKeyListRevokeMutation$data = {
|
||||
readonly revokePersonalAPIKey: {
|
||||
readonly success: boolean;
|
||||
} | null | undefined;
|
||||
};
|
||||
export type PersonalAPIKeyListRevokeMutation = {
|
||||
response: PersonalAPIKeyListRevokeMutation$data;
|
||||
variables: PersonalAPIKeyListRevokeMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"alias": null,
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
"concreteType": "RevokePersonalAPIKeyPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "revokePersonalAPIKey",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "success",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PersonalAPIKeyListRevokeMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "PersonalAPIKeyListRevokeMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "fd05bb0236b583ca54be466cffe8f45b",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PersonalAPIKeyListRevokeMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation PersonalAPIKeyListRevokeMutation(\n $input: RevokePersonalAPIKeyInput!\n) {\n revokePersonalAPIKey(input: $input) {\n success\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "34ef04d19251c479827ca5028346f65b";
|
||||
|
||||
export default node;
|
||||
101
apps/console/src/__generated__/iam/PersonalAPIKeyRowFragment.graphql.ts
generated
Normal file
101
apps/console/src/__generated__/iam/PersonalAPIKeyRowFragment.graphql.ts
generated
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @generated SignedSource<<ed51014f8f35d76faaa63c10998610be>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type PersonalAPIKeyRowFragment$data = {
|
||||
readonly createdAt: any;
|
||||
readonly expiresAt: any;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly token?: string | null | undefined;
|
||||
readonly " $fragmentType": "PersonalAPIKeyRowFragment";
|
||||
};
|
||||
export type PersonalAPIKeyRowFragment$key = {
|
||||
readonly " $data"?: PersonalAPIKeyRowFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PersonalAPIKeyRowFragment">;
|
||||
};
|
||||
|
||||
import PersonalAPIKeyRowRefetchQuery_graphql from './PersonalAPIKeyRowRefetchQuery.graphql';
|
||||
|
||||
const node: ReaderFragment = {
|
||||
"argumentDefinitions": [
|
||||
{
|
||||
"defaultValue": false,
|
||||
"kind": "LocalArgument",
|
||||
"name": "includeToken"
|
||||
}
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": {
|
||||
"refetch": {
|
||||
"connection": null,
|
||||
"fragmentPathInResult": [
|
||||
"node"
|
||||
],
|
||||
"operation": PersonalAPIKeyRowRefetchQuery_graphql,
|
||||
"identifierInfo": {
|
||||
"identifierField": "id",
|
||||
"identifierQueryVariableName": "id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "PersonalAPIKeyRowFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "expiresAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"condition": "includeToken",
|
||||
"kind": "Condition",
|
||||
"passingValue": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "token",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"type": "PersonalAPIKey",
|
||||
"abstractKey": null
|
||||
};
|
||||
|
||||
(node as any).hash = "d17db443fa203ee5f9c7d0f4576295f0";
|
||||
|
||||
export default node;
|
||||
172
apps/console/src/__generated__/iam/PersonalAPIKeyRowRefetchQuery.graphql.ts
generated
Normal file
172
apps/console/src/__generated__/iam/PersonalAPIKeyRowRefetchQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* @generated SignedSource<<2d413d4e4cf2528a45ff243c0fa733bb>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type PersonalAPIKeyRowRefetchQuery$variables = {
|
||||
id: string;
|
||||
includeToken?: boolean | null | undefined;
|
||||
};
|
||||
export type PersonalAPIKeyRowRefetchQuery$data = {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"PersonalAPIKeyRowFragment">;
|
||||
} | null | undefined;
|
||||
};
|
||||
export type PersonalAPIKeyRowRefetchQuery = {
|
||||
response: PersonalAPIKeyRowRefetchQuery$data;
|
||||
variables: PersonalAPIKeyRowRefetchQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "id"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": false,
|
||||
"kind": "LocalArgument",
|
||||
"name": "includeToken"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "id"
|
||||
}
|
||||
];
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PersonalAPIKeyRowRefetchQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "includeToken",
|
||||
"variableName": "includeToken"
|
||||
}
|
||||
],
|
||||
"kind": "FragmentSpread",
|
||||
"name": "PersonalAPIKeyRowFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "PersonalAPIKeyRowRefetchQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "expiresAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"condition": "includeToken",
|
||||
"kind": "Condition",
|
||||
"passingValue": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "token",
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"type": "PersonalAPIKey",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "b4732244e28d5847baaa5520a95a4cfb",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PersonalAPIKeyRowRefetchQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query PersonalAPIKeyRowRefetchQuery(\n $includeToken: Boolean = false\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...PersonalAPIKeyRowFragment_2T7Twf\n id\n }\n}\n\nfragment PersonalAPIKeyRowFragment_2T7Twf on PersonalAPIKey {\n id\n name\n createdAt\n expiresAt\n token @include(if: $includeToken)\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "d17db443fa203ee5f9c7d0f4576295f0";
|
||||
|
||||
export default node;
|
||||
132
apps/console/src/__generated__/iam/PersonalAPIKeyRow_revokeMutation.graphql.ts
generated
Normal file
132
apps/console/src/__generated__/iam/PersonalAPIKeyRow_revokeMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @generated SignedSource<<64ad5ad6ff333dffc7b2d5d578028d7f>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type RevokePersonalAPIKeyInput = {
|
||||
personalAPIKeyId: string;
|
||||
};
|
||||
export type PersonalAPIKeyRow_revokeMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: RevokePersonalAPIKeyInput;
|
||||
};
|
||||
export type PersonalAPIKeyRow_revokeMutation$data = {
|
||||
readonly revokePersonalAPIKey: {
|
||||
readonly personalAPIKeyId: string;
|
||||
} | null | undefined;
|
||||
};
|
||||
export type PersonalAPIKeyRow_revokeMutation = {
|
||||
response: PersonalAPIKeyRow_revokeMutation$data;
|
||||
variables: PersonalAPIKeyRow_revokeMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "personalAPIKeyId",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "PersonalAPIKeyRow_revokeMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "RevokePersonalAPIKeyPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "revokePersonalAPIKey",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "PersonalAPIKeyRow_revokeMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "RevokePersonalAPIKeyPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "revokePersonalAPIKey",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "deleteEdge",
|
||||
"key": "",
|
||||
"kind": "ScalarHandle",
|
||||
"name": "personalAPIKeyId",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "29dfd9de3537585482c2863a1d547031",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "PersonalAPIKeyRow_revokeMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation PersonalAPIKeyRow_revokeMutation(\n $input: RevokePersonalAPIKeyInput!\n) {\n revokePersonalAPIKey(input: $input) {\n personalAPIKeyId\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "258ca02064f55b6fd4a3630ba1f904a4";
|
||||
|
||||
export default node;
|
||||
@@ -6,10 +6,9 @@ import {
|
||||
useFragment,
|
||||
useMutation,
|
||||
} from "react-relay";
|
||||
import type { RecordSourceSelectorProxy } from "relay-runtime";
|
||||
import { z } from "zod";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { formatError, type GraphQLError } from "@probo/helpers";
|
||||
import { formatError } from "@probo/helpers";
|
||||
import {
|
||||
Breadcrumb,
|
||||
Button,
|
||||
@@ -22,15 +21,12 @@ import {
|
||||
Label,
|
||||
Option,
|
||||
Select,
|
||||
useConfirm,
|
||||
useDialogRef,
|
||||
useToast,
|
||||
} from "@probo/ui";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import type { PersonalAPIKeyListFragment$key } from "/__generated__/iam/PersonalAPIKeyListFragment.graphql";
|
||||
import type { PersonalAPIKeyListCreateMutation } from "/__generated__/iam/PersonalAPIKeyListCreateMutation.graphql";
|
||||
import type { PersonalAPIKeyListRevokeMutation } from "/__generated__/iam/PersonalAPIKeyListRevokeMutation.graphql";
|
||||
import type { PersonalAPIKeyListRevealTokenMutation } from "/__generated__/iam/PersonalAPIKeyListRevealTokenMutation.graphql";
|
||||
import { PersonalAPIKeysTable } from "./PersonalAPIKeysTable";
|
||||
import { PersonalAPIKeyTokenDialog } from "./PersonalAPIKeyTokenDialog";
|
||||
|
||||
@@ -44,10 +40,7 @@ const fragment = graphql`
|
||||
edges @required(action: THROW) {
|
||||
node {
|
||||
id
|
||||
name
|
||||
createdAt
|
||||
expiresAt
|
||||
lastUsedAt
|
||||
...PersonalAPIKeyRowFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,7 +59,6 @@ const createMutation = graphql`
|
||||
name
|
||||
createdAt
|
||||
expiresAt
|
||||
lastUsedAt
|
||||
}
|
||||
}
|
||||
token
|
||||
@@ -74,26 +66,6 @@ const createMutation = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
const revokeMutation = graphql`
|
||||
mutation PersonalAPIKeyListRevokeMutation(
|
||||
$input: RevokePersonalAPIKeyInput!
|
||||
) {
|
||||
revokePersonalAPIKey(input: $input) {
|
||||
success
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const revealTokenMutation = graphql`
|
||||
mutation PersonalAPIKeyListRevealTokenMutation(
|
||||
$input: RevealPersonalAPIKeyTokenInput!
|
||||
) {
|
||||
revealPersonalAPIKeyToken(input: $input) {
|
||||
token
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const createSchema = z.object({
|
||||
name: z.string().min(1, "Name is required"),
|
||||
expiresIn: z.enum(["1month", "3months", "6months", "1year"]),
|
||||
@@ -126,7 +98,6 @@ export function PersonalAPIKeyList(props: {
|
||||
const { fKey } = props;
|
||||
const { __ } = useTranslate();
|
||||
const { toast } = useToast();
|
||||
const confirm = useConfirm();
|
||||
const createDialogRef = useDialogRef();
|
||||
const tokenDialogRef = useDialogRef();
|
||||
|
||||
@@ -134,9 +105,12 @@ export function PersonalAPIKeyList(props: {
|
||||
|
||||
const viewer = useFragment(fragment, fKey);
|
||||
|
||||
const keys = viewer.personalAPIKeys.edges.map(({ node }) => node);
|
||||
const connectionID = ConnectionHandler.getConnectionID(
|
||||
viewer.id,
|
||||
"PersonalAPIKeyListFragment_personalAPIKeys"
|
||||
);
|
||||
|
||||
const { formState, handleSubmit, register, control, reset, watch } =
|
||||
const { formState, handleSubmit, register, control, reset } =
|
||||
useFormWithSchema(createSchema, {
|
||||
defaultValues: {
|
||||
name: new Date().toISOString().split("T")[0],
|
||||
@@ -144,20 +118,14 @@ export function PersonalAPIKeyList(props: {
|
||||
},
|
||||
});
|
||||
|
||||
watch();
|
||||
|
||||
const [createCommit, isCreating] =
|
||||
useMutation<PersonalAPIKeyListCreateMutation>(createMutation);
|
||||
const [revokeCommit] =
|
||||
useMutation<PersonalAPIKeyListRevokeMutation>(revokeMutation);
|
||||
const [revealTokenCommit, isRevealingToken] =
|
||||
useMutation<PersonalAPIKeyListRevealTokenMutation>(revealTokenMutation);
|
||||
|
||||
const handleCreate = (data: CreateFormData) => {
|
||||
const expiresAt = computeExpiresAt(data.expiresIn);
|
||||
const connectionID = ConnectionHandler.getConnectionID(
|
||||
viewer.id,
|
||||
"PersonalAPIKeyListFragment_personalAPIKeys",
|
||||
"PersonalAPIKeyListFragment_personalAPIKeys"
|
||||
);
|
||||
|
||||
createCommit({
|
||||
@@ -165,8 +133,6 @@ export function PersonalAPIKeyList(props: {
|
||||
input: {
|
||||
name: data.name,
|
||||
expiresAt: expiresAt.toISOString(),
|
||||
// API keys are no longer linked to organizations; keep schema compatibility.
|
||||
organizationIds: [],
|
||||
},
|
||||
connections: [connectionID],
|
||||
},
|
||||
@@ -194,116 +160,6 @@ export function PersonalAPIKeyList(props: {
|
||||
});
|
||||
};
|
||||
|
||||
const handleRevoke = (key: { id: string; name: string }) => {
|
||||
confirm(
|
||||
async () => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
revokeCommit({
|
||||
variables: {
|
||||
input: { tokenId: key.id },
|
||||
},
|
||||
updater: (store: RecordSourceSelectorProxy) => {
|
||||
const viewerRecord = store.getRoot().getLinkedRecord("viewer");
|
||||
if (!viewerRecord) return;
|
||||
const connection = ConnectionHandler.getConnection(
|
||||
viewerRecord,
|
||||
"PersonalAPIKeyListFragment_personalAPIKeys",
|
||||
);
|
||||
if (connection) {
|
||||
ConnectionHandler.deleteNode(connection, key.id);
|
||||
}
|
||||
},
|
||||
onCompleted: (_response, errors) => {
|
||||
if (errors?.length) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(
|
||||
__("Failed to revoke API key."),
|
||||
errors as GraphQLError[],
|
||||
),
|
||||
variant: "error",
|
||||
});
|
||||
reject(errors);
|
||||
return;
|
||||
}
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("API key revoked successfully."),
|
||||
variant: "success",
|
||||
});
|
||||
resolve();
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(
|
||||
__("Failed to revoke API key."),
|
||||
error,
|
||||
),
|
||||
variant: "error",
|
||||
});
|
||||
reject(error);
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
{
|
||||
title: __("Revoke API Key"),
|
||||
message: __(
|
||||
`Are you sure you want to revoke the API key "${key.name}"? This action cannot be undone.`,
|
||||
),
|
||||
label: __("Revoke"),
|
||||
variant: "danger",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const handleShowToken = (key: { id: string; name: string }) => {
|
||||
revealTokenCommit({
|
||||
variables: {
|
||||
input: {
|
||||
tokenId: key.id,
|
||||
},
|
||||
},
|
||||
onCompleted: (response, errors) => {
|
||||
if (errors?.length) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(
|
||||
__("Failed to reveal API key token."),
|
||||
errors as any,
|
||||
),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const tokenValue = response.revealPersonalAPIKeyToken?.token;
|
||||
if (!tokenValue) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: __("No token returned."),
|
||||
variant: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setToken(tokenValue);
|
||||
tokenDialogRef.current?.open();
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(
|
||||
__("Failed to reveal API key token."),
|
||||
error,
|
||||
),
|
||||
variant: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-4">
|
||||
@@ -314,7 +170,7 @@ export function PersonalAPIKeyList(props: {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{keys.length === 0 ? (
|
||||
{viewer.personalAPIKeys.edges.length === 0 ? (
|
||||
<Card padded>
|
||||
<div className="text-center py-12">
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-2">
|
||||
@@ -328,10 +184,8 @@ export function PersonalAPIKeyList(props: {
|
||||
) : (
|
||||
<Card padded>
|
||||
<PersonalAPIKeysTable
|
||||
keys={keys}
|
||||
onRevoke={handleRevoke}
|
||||
onShowToken={handleShowToken}
|
||||
isShowingToken={isRevealingToken}
|
||||
edges={viewer.personalAPIKeys.edges}
|
||||
connectionId={connectionID}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { formatDate, formatError, type GraphQLError } from "@probo/helpers";
|
||||
import { Button, Spinner, Td, Tr, useConfirm, useToast } from "@probo/ui";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useMutation, useFragment } from "react-relay";
|
||||
import type { PersonalAPIKeyRowFragment$key } from "/__generated__/iam/PersonalAPIKeyRowFragment.graphql";
|
||||
import type { PersonalAPIKeyRow_revokeMutation } from "/__generated__/iam/PersonalAPIKeyRow_revokeMutation.graphql";
|
||||
import clsx from "clsx";
|
||||
import { PersonalAPIKeyTokenAction } from "./PersonalAPIKeyTokenAction";
|
||||
import { Suspense } from "react";
|
||||
|
||||
const revokeMutation = graphql`
|
||||
mutation PersonalAPIKeyRow_revokeMutation(
|
||||
$input: RevokePersonalAPIKeyInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
revokePersonalAPIKey(input: $input) {
|
||||
personalAPIKeyId @deleteEdge(connections: $connections)
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const personalAPIKeyRowFragment = graphql`
|
||||
fragment PersonalAPIKeyRowFragment on PersonalAPIKey
|
||||
@refetchable(queryName: "PersonalAPIKeyRowRefetchQuery")
|
||||
@argumentDefinitions(includeToken: { type: "Boolean", defaultValue: false }) {
|
||||
id
|
||||
name
|
||||
createdAt
|
||||
expiresAt
|
||||
token @include(if: $includeToken)
|
||||
}
|
||||
`;
|
||||
|
||||
export function PersonalAPIKeyRow(props: {
|
||||
fKey: PersonalAPIKeyRowFragment$key;
|
||||
connectionId: string;
|
||||
}) {
|
||||
const { fKey, connectionId } = props;
|
||||
const { __ } = useTranslate();
|
||||
const confirm = useConfirm();
|
||||
const { toast } = useToast();
|
||||
const now = new Date();
|
||||
|
||||
const key = useFragment(personalAPIKeyRowFragment, fKey);
|
||||
const expired = new Date(key.expiresAt) < now;
|
||||
|
||||
const [revokeCommit, isRevoking] =
|
||||
useMutation<PersonalAPIKeyRow_revokeMutation>(revokeMutation);
|
||||
|
||||
const handleRevoke = () => {
|
||||
confirm(
|
||||
async () => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
revokeCommit({
|
||||
variables: {
|
||||
input: { personalAPIKeyId: key.id },
|
||||
connections: [connectionId],
|
||||
},
|
||||
onCompleted: (_response, errors) => {
|
||||
if (errors?.length) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(
|
||||
__("Failed to revoke API key."),
|
||||
errors as GraphQLError[]
|
||||
),
|
||||
variant: "error",
|
||||
});
|
||||
reject(errors);
|
||||
return;
|
||||
}
|
||||
toast({
|
||||
title: __("Success"),
|
||||
description: __("API key revoked successfully."),
|
||||
variant: "success",
|
||||
});
|
||||
resolve();
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(
|
||||
__("Failed to revoke API key."),
|
||||
error
|
||||
),
|
||||
variant: "error",
|
||||
});
|
||||
reject(error);
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
{
|
||||
title: __("Revoke API Key"),
|
||||
message: __(
|
||||
`Are you sure you want to revoke the API key "${key.name}"? This action cannot be undone.`
|
||||
),
|
||||
label: __("Revoke"),
|
||||
variant: "danger",
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Tr className={clsx(isRevoking && "opacity-60 pointer-events-none")}>
|
||||
<Td>
|
||||
<div className="font-medium text-txt-primary">{key.name}</div>
|
||||
<div className="text-xs text-txt-tertiary">
|
||||
{expired ? __("Expired") : __("Active")}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<span className="text-sm text-txt-secondary">
|
||||
{formatDate(key.createdAt)}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<span className="text-sm text-txt-secondary">
|
||||
{formatDate(key.expiresAt)}
|
||||
</span>
|
||||
</Td>
|
||||
<Td width={140} className="text-end">
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<PersonalAPIKeyTokenAction fKey={fKey} disabled={isRevoking} />
|
||||
</Suspense>
|
||||
<Button variant="danger" onClick={handleRevoke} disabled={isRevoking}>
|
||||
{__("Revoke")}
|
||||
</Button>
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { Button, useDialogRef, useToast } from "@probo/ui";
|
||||
import { formatError } from "@probo/helpers";
|
||||
import { useRefetchableFragment } from "react-relay";
|
||||
import { PersonalAPIKeyTokenDialog } from "./PersonalAPIKeyTokenDialog";
|
||||
import { personalAPIKeyRowFragment } from "./PersonalAPIKeyRow";
|
||||
import type { PersonalAPIKeyRowFragment$key } from "/__generated__/iam/PersonalAPIKeyRowFragment.graphql";
|
||||
import type { PersonalAPIKeyRowRefetchQuery } from "/__generated__/iam/PersonalAPIKeyRowRefetchQuery.graphql";
|
||||
|
||||
export function PersonalAPIKeyTokenAction(props: {
|
||||
fKey: PersonalAPIKeyRowFragment$key;
|
||||
disabled?: boolean;
|
||||
}) {
|
||||
const { fKey, disabled } = props;
|
||||
const { __ } = useTranslate();
|
||||
const { toast } = useToast();
|
||||
const dialogRef = useDialogRef();
|
||||
|
||||
const [data, refetch] = useRefetchableFragment<
|
||||
PersonalAPIKeyRowRefetchQuery,
|
||||
PersonalAPIKeyRowFragment$key
|
||||
>(personalAPIKeyRowFragment, fKey);
|
||||
|
||||
const handleShow = () => {
|
||||
dialogRef.current?.open();
|
||||
|
||||
refetch(
|
||||
{ includeToken: true },
|
||||
{
|
||||
fetchPolicy: "network-only",
|
||||
onComplete: (error) => {
|
||||
if (error) {
|
||||
toast({
|
||||
title: __("Error"),
|
||||
description: formatError(
|
||||
__("Failed to load API key token."),
|
||||
error
|
||||
),
|
||||
variant: "error",
|
||||
});
|
||||
dialogRef.current?.close();
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant="secondary" onClick={handleShow} disabled={!!disabled}>
|
||||
{__("Show")}
|
||||
</Button>
|
||||
|
||||
<PersonalAPIKeyTokenDialog
|
||||
dialogRef={dialogRef}
|
||||
token={data.token ?? ""}
|
||||
onDone={() => {
|
||||
dialogRef.current?.close();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useCopy } from "@probo/hooks";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export function PersonalAPIKeyTokenDialog(props: {
|
||||
dialogRef: React.RefObject<{ open: () => void; close: () => void } | null>;
|
||||
@@ -18,8 +17,6 @@ export function PersonalAPIKeyTokenDialog(props: {
|
||||
const { __ } = useTranslate();
|
||||
const [isCopied, copy] = useCopy();
|
||||
|
||||
useEffect(() => {}, [token]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
ref={dialogRef}
|
||||
|
||||
@@ -1,24 +1,14 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { formatDate } from "@probo/helpers";
|
||||
import { Button, Table, Tbody, Td, Th, Thead, Tr } from "@probo/ui";
|
||||
|
||||
export type PersonalAPIKeyRow = {
|
||||
id: string;
|
||||
name: string;
|
||||
createdAt: string;
|
||||
expiresAt: string;
|
||||
lastUsedAt: string | null;
|
||||
};
|
||||
import { Table, Tbody, Th, Thead, Tr } from "@probo/ui";
|
||||
import { PersonalAPIKeyRow } from "./PersonalAPIKeyRow";
|
||||
import type { PersonalAPIKeyListFragment$data } from "/__generated__/iam/PersonalAPIKeyListFragment.graphql";
|
||||
|
||||
export function PersonalAPIKeysTable(props: {
|
||||
keys: PersonalAPIKeyRow[];
|
||||
onRevoke: (key: { id: string; name: string }) => void;
|
||||
onShowToken: (key: { id: string; name: string }) => void;
|
||||
isShowingToken?: boolean;
|
||||
edges: PersonalAPIKeyListFragment$data["personalAPIKeys"]["edges"];
|
||||
connectionId: string;
|
||||
}) {
|
||||
const { keys, onRevoke, onShowToken, isShowingToken } = props;
|
||||
const { edges, connectionId } = props;
|
||||
const { __ } = useTranslate();
|
||||
const now = new Date();
|
||||
|
||||
return (
|
||||
<Table>
|
||||
@@ -32,51 +22,13 @@ export function PersonalAPIKeysTable(props: {
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{keys.map((k) => {
|
||||
const expired = new Date(k.expiresAt) < now;
|
||||
return (
|
||||
<Tr key={k.id}>
|
||||
<Td>
|
||||
<div className="font-medium text-txt-primary">{k.name}</div>
|
||||
<div className="text-xs text-txt-tertiary">
|
||||
{expired ? __("Expired") : __("Active")}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<span className="text-sm text-txt-secondary">
|
||||
{k.lastUsedAt ? formatDate(k.lastUsedAt) : "—"}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<span className="text-sm text-txt-secondary">
|
||||
{formatDate(k.createdAt)}
|
||||
</span>
|
||||
</Td>
|
||||
<Td>
|
||||
<span className="text-sm text-txt-secondary">
|
||||
{formatDate(k.expiresAt)}
|
||||
</span>
|
||||
</Td>
|
||||
<Td width={140} className="text-end">
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => onShowToken({ id: k.id, name: k.name })}
|
||||
disabled={!!isShowingToken}
|
||||
>
|
||||
{__("Show")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="danger"
|
||||
onClick={() => onRevoke({ id: k.id, name: k.name })}
|
||||
>
|
||||
{__("Revoke")}
|
||||
</Button>
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
{edges.map(({ node }) => (
|
||||
<PersonalAPIKeyRow
|
||||
key={node.id}
|
||||
fKey={node}
|
||||
connectionId={connectionId}
|
||||
/>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
);
|
||||
|
||||
@@ -56,6 +56,21 @@ func (a *Asset) CursorKey(field AssetOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (a *Asset) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM assets WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, a.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query asset authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (a *Asset) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -60,6 +60,21 @@ func (a *Audit) CursorKey(field AuditOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (a *Audit) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM audits WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, a.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query audit authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (a *Audit) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -17,16 +17,17 @@ package coredata
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/connector"
|
||||
"go.probo.inc/probo/pkg/crypto/cipher"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -56,6 +57,21 @@ func (c *Connector) CursorKey(orderBy ConnectorOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (c *Connector) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM connectors WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, c.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query connector authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (c *Connectors) LoadByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -16,14 +16,15 @@ package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -63,6 +64,21 @@ func (ci *ContinualImprovement) CursorKey(field ContinualImprovementOrderField)
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (ci *ContinualImprovement) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM continual_improvements WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, ci.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query continual improvement authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (ci *ContinualImprovement) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -57,6 +57,21 @@ func (c Control) CursorKey(orderBy ControlOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (c *Control) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM controls WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, c.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query control authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (c *Controls) CountByDocumentID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -65,6 +65,21 @@ func NewCustomDomain(tenantID gid.TenantID, domain string) *CustomDomain {
|
||||
}
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (cd *CustomDomain) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM custom_domains WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, cd.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query custom domain authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (cd *CustomDomain) CursorKey(field CustomDomainOrderField) page.CursorKey {
|
||||
switch field {
|
||||
case CustomDomainOrderFieldCreatedAt:
|
||||
|
||||
@@ -69,6 +69,21 @@ func (dpia *DataProtectionImpactAssessment) CursorKey(field DataProtectionImpact
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (dpia *DataProtectionImpactAssessment) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM processing_activity_data_protection_impact_assessments WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, dpia.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query data protection impact assessment authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (dpias *DataProtectionImpactAssessments) CountByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -16,14 +16,15 @@ package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -59,6 +60,21 @@ func (d *Datum) CursorKey(field DatumOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (d *Datum) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM data WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, d.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query datum authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (d *Datum) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -58,6 +58,21 @@ func (p Document) CursorKey(orderBy DocumentOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (d *Document) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM documents WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, d.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query document authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (p *Document) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -48,6 +48,21 @@ type (
|
||||
DocumentVersions []*DocumentVersion
|
||||
)
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (dv *DocumentVersion) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM document_versions WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, dv.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query document version authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (p *DocumentVersions) LoadByDocumentID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -62,6 +62,21 @@ func (pvs DocumentVersionSignature) CursorKey(orderBy DocumentVersionSignatureOr
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (dvs *DocumentVersionSignature) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM document_version_signatures WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, dvs.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query document version signature authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (pvs *DocumentVersionSignature) LoadByDocumentVersionIDAndSignatory(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -44,6 +44,12 @@ var (
|
||||
ErrNoUnsentEmail = errors.New("no unsent email found")
|
||||
)
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
// Email is identity-scoped (not org-scoped), so it returns an empty map.
|
||||
func (e *Email) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
return map[string]string{}, nil
|
||||
}
|
||||
|
||||
func NewEmail(
|
||||
recipientName string,
|
||||
recipientEmail mail.Addr,
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
package coredata
|
||||
|
||||
import "go.probo.inc/probo/pkg/gid"
|
||||
|
||||
type ctxKey struct{ name string }
|
||||
|
||||
var (
|
||||
@@ -75,6 +77,109 @@ const (
|
||||
MembershipProfileEntityType uint16 = 51
|
||||
)
|
||||
|
||||
func NewEntityFromID(id gid.GID) (any, bool) {
|
||||
switch id.EntityType() {
|
||||
case OrganizationEntityType:
|
||||
return &Organization{ID: id}, true
|
||||
case FrameworkEntityType:
|
||||
return &Framework{ID: id}, true
|
||||
case MeasureEntityType:
|
||||
return &Measure{ID: id}, true
|
||||
case TaskEntityType:
|
||||
return &Task{ID: id}, true
|
||||
case EvidenceEntityType:
|
||||
return &Evidence{ID: id}, true
|
||||
case ConnectorEntityType:
|
||||
return &Connector{ID: id}, true
|
||||
case VendorRiskAssessmentEntityType:
|
||||
return &VendorRiskAssessment{ID: id}, true
|
||||
case VendorEntityType:
|
||||
return &Vendor{ID: id}, true
|
||||
case PeopleEntityType:
|
||||
return &People{ID: id}, true
|
||||
case VendorComplianceReportEntityType:
|
||||
return &VendorComplianceReport{ID: id}, true
|
||||
case DocumentEntityType:
|
||||
return &Document{ID: id}, true
|
||||
case IdentityEntityType:
|
||||
return &Identity{ID: id}, true
|
||||
case SessionEntityType:
|
||||
return &Session{ID: id}, true
|
||||
case EmailEntityType:
|
||||
return &Email{ID: id}, true
|
||||
case ControlEntityType:
|
||||
return &Control{ID: id}, true
|
||||
case RiskEntityType:
|
||||
return &Risk{ID: id}, true
|
||||
case DocumentVersionEntityType:
|
||||
return &DocumentVersion{ID: id}, true
|
||||
case DocumentVersionSignatureEntityType:
|
||||
return &DocumentVersionSignature{ID: id}, true
|
||||
case AssetEntityType:
|
||||
return &Asset{ID: id}, true
|
||||
case DatumEntityType:
|
||||
return &Datum{ID: id}, true
|
||||
case AuditEntityType:
|
||||
return &Audit{ID: id}, true
|
||||
case ReportEntityType:
|
||||
return &Report{ID: id}, true
|
||||
case TrustCenterEntityType:
|
||||
return &TrustCenter{ID: id}, true
|
||||
case TrustCenterAccessEntityType:
|
||||
return &TrustCenterAccess{ID: id}, true
|
||||
case VendorBusinessAssociateAgreementEntityType:
|
||||
return &VendorBusinessAssociateAgreement{ID: id}, true
|
||||
case FileEntityType:
|
||||
return &File{ID: id}, true
|
||||
case VendorContactEntityType:
|
||||
return &VendorContact{ID: id}, true
|
||||
case VendorDataPrivacyAgreementEntityType:
|
||||
return &VendorDataPrivacyAgreement{ID: id}, true
|
||||
case NonconformityEntityType:
|
||||
return &Nonconformity{ID: id}, true
|
||||
case ObligationEntityType:
|
||||
return &Obligation{ID: id}, true
|
||||
case VendorServiceEntityType:
|
||||
return &VendorService{ID: id}, true
|
||||
case SnapshotEntityType:
|
||||
return &Snapshot{ID: id}, true
|
||||
case ContinualImprovementEntityType:
|
||||
return &ContinualImprovement{ID: id}, true
|
||||
case ProcessingActivityEntityType:
|
||||
return &ProcessingActivity{ID: id}, true
|
||||
case ExportJobEntityType:
|
||||
return &ExportJob{ID: id}, true
|
||||
case TrustCenterReferenceEntityType:
|
||||
return &TrustCenterReference{ID: id}, true
|
||||
case TrustCenterDocumentAccessEntityType:
|
||||
return &TrustCenterDocumentAccess{ID: id}, true
|
||||
case CustomDomainEntityType:
|
||||
return &CustomDomain{ID: id}, true
|
||||
case InvitationEntityType:
|
||||
return &Invitation{ID: id}, true
|
||||
case MembershipEntityType:
|
||||
return &Membership{ID: id}, true
|
||||
case SlackMessageEntityType:
|
||||
return &SlackMessage{ID: id}, true
|
||||
case TrustCenterFileEntityType:
|
||||
return &TrustCenterFile{ID: id}, true
|
||||
case SAMLConfigurationEntityType:
|
||||
return &SAMLConfiguration{ID: id}, true
|
||||
case PersonalAPIKeyEntityType:
|
||||
return &PersonalAPIKey{ID: id}, true
|
||||
case MeetingEntityType:
|
||||
return &Meeting{ID: id}, true
|
||||
case DataProtectionImpactAssessmentEntityType:
|
||||
return &DataProtectionImpactAssessment{ID: id}, true
|
||||
case TransferImpactAssessmentEntityType:
|
||||
return &TransferImpactAssessment{ID: id}, true
|
||||
case MembershipProfileEntityType:
|
||||
return &MembershipProfile{ID: id}, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
type EntityInfo struct {
|
||||
Model string
|
||||
Table string
|
||||
|
||||
@@ -56,6 +56,21 @@ func (e Evidence) CursorKey(orderBy EvidenceOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (e *Evidence) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM evidences WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, e.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query evidence authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (e Evidence) Upsert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -16,18 +16,18 @@ import (
|
||||
|
||||
type (
|
||||
ExportJob struct {
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
Type ExportJobType `db:"type"`
|
||||
Arguments json.RawMessage `db:"arguments"`
|
||||
Error *string `db:"error"`
|
||||
Status ExportJobStatus `db:"status"`
|
||||
FileID *gid.GID `db:"file_id"`
|
||||
RecipientEmail mail.Addr `db:"recipient_email"`
|
||||
RecipientName string `db:"recipient_name"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
StartedAt *time.Time `db:"started_at"`
|
||||
CompletedAt *time.Time `db:"completed_at"`
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
Type ExportJobType `db:"type"`
|
||||
Arguments json.RawMessage `db:"arguments"`
|
||||
Error *string `db:"error"`
|
||||
Status ExportJobStatus `db:"status"`
|
||||
FileID *gid.GID `db:"file_id"`
|
||||
RecipientEmail mail.Addr `db:"recipient_email"`
|
||||
RecipientName string `db:"recipient_name"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
StartedAt *time.Time `db:"started_at"`
|
||||
CompletedAt *time.Time `db:"completed_at"`
|
||||
}
|
||||
|
||||
ExportJobs []*ExportJob
|
||||
@@ -48,6 +48,21 @@ var (
|
||||
ErrNoExportJobAvailable = errors.New("no export job available")
|
||||
)
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (ej *ExportJob) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM export_jobs WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, ej.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query export job authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (ej *ExportJob) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -44,6 +44,21 @@ type (
|
||||
Files []*File
|
||||
)
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (f *File) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM files WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, f.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query file authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (f *File) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -53,6 +53,21 @@ func (f *Framework) CursorKey(orderBy FrameworkOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (f *Framework) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM frameworks WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, f.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query framework authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (f *Frameworks) CountByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -220,6 +220,34 @@ LIMIT 1;
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuthorizationAttributes loads the minimal authorization attributes for policy condition evaluation.
|
||||
// It is intentionally lightweight and does not populate the Identity struct.
|
||||
func (i *Identity) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
email_address
|
||||
FROM
|
||||
identities
|
||||
WHERE
|
||||
id = $1
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
var (
|
||||
id gid.GID
|
||||
emailAddress string
|
||||
)
|
||||
if err := conn.QueryRow(ctx, q, i.ID).Scan(&id, &emailAddress); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query identity iam attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"id": id.String(), "email": emailAddress}, nil
|
||||
}
|
||||
|
||||
func (i *Identity) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -164,6 +164,35 @@ WHERE
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuthorizationAttributes loads the minimal authorization attributes for policy condition evaluation.
|
||||
// It is intentionally lightweight and does not populate the Invitation struct.
|
||||
func (i *Invitation) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `
|
||||
SELECT
|
||||
email
|
||||
, organization_id
|
||||
FROM
|
||||
iam_invitations
|
||||
WHERE
|
||||
id = $1
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
var email string
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, i.ID).Scan(&email, &organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query invitation iam attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
"email": email,
|
||||
"organization_id": organizationID.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *Invitation) Update(ctx context.Context, conn pg.Conn, scope Scoper) error {
|
||||
query := `
|
||||
UPDATE
|
||||
|
||||
@@ -56,6 +56,21 @@ func (m Measure) CursorKey(orderBy MeasureOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (m *Measure) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM measures WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, m.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query measure authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (m *Measures) CountByRiskID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -54,6 +54,21 @@ func (m Meeting) CursorKey(orderBy MeetingOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
// AuthorizationAttributes returns the authorization attributes for policy evaluation.
|
||||
func (m *Meeting) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM meetings WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, m.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query meeting authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (m *Meeting) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -226,6 +226,33 @@ LEFT JOIN
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Membership) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `
|
||||
SELECT
|
||||
identity_id
|
||||
, organization_id
|
||||
FROM
|
||||
iam_memberships
|
||||
WHERE
|
||||
id = $1
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
var identityID gid.GID
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, m.ID).Scan(&identityID, &organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query membership iam attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
"identity_id": identityID.String(),
|
||||
"organization_id": organizationID.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func LoadRoleByIdentityAndEntityIDOnly(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
@@ -235,7 +262,6 @@ func LoadRoleByIdentityAndEntityIDOnly(
|
||||
) (MembershipRole, error) {
|
||||
entityType := entityID.EntityType()
|
||||
|
||||
// For organization, the entity ID is the organization ID - optimized path
|
||||
if entityType == OrganizationEntityType {
|
||||
query := `
|
||||
SELECT role
|
||||
@@ -595,3 +621,39 @@ WHERE
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (m *Memberships) LoadAllByIdentityID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
identityID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
identity_id,
|
||||
organization_id,
|
||||
role,
|
||||
'' as full_name,
|
||||
NULL as email_address,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
iam_memberships
|
||||
WHERE
|
||||
identity_id = $1
|
||||
;
|
||||
`
|
||||
|
||||
rows, err := conn.Query(ctx, q, identityID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query memberships: %w", err)
|
||||
}
|
||||
|
||||
memberships, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Membership])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect memberships: %w", err)
|
||||
}
|
||||
|
||||
*m = memberships
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -36,6 +36,20 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func (p *MembershipProfile) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT m.organization_id FROM iam_membership_profiles mp JOIN iam_memberships m ON mp.membership_id = m.id WHERE mp.id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, p.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query membership profile authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (p *MembershipProfile) LoadByMembershipID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -16,14 +16,15 @@ package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -66,6 +67,20 @@ func (nc *Nonconformity) CursorKey(field NonconformityOrderField) page.CursorKey
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
func (nc *Nonconformity) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM nonconformities WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, nc.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query nonconformity authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (nc *Nonconformity) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
@@ -399,9 +414,9 @@ WHERE %s AND nc.organization_id = @organization_id AND nc.snapshot_id IS NULL
|
||||
query = fmt.Sprintf(query, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"snapshot_id": snapshotID,
|
||||
"organization_id": organizationID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"snapshot_id": snapshotID,
|
||||
"organization_id": organizationID,
|
||||
"nonconformity_entity_type": NonconformityEntityType,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
@@ -16,6 +16,7 @@ package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
@@ -64,6 +65,20 @@ func (o *Obligation) CursorKey(field ObligationOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
func (o *Obligation) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM obligations WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, o.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query obligation authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (o *Obligation) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -46,6 +46,20 @@ type (
|
||||
Organizations []*Organization
|
||||
)
|
||||
|
||||
func (o *Organization) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT id FROM organizations WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var id gid.GID
|
||||
if err := conn.QueryRow(ctx, q, o.ID).Scan(&id); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query organization authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": o.ID.String()}, nil
|
||||
}
|
||||
|
||||
func (o Organization) CursorKey(orderBy OrganizationOrderField) page.CursorKey {
|
||||
switch orderBy {
|
||||
case OrganizationOrderFieldName:
|
||||
|
||||
@@ -60,6 +60,20 @@ func (p People) CursorKey(orderBy PeopleOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (p *People) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM peoples WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, p.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query people authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (p *People) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -91,6 +91,20 @@ LIMIT 1;
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PersonalAPIKey) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := "SELECT identity_id FROM iam_personal_api_keys WHERE id = $1 LIMIT 1;"
|
||||
|
||||
var identityID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, a.ID).Scan(&identityID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query personal api key iam attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"identity_id": identityID.String()}, nil
|
||||
}
|
||||
|
||||
func (a *PersonalAPIKeys) LoadByIdentityID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -16,6 +16,7 @@ package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
@@ -75,6 +76,20 @@ func (p *ProcessingActivity) CursorKey(field ProcessingActivityOrderField) page.
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
func (p *ProcessingActivity) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM processing_activities WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, p.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query processing activity authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (p *ProcessingActivity) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -42,6 +42,20 @@ type (
|
||||
Reports []*Report
|
||||
)
|
||||
|
||||
func (r *Report) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM reports WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, r.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query report authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (r *Report) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -80,6 +80,20 @@ func (r *Risk) CursorKey(orderBy RiskOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (r *Risk) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM risks WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, r.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query risk authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (r *Risks) CountByMeasureID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -63,6 +63,20 @@ func (s *SAMLConfiguration) CursorKey(orderBy SAMLConfigurationOrderField) page.
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (s *SAMLConfiguration) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM iam_saml_configurations WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, s.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query saml configuration authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (s *SAMLConfiguration) GetIdPCertificate() (*x509.Certificate, error) {
|
||||
block, _ := pem.Decode([]byte(s.IdPCertificate))
|
||||
if block == nil {
|
||||
|
||||
@@ -140,6 +140,30 @@ LIMIT 1;
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuthorizationAttributes loads the minimal authorization attributes for policy condition evaluation.
|
||||
// It is intentionally lightweight and does not populate the Session struct.
|
||||
func (s *Session) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `
|
||||
SELECT
|
||||
identity_id
|
||||
FROM
|
||||
iam_sessions
|
||||
WHERE
|
||||
id = $1
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
var identityID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, s.ID).Scan(&identityID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query session iam attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"identity_id": identityID.String()}, nil
|
||||
}
|
||||
|
||||
func (s *Session) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -29,19 +29,19 @@ import (
|
||||
|
||||
type (
|
||||
SlackMessage struct {
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
Type SlackMessageType `db:"type"`
|
||||
Body map[string]any `db:"body"`
|
||||
MessageTS *string `db:"message_ts"`
|
||||
ChannelID *string `db:"channel_id"`
|
||||
RequesterEmail *mail.Addr `db:"requester_email"`
|
||||
Metadata map[string]any `db:"metadata"`
|
||||
InitialSlackMessageID gid.GID `db:"initial_slack_message_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
SentAt *time.Time `db:"sent_at"`
|
||||
Error *string `db:"error"`
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
Type SlackMessageType `db:"type"`
|
||||
Body map[string]any `db:"body"`
|
||||
MessageTS *string `db:"message_ts"`
|
||||
ChannelID *string `db:"channel_id"`
|
||||
RequesterEmail *mail.Addr `db:"requester_email"`
|
||||
Metadata map[string]any `db:"metadata"`
|
||||
InitialSlackMessageID gid.GID `db:"initial_slack_message_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
SentAt *time.Time `db:"sent_at"`
|
||||
Error *string `db:"error"`
|
||||
}
|
||||
|
||||
ErrNoUnsentSlackMessage struct{}
|
||||
@@ -57,6 +57,20 @@ func (e ErrSlackMessageNotFound) Error() string {
|
||||
return "slack message not found"
|
||||
}
|
||||
|
||||
func (sm *SlackMessage) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM slack_messages WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, sm.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query slack message authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func NewSlackMessage(
|
||||
scope Scoper,
|
||||
organizationID gid.GID,
|
||||
|
||||
@@ -16,6 +16,7 @@ package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
@@ -52,6 +53,20 @@ func (s *Snapshot) CursorKey(field SnapshotOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
func (s *Snapshot) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM snapshots WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, s.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query snapshot authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (s *Snapshot) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -57,6 +57,20 @@ func (c Task) CursorKey(orderBy TaskOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (t *Task) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM tasks WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, t.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query task authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (c *Task) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -69,6 +69,20 @@ func (tia *TransferImpactAssessment) CursorKey(field TransferImpactAssessmentOrd
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
}
|
||||
|
||||
func (tia *TransferImpactAssessment) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM processing_activity_transfer_impact_assessments WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, tia.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query transfer impact assessment authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (tias *TransferImpactAssessments) CountByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -52,6 +52,20 @@ func (tc *TrustCenter) CursorKey(orderBy TrustCenterOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (tc *TrustCenter) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM trust_centers WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, tc.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query trust center authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (tc *TrustCenter) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -32,19 +32,19 @@ import (
|
||||
|
||||
type (
|
||||
TrustCenterAccess struct {
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
TenantID gid.TenantID `db:"tenant_id"`
|
||||
TrustCenterID gid.GID `db:"trust_center_id"`
|
||||
Email mail.Addr `db:"email"`
|
||||
Name string `db:"name"`
|
||||
Active bool `db:"active"`
|
||||
HasAcceptedNonDisclosureAgreement bool `db:"has_accepted_non_disclosure_agreement"`
|
||||
HasAcceptedNonDisclosureAgreementMetadata json.RawMessage `db:"has_accepted_non_disclosure_agreement_metadata"`
|
||||
NDAFileID *gid.GID `db:"nda_file_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
LastTokenExpiresAt *time.Time `db:"last_token_expires_at"`
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
TenantID gid.TenantID `db:"tenant_id"`
|
||||
TrustCenterID gid.GID `db:"trust_center_id"`
|
||||
Email mail.Addr `db:"email"`
|
||||
Name string `db:"name"`
|
||||
Active bool `db:"active"`
|
||||
HasAcceptedNonDisclosureAgreement bool `db:"has_accepted_non_disclosure_agreement"`
|
||||
HasAcceptedNonDisclosureAgreementMetadata json.RawMessage `db:"has_accepted_non_disclosure_agreement_metadata"`
|
||||
NDAFileID *gid.GID `db:"nda_file_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
LastTokenExpiresAt *time.Time `db:"last_token_expires_at"`
|
||||
}
|
||||
|
||||
TrustCenterAccesses []*TrustCenterAccess
|
||||
@@ -59,6 +59,20 @@ func (tca *TrustCenterAccess) CursorKey(orderBy TrustCenterAccessOrderField) pag
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (tca *TrustCenterAccess) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM trust_center_accesses WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, tca.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query trust center access authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (tca *TrustCenterAccess) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -53,6 +53,20 @@ func (tcda *TrustCenterDocumentAccess) CursorKey(orderBy TrustCenterDocumentAcce
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (tcda *TrustCenterDocumentAccess) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM trust_center_document_accesses WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, tcda.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query trust center document access authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (tcda *TrustCenterDocumentAccess) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -16,6 +16,7 @@ package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
@@ -53,6 +54,20 @@ func (t TrustCenterFile) CursorKey(orderBy TrustCenterFileOrderField) page.Curso
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (t *TrustCenterFile) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM trust_center_files WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, t.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query trust center file authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (t *TrustCenterFile) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -59,6 +59,20 @@ func (t TrustCenterReference) CursorKey(orderBy TrustCenterReferenceOrderField)
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (t *TrustCenterReference) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM trust_center_references WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, t.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query trust center reference authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (t *TrustCenterReference) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -78,6 +78,20 @@ func (v Vendor) CursorKey(orderBy VendorOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (v *Vendor) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM vendors WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, v.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query vendor authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (v *Vendor) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -56,6 +56,20 @@ func (v VendorBusinessAssociateAgreement) CursorKey(orderBy VendorBusinessAssoci
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (vbaa *VendorBusinessAssociateAgreement) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM vendor_business_associate_agreements WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, vbaa.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query vendor business associate agreement authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (vbaa *VendorBusinessAssociateAgreement) LoadByVendorID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -16,14 +16,15 @@ package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -55,6 +56,20 @@ func (c VendorComplianceReport) CursorKey(orderBy VendorComplianceReportOrderFie
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (v *VendorComplianceReport) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM vendor_compliance_reports WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, v.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query vendor compliance report authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (vcs *VendorComplianceReports) LoadForVendorID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -59,6 +59,20 @@ func (vc VendorContact) CursorKey(orderBy VendorContactOrderField) page.CursorKe
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (vc *VendorContact) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM vendor_contacts WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, vc.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query vendor contact authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (vc *VendorContact) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -56,6 +56,20 @@ func (v VendorDataPrivacyAgreement) CursorKey(orderBy VendorDataPrivacyAgreement
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (vdpa *VendorDataPrivacyAgreement) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM vendor_data_privacy_agreements WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, vdpa.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query vendor data privacy agreement authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (vdpa *VendorDataPrivacyAgreement) LoadByVendorID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -16,6 +16,7 @@ package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
@@ -56,6 +57,20 @@ func (v VendorRiskAssessment) CursorKey(orderBy VendorRiskAssessmentOrderField)
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (v *VendorRiskAssessment) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM vendor_risk_assessments WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, v.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query vendor risk assessment authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
// Insert adds a new risk assessment to the database
|
||||
func (r VendorRiskAssessment) Insert(
|
||||
ctx context.Context,
|
||||
|
||||
@@ -54,6 +54,20 @@ func (vs VendorService) CursorKey(orderBy VendorServiceOrderField) page.CursorKe
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (vs *VendorService) AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error) {
|
||||
q := `SELECT organization_id FROM vendor_services WHERE id = $1 LIMIT 1;`
|
||||
|
||||
var organizationID gid.GID
|
||||
if err := conn.QueryRow(ctx, q, vs.ID).Scan(&organizationID); err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, ErrResourceNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("cannot query vendor service authorization attributes: %w", err)
|
||||
}
|
||||
|
||||
return map[string]string{"organization_id": organizationID.String()}, nil
|
||||
}
|
||||
|
||||
func (vs *VendorService) LoadByID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
@@ -16,7 +16,7 @@ package iam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
|
||||
"go.gearno.de/kit/pg"
|
||||
@@ -25,12 +25,28 @@ import (
|
||||
"go.probo.inc/probo/pkg/iam/policy"
|
||||
)
|
||||
|
||||
// AuthorizationAttributer is implemented by entities that provide attributes
|
||||
// for policy condition evaluation.
|
||||
type AuthorizationAttributer interface {
|
||||
AuthorizationAttributes(ctx context.Context, conn pg.Conn) (map[string]string, error)
|
||||
}
|
||||
|
||||
// AuthorizeParams contains the parameters for an authorization request.
|
||||
type AuthorizeParams struct {
|
||||
Principal gid.GID
|
||||
Resource gid.GID
|
||||
Action string
|
||||
ResourceAttributes map[string]string
|
||||
}
|
||||
|
||||
// Authorizer evaluates authorization requests against registered policies.
|
||||
type Authorizer struct {
|
||||
pg *pg.Client
|
||||
evaluator *policy.Evaluator
|
||||
policySet *PolicySet
|
||||
}
|
||||
|
||||
// NewAuthorizer creates a new Authorizer instance.
|
||||
func NewAuthorizer(pgClient *pg.Client) *Authorizer {
|
||||
return &Authorizer{
|
||||
pg: pgClient,
|
||||
@@ -39,87 +55,144 @@ func NewAuthorizer(pgClient *pg.Client) *Authorizer {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Authorizer) RegisterPolicySet(policySet *PolicySet) {
|
||||
a.policySet.Merge(policySet)
|
||||
}
|
||||
|
||||
type AuthorizeParams struct {
|
||||
Principal gid.GID
|
||||
Resource gid.GID
|
||||
Action string
|
||||
ResourceAttributes map[string]string
|
||||
// RegisterPolicySet merges the given policy set into the authorizer.
|
||||
func (a *Authorizer) RegisterPolicySet(ps *PolicySet) {
|
||||
a.policySet.Merge(ps)
|
||||
}
|
||||
|
||||
// Authorize checks if the principal is allowed to perform the action on the resource.
|
||||
func (a *Authorizer) Authorize(ctx context.Context, params AuthorizeParams) error {
|
||||
if params.Principal.EntityType() != coredata.IdentityEntityType {
|
||||
return NewUnsupportedPrincipalTypeError(params.Principal.EntityType())
|
||||
}
|
||||
|
||||
policies := a.buildPolicies(ctx, params)
|
||||
return a.pg.WithConn(ctx, func(conn pg.Conn) error { return a.authorize(ctx, conn, params) })
|
||||
}
|
||||
|
||||
// Pre-allocate Resource map with capacity for id + attributes
|
||||
resourceAttrs := make(map[string]string, 1+len(params.ResourceAttributes))
|
||||
resourceAttrs["id"] = params.Resource.String()
|
||||
maps.Copy(resourceAttrs, params.ResourceAttributes)
|
||||
|
||||
conditionCtx := policy.ConditionContext{
|
||||
Principal: map[string]string{
|
||||
"id": params.Principal.String(),
|
||||
},
|
||||
Resource: resourceAttrs,
|
||||
func (a *Authorizer) authorize(ctx context.Context, conn pg.Conn, params AuthorizeParams) error {
|
||||
memberships, err := a.loadMemberships(ctx, conn, params.Principal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resourceAttrs, err := a.buildResourceAttributes(ctx, conn, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find role for resource's organization
|
||||
resourceOrgID := resourceAttrs["organization_id"]
|
||||
role := findRoleForOrg(memberships, resourceOrgID)
|
||||
|
||||
// Only set principal.organization_id if they have a role in this org
|
||||
var principalOrgID string
|
||||
if role != "" {
|
||||
principalOrgID = resourceOrgID
|
||||
}
|
||||
|
||||
principalAttrs, err := a.buildPrincipalAttributes(ctx, conn, params.Principal, principalOrgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
policies := a.buildPoliciesForRole(role)
|
||||
|
||||
req := policy.AuthorizationRequest{
|
||||
Principal: params.Principal,
|
||||
Resource: params.Resource,
|
||||
Action: params.Action,
|
||||
ConditionContext: conditionCtx,
|
||||
Principal: params.Principal,
|
||||
Resource: params.Resource,
|
||||
Action: params.Action,
|
||||
ConditionContext: policy.ConditionContext{
|
||||
Principal: principalAttrs,
|
||||
Resource: resourceAttrs,
|
||||
},
|
||||
}
|
||||
|
||||
result := a.evaluator.Evaluate(req, policies)
|
||||
|
||||
if result.IsAllowed() {
|
||||
if a.evaluator.Evaluate(req, policies).IsAllowed() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return NewInsufficientPermissionsError(params.Principal, params.Resource, params.Action)
|
||||
}
|
||||
|
||||
func (a *Authorizer) buildPolicies(ctx context.Context, params AuthorizeParams) []*policy.Policy {
|
||||
selfManageCount := len(a.policySet.SelfManagePolicies)
|
||||
func (a *Authorizer) loadMemberships(ctx context.Context, conn pg.Conn, principalID gid.GID) (coredata.Memberships, error) {
|
||||
var memberships coredata.Memberships
|
||||
if err := memberships.LoadAllByIdentityID(ctx, conn, principalID); err != nil {
|
||||
return nil, fmt.Errorf("cannot load memberships: %w", err)
|
||||
}
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
var rolePolicies []*policy.Policy
|
||||
if params.Resource.TenantID() != gid.NilTenant {
|
||||
rolePolicies = a.loadRolePolicies(ctx, params.Principal, params.Resource)
|
||||
func (a *Authorizer) buildPrincipalAttributes(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
principalID gid.GID,
|
||||
organizationID string,
|
||||
) (map[string]string, error) {
|
||||
attrs := map[string]string{
|
||||
"id": principalID.String(),
|
||||
"organization_id": organizationID,
|
||||
}
|
||||
|
||||
totalCount := selfManageCount + len(rolePolicies)
|
||||
policies := make([]*policy.Policy, selfManageCount, totalCount)
|
||||
copy(policies, a.policySet.SelfManagePolicies)
|
||||
policies = append(policies, rolePolicies...)
|
||||
if entity, ok := coredata.NewEntityFromID(principalID); ok {
|
||||
if attributer, ok := entity.(AuthorizationAttributer); ok {
|
||||
entityAttrs, err := attributer.AuthorizationAttributes(ctx, conn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot load principal attributes: %w", err)
|
||||
}
|
||||
maps.Copy(attrs, entityAttrs)
|
||||
}
|
||||
}
|
||||
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
func (a *Authorizer) buildResourceAttributes(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
params AuthorizeParams,
|
||||
) (map[string]string, error) {
|
||||
attrs := map[string]string{
|
||||
"id": params.Resource.String(),
|
||||
}
|
||||
|
||||
entity, ok := coredata.NewEntityFromID(params.Resource)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported resource type: %d", params.Resource.EntityType())
|
||||
}
|
||||
|
||||
attributer, ok := entity.(AuthorizationAttributer)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("resource %d does not implement AuthorizationAttributer", params.Resource.EntityType())
|
||||
}
|
||||
|
||||
entityAttrs, err := attributer.AuthorizationAttributes(ctx, conn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot load resource attributes: %w", err)
|
||||
}
|
||||
maps.Copy(attrs, entityAttrs)
|
||||
|
||||
if params.ResourceAttributes != nil {
|
||||
maps.Copy(attrs, params.ResourceAttributes)
|
||||
}
|
||||
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
func (a *Authorizer) buildPoliciesForRole(role string) []*policy.Policy {
|
||||
policies := append([]*policy.Policy{}, a.policySet.IdentityScopedPolicies...)
|
||||
|
||||
if role != "" {
|
||||
policies = append(policies, a.policySet.RolePolicies[role]...)
|
||||
}
|
||||
|
||||
return policies
|
||||
}
|
||||
|
||||
func (a *Authorizer) loadRolePolicies(ctx context.Context, principalID gid.GID, resourceID gid.GID) []*policy.Policy {
|
||||
var role coredata.MembershipRole
|
||||
|
||||
err := a.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) (err error) {
|
||||
scope := coredata.NewScopeFromObjectID(resourceID)
|
||||
role, err = coredata.LoadRoleByIdentityAndEntityIDOnly(ctx, conn, scope, principalID, resourceID)
|
||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||
return nil // No membership = no role-based policies
|
||||
}
|
||||
|
||||
return err
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil || role == "" {
|
||||
return nil
|
||||
func findRoleForOrg(memberships coredata.Memberships, orgID string) string {
|
||||
for _, m := range memberships {
|
||||
if m.OrganizationID.String() == orgID {
|
||||
return string(m.Role)
|
||||
}
|
||||
}
|
||||
|
||||
return a.policySet.RolePolicies[role.String()]
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ var IAMSelfManageSessionPolicy = policy.NewPolicy(
|
||||
ActionIAMSessionRevoke,
|
||||
ActionIAMSessionRevokeAll,
|
||||
).WithSID("manage-own-sessions").
|
||||
When(policy.Equals("principal.id", "resource.user_id")),
|
||||
When(policy.Equals("principal.id", "resource.identity_id")),
|
||||
).WithDescription("Allows users to view and revoke their own sessions")
|
||||
|
||||
// IAMSelfManageInvitationPolicy allows users to manage invitations sent to them.
|
||||
@@ -66,7 +66,7 @@ var IAMSelfManageInvitationPolicy = policy.NewPolicy(
|
||||
ActionIAMInvitationGet,
|
||||
ActionIAMInvitationAccept,
|
||||
).WithSID("manage-own-invitations").
|
||||
When(policy.Equals("principal.id", "resource.user_id")),
|
||||
When(policy.Equals("principal.email", "resource.email")),
|
||||
).WithDescription("Allows users to view and accept invitations sent to them")
|
||||
|
||||
// IAMSelfManageMembershipPolicy allows users to view their own memberships.
|
||||
@@ -77,7 +77,7 @@ var IAMSelfManageMembershipPolicy = policy.NewPolicy(
|
||||
policy.Allow(
|
||||
ActionIAMMembershipGet,
|
||||
).WithSID("view-own-memberships").
|
||||
When(policy.Equals("principal.id", "resource.user_id")),
|
||||
When(policy.Equals("principal.id", "resource.identity_id")),
|
||||
).WithDescription("Allows users to view their organization memberships")
|
||||
|
||||
// IAMSelfManagePersonalAPIKeyPolicy allows users to manage their own API keys.
|
||||
@@ -91,7 +91,7 @@ var IAMSelfManagePersonalAPIKeyPolicy = policy.NewPolicy(
|
||||
ActionIAMPersonalAPIKeyUpdate,
|
||||
ActionIAMPersonalAPIKeyDelete,
|
||||
).WithSID("manage-own-api-keys").
|
||||
When(policy.Equals("principal.id", "resource.user_id")),
|
||||
When(policy.Equals("principal.id", "resource.identity_id")),
|
||||
).WithDescription("Allows users to manage their own personal API keys")
|
||||
|
||||
// IAMOwnerPolicy defines permissions for organization owners.
|
||||
@@ -99,17 +99,20 @@ var IAMOwnerPolicy = policy.NewPolicy(
|
||||
"iam:owner",
|
||||
"Organization Owner",
|
||||
// Full access to organization management
|
||||
policy.Allow("iam:organization:*").WithSID("full-org-access"),
|
||||
// Full access to member management
|
||||
policy.Allow("iam:membership:*").WithSID("full-membership-access"),
|
||||
// Can manage invitations
|
||||
policy.Allow("iam:organization:*").WithSID("full-org-access").When(policy.Equals("principal.organization_id", "resource.id")),
|
||||
// Full access to member management (scoped to own organization)
|
||||
policy.Allow("iam:membership:*").WithSID("full-membership-access").
|
||||
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
||||
// Can manage invitations (scoped to own organization)
|
||||
policy.Allow(
|
||||
ActionIAMInvitationCreate,
|
||||
ActionIAMInvitationGet,
|
||||
ActionIAMInvitationDelete,
|
||||
).WithSID("manage-invitations"),
|
||||
// Full access to SAML configuration management
|
||||
policy.Allow("iam:saml-configuration:*").WithSID("full-saml-access"),
|
||||
).WithSID("manage-invitations").
|
||||
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
||||
// Full access to SAML configuration management (scoped to own organization)
|
||||
policy.Allow("iam:saml-configuration:*").WithSID("full-saml-access").
|
||||
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
||||
).WithDescription("Full IAM access for organization owners")
|
||||
|
||||
// IAMAdminPolicy defines permissions for organization admins.
|
||||
@@ -123,19 +126,22 @@ var IAMAdminPolicy = policy.NewPolicy(
|
||||
ActionIAMOrganizationListMembers,
|
||||
ActionIAMOrganizationListInvitations,
|
||||
ActionIAMOrganizationInviteMember,
|
||||
).WithSID("org-admin-access"),
|
||||
// Can manage memberships (but not remove owner)
|
||||
).WithSID("org-admin-access").When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
||||
// Can manage memberships (scoped to own organization)
|
||||
policy.Allow(
|
||||
ActionIAMMembershipGet,
|
||||
ActionIAMMembershipUpdate,
|
||||
).WithSID("membership-admin-access"),
|
||||
// Can manage invitations
|
||||
).WithSID("membership-admin-access").
|
||||
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
||||
// Can manage invitations (scoped to own organization)
|
||||
policy.Allow(
|
||||
ActionIAMInvitationGet,
|
||||
ActionIAMInvitationDelete,
|
||||
).WithSID("invitation-admin-access"),
|
||||
// Can view and update SAML configurations
|
||||
policy.Allow(ActionIAMSAMLConfigurationGet).WithSID("saml-configuration-admin-access"),
|
||||
).WithSID("invitation-admin-access").
|
||||
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
||||
// Can view SAML configurations (scoped to own organization)
|
||||
policy.Allow(ActionIAMSAMLConfigurationGet).WithSID("saml-configuration-admin-access").
|
||||
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
||||
// Cannot delete organization
|
||||
policy.Deny(ActionIAMOrganizationDelete).WithSID("deny-org-delete"),
|
||||
// Cannot remove members (only owner can)
|
||||
@@ -156,7 +162,8 @@ var IAMViewerPolicy = policy.NewPolicy(
|
||||
policy.Allow(
|
||||
ActionIAMOrganizationGet,
|
||||
ActionIAMOrganizationListMembers,
|
||||
).WithSID("org-viewer-access"),
|
||||
// Can view memberships
|
||||
policy.Allow(ActionIAMMembershipGet).WithSID("membership-viewer-access"),
|
||||
).WithSID("org-viewer-access").When(policy.Equals("principal.organization_id", "resource.id")),
|
||||
// Can view memberships (scoped to own organization)
|
||||
policy.Allow(ActionIAMMembershipGet).WithSID("membership-viewer-access").
|
||||
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
||||
).WithDescription("Read-only IAM access for organization viewers")
|
||||
|
||||
@@ -131,4 +131,3 @@ func ForEntityType(entityType uint16) ResourcePattern {
|
||||
EntityType: &entityType,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
)
|
||||
|
||||
@@ -139,7 +141,22 @@ func (c Condition) Evaluate(ctx ConditionContext) bool {
|
||||
case ConditionIn:
|
||||
for _, v := range c.Values {
|
||||
resolved, ok := resolveValue(v, ctx)
|
||||
if ok && value == resolved {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Support a comma-separated "set" value, e.g.
|
||||
// principal.organization_ids = "org_1,org_2"
|
||||
if strings.Contains(resolved, ",") {
|
||||
for _, item := range strings.Split(resolved, ",") {
|
||||
if value == strings.TrimSpace(item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if value == resolved {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -148,7 +165,20 @@ func (c Condition) Evaluate(ctx ConditionContext) bool {
|
||||
case ConditionNotIn:
|
||||
for _, v := range c.Values {
|
||||
resolved, ok := resolveValue(v, ctx)
|
||||
if ok && value == resolved {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(resolved, ",") {
|
||||
for _, item := range strings.Split(resolved, ",") {
|
||||
if value == strings.TrimSpace(item) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if value == resolved {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,15 +88,28 @@ func TestCondition_Evaluate_Equals(t *testing.T) {
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "equals - match resource.user_id reference",
|
||||
name: "equals - match resource.identity_id reference",
|
||||
condition: Condition{
|
||||
Operator: ConditionEquals,
|
||||
Key: "principal.id",
|
||||
Values: []string{"resource.user_id"},
|
||||
Values: []string{"resource.identity_id"},
|
||||
},
|
||||
ctx: ConditionContext{
|
||||
Principal: map[string]string{"id": "user_123"},
|
||||
Resource: map[string]string{"user_id": "user_123"},
|
||||
Resource: map[string]string{"identity_id": "user_123"},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "equals - match principal.email to resource.email reference",
|
||||
condition: Condition{
|
||||
Operator: ConditionEquals,
|
||||
Key: "principal.email",
|
||||
Values: []string{"resource.email"},
|
||||
},
|
||||
ctx: ConditionContext{
|
||||
Principal: map[string]string{"email": "user@example.com"},
|
||||
Resource: map[string]string{"email": "user@example.com"},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
|
||||
@@ -16,21 +16,21 @@ package iam
|
||||
|
||||
import "go.probo.inc/probo/pkg/iam/policy"
|
||||
|
||||
// PolicySet holds role-based and self-management policies.
|
||||
// PolicySet holds organization-scoped (role) policies and identity-scoped policies.
|
||||
// Services create their own PolicySet and combine them when creating the Authorizer.
|
||||
type PolicySet struct {
|
||||
// RolePolicies maps role names to policies.
|
||||
RolePolicies map[string][]*policy.Policy
|
||||
|
||||
// SelfManagePolicies are applied to all authenticated users.
|
||||
SelfManagePolicies []*policy.Policy
|
||||
// IdentityScopedPolicies are applied to all authenticated users, independent of organization membership.
|
||||
IdentityScopedPolicies []*policy.Policy
|
||||
}
|
||||
|
||||
// NewPolicySet creates an empty PolicySet.
|
||||
func NewPolicySet() *PolicySet {
|
||||
return &PolicySet{
|
||||
RolePolicies: make(map[string][]*policy.Policy),
|
||||
SelfManagePolicies: make([]*policy.Policy, 0),
|
||||
RolePolicies: make(map[string][]*policy.Policy),
|
||||
IdentityScopedPolicies: make([]*policy.Policy, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ func (ps *PolicySet) AddRolePolicy(role string, policies ...*policy.Policy) *Pol
|
||||
return ps
|
||||
}
|
||||
|
||||
// AddSelfManagePolicy adds policies applied to all authenticated users.
|
||||
func (ps *PolicySet) AddSelfManagePolicy(policies ...*policy.Policy) *PolicySet {
|
||||
ps.SelfManagePolicies = append(ps.SelfManagePolicies, policies...)
|
||||
// AddIdentityScopedPolicy adds policies applied to all authenticated users (identity-scoped).
|
||||
func (ps *PolicySet) AddIdentityScopedPolicy(policies ...*policy.Policy) *PolicySet {
|
||||
ps.IdentityScopedPolicies = append(ps.IdentityScopedPolicies, policies...)
|
||||
return ps
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func (ps *PolicySet) Merge(other *PolicySet) *PolicySet {
|
||||
for role, policies := range other.RolePolicies {
|
||||
ps.RolePolicies[role] = append(ps.RolePolicies[role], policies...)
|
||||
}
|
||||
ps.SelfManagePolicies = append(ps.SelfManagePolicies, other.SelfManagePolicies...)
|
||||
ps.IdentityScopedPolicies = append(ps.IdentityScopedPolicies, other.IdentityScopedPolicies...)
|
||||
return ps
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func IAMPolicySet() *PolicySet {
|
||||
AddRolePolicy("VIEWER", IAMViewerPolicy).
|
||||
AddRolePolicy("EMPLOYEE", IAMViewerPolicy).
|
||||
AddRolePolicy("AUDITOR", IAMViewerPolicy).
|
||||
AddSelfManagePolicy(
|
||||
AddIdentityScopedPolicy(
|
||||
IAMSelfManageIdentityPolicy,
|
||||
IAMSelfManageSessionPolicy,
|
||||
IAMSelfManageInvitationPolicy,
|
||||
|
||||
@@ -28,13 +28,13 @@ func TestPolicySet_AddAndMerge(t *testing.T) {
|
||||
iamPolicies := NewPolicySet().
|
||||
AddRolePolicy("OWNER", policy.NewPolicy("iam-owner", "IAM Owner", policy.Allow("iam:*"))).
|
||||
AddRolePolicy("ADMIN", policy.NewPolicy("iam-admin", "IAM Admin", policy.Allow("iam:read:*"))).
|
||||
AddSelfManagePolicy(policy.NewPolicy("iam-self", "IAM Self", policy.Allow("iam:identity:get")))
|
||||
AddIdentityScopedPolicy(policy.NewPolicy("iam-self", "IAM Self", policy.Allow("iam:identity:get")))
|
||||
|
||||
// Create second policy set (simulating Documents service)
|
||||
docsPolicies := NewPolicySet().
|
||||
AddRolePolicy("OWNER", policy.NewPolicy("docs-owner", "Docs Owner", policy.Allow("docs:*"))).
|
||||
AddRolePolicy("VIEWER", policy.NewPolicy("docs-viewer", "Docs Viewer", policy.Allow("docs:read:*"))).
|
||||
AddSelfManagePolicy(policy.NewPolicy("docs-self", "Docs Self", policy.Allow("docs:own:*")))
|
||||
AddIdentityScopedPolicy(policy.NewPolicy("docs-self", "Docs Self", policy.Allow("docs:own:*")))
|
||||
|
||||
// Merge them
|
||||
combined := iamPolicies.Merge(docsPolicies)
|
||||
@@ -53,8 +53,8 @@ func TestPolicySet_AddAndMerge(t *testing.T) {
|
||||
require.Len(t, viewerPolicies, 1, "should have 1 VIEWER policy")
|
||||
|
||||
// Test self-manage policies from both services
|
||||
selfPolicies := combined.SelfManagePolicies
|
||||
require.Len(t, selfPolicies, 2, "should have 2 self-manage policies")
|
||||
identityPolicies := combined.IdentityScopedPolicies
|
||||
require.Len(t, identityPolicies, 2, "should have 2 identity-scoped policies")
|
||||
}
|
||||
|
||||
func TestIAMPolicySet(t *testing.T) {
|
||||
@@ -69,5 +69,5 @@ func TestIAMPolicySet(t *testing.T) {
|
||||
}
|
||||
|
||||
// Should have self-manage policies
|
||||
assert.NotEmpty(t, policySet.SelfManagePolicies, "expected self-manage policies")
|
||||
assert.NotEmpty(t, policySet.IdentityScopedPolicies, "expected identity-scoped policies")
|
||||
}
|
||||
|
||||
@@ -247,3 +247,28 @@ func (s *Service) GetSAMLconfiguration(ctx context.Context, samlConfigurationID
|
||||
|
||||
return samlConfiguration, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetPersonalAPIKey(ctx context.Context, personalAPIKeyID gid.GID) (*coredata.PersonalAPIKey, error) {
|
||||
personalAPIKey := &coredata.PersonalAPIKey{}
|
||||
|
||||
err := s.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
err := personalAPIKey.LoadByID(ctx, conn, personalAPIKeyID)
|
||||
if err != nil {
|
||||
if err == coredata.ErrResourceNotFound {
|
||||
return NewPersonalAPIKeyNotFoundError(personalAPIKeyID)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot load personal API key: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return personalAPIKey, nil
|
||||
}
|
||||
|
||||
@@ -19,34 +19,32 @@ import (
|
||||
"go.probo.inc/probo/pkg/iam/policy"
|
||||
)
|
||||
|
||||
var organizationCondition = policy.Equals("principal.organization_id", "resource.organization_id")
|
||||
|
||||
// OwnerPolicy defines permissions for organization owners.
|
||||
var OwnerPolicy = policy.NewPolicy(
|
||||
"probo:owner",
|
||||
"Probo Owner",
|
||||
// Full access to all probo resources
|
||||
policy.Allow("core:*").WithSID("full-core-access"),
|
||||
policy.Allow("core:*").WithSID("full-core-access").When(organizationCondition),
|
||||
).WithDescription("Full probo access for organization owners")
|
||||
|
||||
// AdminPolicy defines permissions for organization admins.
|
||||
var AdminPolicy = policy.NewPolicy(
|
||||
"probo:admin",
|
||||
"Probo Admin",
|
||||
// Full access to all probo resources (same as owner for core entities)
|
||||
policy.Allow("core:*").WithSID("full-c ore-access"),
|
||||
policy.Allow("core:*").WithSID("full-core-access").When(organizationCondition),
|
||||
).WithDescription("Probo admin access - can manage core entities")
|
||||
|
||||
// ViewerPolicy defines read-only permissions for organization viewers.
|
||||
var ViewerPolicy = policy.NewPolicy(
|
||||
"probo:viewer",
|
||||
"Probo Viewer",
|
||||
// Organization read actions
|
||||
policy.Allow(
|
||||
ActionOrganizationGet,
|
||||
ActionOrganizationGetLogoUrl,
|
||||
ActionOrganizationGetHorizontalLogoUrl,
|
||||
).WithSID("org-read-access"),
|
||||
).WithSID("org-read-access").When(organizationCondition),
|
||||
|
||||
// Entity read actions
|
||||
policy.Allow(
|
||||
ActionPeopleGet, ActionPeopleList,
|
||||
ActionVendorGet, ActionVendorList,
|
||||
@@ -79,42 +77,33 @@ var ViewerPolicy = policy.NewPolicy(
|
||||
ActionMeetingGet, ActionMeetingList,
|
||||
ActionFileGet, ActionFileDownloadUrl,
|
||||
ActionSlackConnectionList,
|
||||
).WithSID("entity-read-access"),
|
||||
).WithSID("entity-read-access").When(organizationCondition),
|
||||
|
||||
// TrustCenter read actions
|
||||
policy.Allow(
|
||||
ActionTrustCenterGet,
|
||||
ActionTrustCenterAccessGet, ActionTrustCenterAccessList,
|
||||
ActionTrustCenterDocumentAccessList,
|
||||
ActionTrustCenterFileGet, ActionTrustCenterFileList, ActionTrustCenterFileGetFileUrl,
|
||||
ActionTrustCenterReferenceList, ActionTrustCenterReferenceGetLogoUrl,
|
||||
).WithSID("trust-center-read-access"),
|
||||
).WithSID("trust-center-read-access").When(organizationCondition),
|
||||
|
||||
// CustomDomain read actions
|
||||
policy.Allow(ActionCustomDomainGet).WithSID("custom-domain-read"),
|
||||
|
||||
// OrganizationContext read actions
|
||||
policy.Allow(ActionOrganizationContextGet).WithSID("organization-context-read"),
|
||||
|
||||
// Document signing actions
|
||||
policy.Allow(ActionCustomDomainGet).WithSID("custom-domain-read").When(organizationCondition),
|
||||
policy.Allow(ActionOrganizationContextGet).WithSID("organization-context-read").When(organizationCondition),
|
||||
policy.Allow(
|
||||
ActionDocumentVersionExportPDF, ActionDocumentVersionExportSignable, ActionDocumentVersionSign,
|
||||
).WithSID("document-signing"),
|
||||
).WithSID("document-signing").When(organizationCondition),
|
||||
).WithDescription("Read-only probo access for organization viewers")
|
||||
|
||||
// AuditorPolicy defines permissions for auditor role.
|
||||
// Auditors have read access to non-employee content plus some specific auditor features.
|
||||
var AuditorPolicy = policy.NewPolicy(
|
||||
"probo:auditor",
|
||||
"Probo Auditor",
|
||||
// Same as viewer but without employee-specific content
|
||||
policy.Allow(
|
||||
ActionOrganizationGet,
|
||||
ActionOrganizationGetLogoUrl,
|
||||
ActionOrganizationGetHorizontalLogoUrl,
|
||||
).WithSID("org-read-access"),
|
||||
).WithSID("org-read-access").When(organizationCondition),
|
||||
|
||||
// Entity read access (same as viewer)
|
||||
policy.Allow(
|
||||
ActionPeopleGet, ActionPeopleList,
|
||||
ActionVendorGet, ActionVendorList,
|
||||
@@ -145,40 +134,34 @@ var AuditorPolicy = policy.NewPolicy(
|
||||
ActionSnapshotGet, ActionSnapshotList,
|
||||
ActionMeetingGet, ActionMeetingList,
|
||||
ActionFileGet, ActionFileDownloadUrl,
|
||||
).WithSID("entity-read-access"),
|
||||
).WithSID("entity-read-access").When(organizationCondition),
|
||||
|
||||
// Document signing actions
|
||||
policy.Allow(
|
||||
ActionDocumentVersionExportPDF, ActionDocumentVersionExportSignable, ActionDocumentVersionSign,
|
||||
).WithSID("document-signing"),
|
||||
).WithSID("document-signing").When(organizationCondition),
|
||||
).WithDescription("Read-only probo access for auditors (excludes internal/employee content)")
|
||||
|
||||
// EmployeePolicy defines permissions for employee role.
|
||||
// Employees have access to internal documents and some limited read access.
|
||||
var EmployeePolicy = policy.NewPolicy(
|
||||
"probo:employee",
|
||||
"Probo Employee",
|
||||
// Basic organization access
|
||||
policy.Allow(
|
||||
ActionOrganizationGet,
|
||||
ActionOrganizationGetLogoUrl,
|
||||
).WithSID("org-basic-access"),
|
||||
).WithSID("org-basic-access").When(organizationCondition),
|
||||
|
||||
// Document signing access
|
||||
policy.Allow(
|
||||
ActionDocumentGet, ActionDocumentList,
|
||||
).WithSID("document-signing-access"),
|
||||
).WithSID("document-signing-access").When(organizationCondition),
|
||||
|
||||
// Document version signing
|
||||
policy.Allow(
|
||||
ActionDocumentVersionGet, ActionDocumentVersionList,
|
||||
ActionDocumentVersionSign,
|
||||
ActionDocumentVersionExportSignable,
|
||||
).WithSID("document-version-signing"),
|
||||
).WithSID("document-version-signing").When(organizationCondition),
|
||||
).WithDescription("Employee access - can sign documents and view internal content")
|
||||
|
||||
// ProboPolicySet returns the PolicySet for the probo service.
|
||||
// This is registered with the IAM Authorizer when probo.Service is created.
|
||||
func ProboPolicySet() *iam.PolicySet {
|
||||
return iam.NewPolicySet().
|
||||
AddRolePolicy("OWNER", OwnerPolicy).
|
||||
|
||||
@@ -86,9 +86,6 @@ type Mutation {
|
||||
createPersonalAPIKey(
|
||||
input: CreatePersonalAPIKeyInput!
|
||||
): CreatePersonalAPIKeyPayload @session(required: PRESENT)
|
||||
revealPersonalAPIKeyToken(
|
||||
input: RevealPersonalAPIKeyTokenInput!
|
||||
): RevealPersonalAPIKeyTokenPayload @session(required: PRESENT)
|
||||
revokePersonalAPIKey(
|
||||
input: RevokePersonalAPIKeyInput!
|
||||
): RevokePersonalAPIKeyPayload @session(required: PRESENT)
|
||||
@@ -285,11 +282,10 @@ type Session implements Node {
|
||||
type PersonalAPIKey implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
lastUsedAt: Datetime
|
||||
expiresAt: Datetime!
|
||||
createdAt: Datetime!
|
||||
scopes: [TokenScope!]!
|
||||
organizations: [Organization!]!
|
||||
|
||||
token: String @goField(forceResolver: true)
|
||||
|
||||
permission(action: String!): Boolean!
|
||||
@goField(forceResolver: true)
|
||||
@@ -576,15 +572,10 @@ input RevokeSessionInput {
|
||||
input CreatePersonalAPIKeyInput {
|
||||
name: String!
|
||||
expiresAt: Datetime!
|
||||
organizationIds: [ID!]!
|
||||
}
|
||||
|
||||
input RevokePersonalAPIKeyInput {
|
||||
tokenId: ID!
|
||||
}
|
||||
|
||||
input RevealPersonalAPIKeyTokenInput {
|
||||
tokenId: ID!
|
||||
personalAPIKeyId: ID!
|
||||
}
|
||||
|
||||
input CreateOrganizationInput {
|
||||
@@ -751,11 +742,7 @@ type CreatePersonalAPIKeyPayload {
|
||||
}
|
||||
|
||||
type RevokePersonalAPIKeyPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type RevealPersonalAPIKeyTokenPayload {
|
||||
token: String!
|
||||
personalAPIKeyId: ID!
|
||||
}
|
||||
|
||||
type CreateOrganizationPayload {
|
||||
|
||||
@@ -218,7 +218,6 @@ type ComplexityRoot struct {
|
||||
InviteMember func(childComplexity int, input types.InviteMemberInput) int
|
||||
RemoveMember func(childComplexity int, input types.RemoveMemberInput) int
|
||||
ResetPassword func(childComplexity int, input types.ResetPasswordInput) int
|
||||
RevealPersonalAPIKeyToken func(childComplexity int, input types.RevealPersonalAPIKeyTokenInput) int
|
||||
RevokeAllSessions func(childComplexity int) int
|
||||
RevokePersonalAPIKey func(childComplexity int, input types.RevokePersonalAPIKeyInput) int
|
||||
RevokeSession func(childComplexity int, input types.RevokeSessionInput) int
|
||||
@@ -267,14 +266,12 @@ type ComplexityRoot struct {
|
||||
}
|
||||
|
||||
PersonalAPIKey struct {
|
||||
CreatedAt func(childComplexity int) int
|
||||
ExpiresAt func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
LastUsedAt func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
Organizations func(childComplexity int) int
|
||||
Permission func(childComplexity int, action string) int
|
||||
Scopes func(childComplexity int) int
|
||||
CreatedAt func(childComplexity int) int
|
||||
ExpiresAt func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
Permission func(childComplexity int, action string) int
|
||||
Token func(childComplexity int) int
|
||||
}
|
||||
|
||||
PersonalAPIKeyConnection struct {
|
||||
@@ -302,16 +299,12 @@ type ComplexityRoot struct {
|
||||
Success func(childComplexity int) int
|
||||
}
|
||||
|
||||
RevealPersonalAPIKeyTokenPayload struct {
|
||||
Token func(childComplexity int) int
|
||||
}
|
||||
|
||||
RevokeAllSessionsPayload struct {
|
||||
RevokedCount func(childComplexity int) int
|
||||
}
|
||||
|
||||
RevokePersonalAPIKeyPayload struct {
|
||||
Success func(childComplexity int) int
|
||||
PersonalAPIKeyID func(childComplexity int) int
|
||||
}
|
||||
|
||||
RevokeSessionPayload struct {
|
||||
@@ -462,7 +455,6 @@ type MutationResolver interface {
|
||||
RevokeSession(ctx context.Context, input types.RevokeSessionInput) (*types.RevokeSessionPayload, error)
|
||||
RevokeAllSessions(ctx context.Context) (*types.RevokeAllSessionsPayload, error)
|
||||
CreatePersonalAPIKey(ctx context.Context, input types.CreatePersonalAPIKeyInput) (*types.CreatePersonalAPIKeyPayload, error)
|
||||
RevealPersonalAPIKeyToken(ctx context.Context, input types.RevealPersonalAPIKeyTokenInput) (*types.RevealPersonalAPIKeyTokenPayload, error)
|
||||
RevokePersonalAPIKey(ctx context.Context, input types.RevokePersonalAPIKeyInput) (*types.RevokePersonalAPIKeyPayload, error)
|
||||
CreateOrganization(ctx context.Context, input types.CreateOrganizationInput) (*types.CreateOrganizationPayload, error)
|
||||
UpdateOrganization(ctx context.Context, input types.UpdateOrganizationInput) (*types.UpdateOrganizationPayload, error)
|
||||
@@ -488,6 +480,7 @@ type OrganizationResolver interface {
|
||||
Permission(ctx context.Context, obj *types.Organization, action string) (bool, error)
|
||||
}
|
||||
type PersonalAPIKeyResolver interface {
|
||||
Token(ctx context.Context, obj *types.PersonalAPIKey) (*string, error)
|
||||
Permission(ctx context.Context, obj *types.PersonalAPIKey, action string) (bool, error)
|
||||
}
|
||||
type PersonalAPIKeyConnectionResolver interface {
|
||||
@@ -1146,17 +1139,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.ResetPassword(childComplexity, args["input"].(types.ResetPasswordInput)), true
|
||||
case "Mutation.revealPersonalAPIKeyToken":
|
||||
if e.complexity.Mutation.RevealPersonalAPIKeyToken == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Mutation_revealPersonalAPIKeyToken_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Mutation.RevealPersonalAPIKeyToken(childComplexity, args["input"].(types.RevealPersonalAPIKeyTokenInput)), true
|
||||
case "Mutation.revokeAllSessions":
|
||||
if e.complexity.Mutation.RevokeAllSessions == nil {
|
||||
break
|
||||
@@ -1443,24 +1425,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
}
|
||||
|
||||
return e.complexity.PersonalAPIKey.ID(childComplexity), true
|
||||
case "PersonalAPIKey.lastUsedAt":
|
||||
if e.complexity.PersonalAPIKey.LastUsedAt == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.PersonalAPIKey.LastUsedAt(childComplexity), true
|
||||
case "PersonalAPIKey.name":
|
||||
if e.complexity.PersonalAPIKey.Name == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.PersonalAPIKey.Name(childComplexity), true
|
||||
case "PersonalAPIKey.organizations":
|
||||
if e.complexity.PersonalAPIKey.Organizations == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.PersonalAPIKey.Organizations(childComplexity), true
|
||||
case "PersonalAPIKey.permission":
|
||||
if e.complexity.PersonalAPIKey.Permission == nil {
|
||||
break
|
||||
@@ -1472,12 +1442,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
}
|
||||
|
||||
return e.complexity.PersonalAPIKey.Permission(childComplexity, args["action"].(string)), true
|
||||
case "PersonalAPIKey.scopes":
|
||||
if e.complexity.PersonalAPIKey.Scopes == nil {
|
||||
case "PersonalAPIKey.token":
|
||||
if e.complexity.PersonalAPIKey.Token == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.PersonalAPIKey.Scopes(childComplexity), true
|
||||
return e.complexity.PersonalAPIKey.Token(childComplexity), true
|
||||
|
||||
case "PersonalAPIKeyConnection.edges":
|
||||
if e.complexity.PersonalAPIKeyConnection.Edges == nil {
|
||||
@@ -1554,13 +1524,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
|
||||
return e.complexity.ResetPasswordPayload.Success(childComplexity), true
|
||||
|
||||
case "RevealPersonalAPIKeyTokenPayload.token":
|
||||
if e.complexity.RevealPersonalAPIKeyTokenPayload.Token == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.RevealPersonalAPIKeyTokenPayload.Token(childComplexity), true
|
||||
|
||||
case "RevokeAllSessionsPayload.revokedCount":
|
||||
if e.complexity.RevokeAllSessionsPayload.RevokedCount == nil {
|
||||
break
|
||||
@@ -1568,12 +1531,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
|
||||
return e.complexity.RevokeAllSessionsPayload.RevokedCount(childComplexity), true
|
||||
|
||||
case "RevokePersonalAPIKeyPayload.success":
|
||||
if e.complexity.RevokePersonalAPIKeyPayload.Success == nil {
|
||||
case "RevokePersonalAPIKeyPayload.personalAPIKeyId":
|
||||
if e.complexity.RevokePersonalAPIKeyPayload.PersonalAPIKeyID == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.RevokePersonalAPIKeyPayload.Success(childComplexity), true
|
||||
return e.complexity.RevokePersonalAPIKeyPayload.PersonalAPIKeyID(childComplexity), true
|
||||
|
||||
case "RevokeSessionPayload.success":
|
||||
if e.complexity.RevokeSessionPayload.Success == nil {
|
||||
@@ -1934,7 +1897,6 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
||||
ec.unmarshalInputMembershipOrder,
|
||||
ec.unmarshalInputRemoveMemberInput,
|
||||
ec.unmarshalInputResetPasswordInput,
|
||||
ec.unmarshalInputRevealPersonalAPIKeyTokenInput,
|
||||
ec.unmarshalInputRevokePersonalAPIKeyInput,
|
||||
ec.unmarshalInputRevokeSessionInput,
|
||||
ec.unmarshalInputSAMLAttributeMappingsInput,
|
||||
@@ -2131,9 +2093,6 @@ type Mutation {
|
||||
createPersonalAPIKey(
|
||||
input: CreatePersonalAPIKeyInput!
|
||||
): CreatePersonalAPIKeyPayload @session(required: PRESENT)
|
||||
revealPersonalAPIKeyToken(
|
||||
input: RevealPersonalAPIKeyTokenInput!
|
||||
): RevealPersonalAPIKeyTokenPayload @session(required: PRESENT)
|
||||
revokePersonalAPIKey(
|
||||
input: RevokePersonalAPIKeyInput!
|
||||
): RevokePersonalAPIKeyPayload @session(required: PRESENT)
|
||||
@@ -2330,11 +2289,10 @@ type Session implements Node {
|
||||
type PersonalAPIKey implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
lastUsedAt: Datetime
|
||||
expiresAt: Datetime!
|
||||
createdAt: Datetime!
|
||||
scopes: [TokenScope!]!
|
||||
organizations: [Organization!]!
|
||||
|
||||
token: String @goField(forceResolver: true)
|
||||
|
||||
permission(action: String!): Boolean!
|
||||
@goField(forceResolver: true)
|
||||
@@ -2621,15 +2579,10 @@ input RevokeSessionInput {
|
||||
input CreatePersonalAPIKeyInput {
|
||||
name: String!
|
||||
expiresAt: Datetime!
|
||||
organizationIds: [ID!]!
|
||||
}
|
||||
|
||||
input RevokePersonalAPIKeyInput {
|
||||
tokenId: ID!
|
||||
}
|
||||
|
||||
input RevealPersonalAPIKeyTokenInput {
|
||||
tokenId: ID!
|
||||
personalAPIKeyId: ID!
|
||||
}
|
||||
|
||||
input CreateOrganizationInput {
|
||||
@@ -2796,11 +2749,7 @@ type CreatePersonalAPIKeyPayload {
|
||||
}
|
||||
|
||||
type RevokePersonalAPIKeyPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type RevealPersonalAPIKeyTokenPayload {
|
||||
token: String!
|
||||
personalAPIKeyId: ID!
|
||||
}
|
||||
|
||||
type CreateOrganizationPayload {
|
||||
@@ -3198,17 +3147,6 @@ func (ec *executionContext) field_Mutation_resetPassword_args(ctx context.Contex
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_revealPersonalAPIKeyToken_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
||||
var err error
|
||||
args := map[string]any{}
|
||||
arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNRevealPersonalAPIKeyTokenInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐRevealPersonalAPIKeyTokenInput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args["input"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Mutation_revokePersonalAPIKey_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
|
||||
var err error
|
||||
args := map[string]any{}
|
||||
@@ -6734,69 +6672,6 @@ func (ec *executionContext) fieldContext_Mutation_createPersonalAPIKey(ctx conte
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_revealPersonalAPIKeyToken(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_Mutation_revealPersonalAPIKeyToken,
|
||||
func(ctx context.Context) (any, error) {
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Mutation().RevealPersonalAPIKeyToken(ctx, fc.Args["input"].(types.RevealPersonalAPIKeyTokenInput))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSessionRequirement(ctx, "PRESENT")
|
||||
if err != nil {
|
||||
var zeroVal *types.RevealPersonalAPIKeyTokenPayload
|
||||
return zeroVal, err
|
||||
}
|
||||
if ec.directives.Session == nil {
|
||||
var zeroVal *types.RevealPersonalAPIKeyTokenPayload
|
||||
return zeroVal, errors.New("directive session is not implemented")
|
||||
}
|
||||
return ec.directives.Session(ctx, nil, directive0, required)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
ec.marshalORevealPersonalAPIKeyTokenPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐRevealPersonalAPIKeyTokenPayload,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Mutation_revealPersonalAPIKeyToken(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Mutation",
|
||||
Field: field,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "token":
|
||||
return ec.fieldContext_RevealPersonalAPIKeyTokenPayload_token(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type RevealPersonalAPIKeyTokenPayload", field.Name)
|
||||
},
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = ec.Recover(ctx, r)
|
||||
ec.Error(ctx, err)
|
||||
}
|
||||
}()
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
if fc.Args, err = ec.field_Mutation_revealPersonalAPIKeyToken_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return fc, err
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Mutation_revokePersonalAPIKey(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
@@ -6840,8 +6715,8 @@ func (ec *executionContext) fieldContext_Mutation_revokePersonalAPIKey(ctx conte
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "success":
|
||||
return ec.fieldContext_RevokePersonalAPIKeyPayload_success(ctx, field)
|
||||
case "personalAPIKeyId":
|
||||
return ec.fieldContext_RevokePersonalAPIKeyPayload_personalAPIKeyId(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type RevokePersonalAPIKeyPayload", field.Name)
|
||||
},
|
||||
@@ -8440,35 +8315,6 @@ func (ec *executionContext) fieldContext_PersonalAPIKey_name(_ context.Context,
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PersonalAPIKey_lastUsedAt(ctx context.Context, field graphql.CollectedField, obj *types.PersonalAPIKey) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_PersonalAPIKey_lastUsedAt,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.LastUsedAt, nil
|
||||
},
|
||||
nil,
|
||||
ec.marshalODatetime2ᚖtimeᚐTime,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_PersonalAPIKey_lastUsedAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "PersonalAPIKey",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type Datetime does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PersonalAPIKey_expiresAt(ctx context.Context, field graphql.CollectedField, obj *types.PersonalAPIKey) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
@@ -8527,91 +8373,30 @@ func (ec *executionContext) fieldContext_PersonalAPIKey_createdAt(_ context.Cont
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PersonalAPIKey_scopes(ctx context.Context, field graphql.CollectedField, obj *types.PersonalAPIKey) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _PersonalAPIKey_token(ctx context.Context, field graphql.CollectedField, obj *types.PersonalAPIKey) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_PersonalAPIKey_scopes,
|
||||
ec.fieldContext_PersonalAPIKey_token,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.Scopes, nil
|
||||
return ec.resolvers.PersonalAPIKey().Token(ctx, obj)
|
||||
},
|
||||
nil,
|
||||
ec.marshalNTokenScope2ᚕgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐTokenScopeᚄ,
|
||||
true,
|
||||
ec.marshalOString2ᚖstring,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_PersonalAPIKey_scopes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
func (ec *executionContext) fieldContext_PersonalAPIKey_token(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "PersonalAPIKey",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type TokenScope does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PersonalAPIKey_organizations(ctx context.Context, field graphql.CollectedField, obj *types.PersonalAPIKey) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_PersonalAPIKey_organizations,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.Organizations, nil
|
||||
},
|
||||
nil,
|
||||
ec.marshalNOrganization2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐOrganizationᚄ,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_PersonalAPIKey_organizations(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "PersonalAPIKey",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "id":
|
||||
return ec.fieldContext_Organization_id(ctx, field)
|
||||
case "name":
|
||||
return ec.fieldContext_Organization_name(ctx, field)
|
||||
case "logoUrl":
|
||||
return ec.fieldContext_Organization_logoUrl(ctx, field)
|
||||
case "horizontalLogoUrl":
|
||||
return ec.fieldContext_Organization_horizontalLogoUrl(ctx, field)
|
||||
case "email":
|
||||
return ec.fieldContext_Organization_email(ctx, field)
|
||||
case "description":
|
||||
return ec.fieldContext_Organization_description(ctx, field)
|
||||
case "websiteUrl":
|
||||
return ec.fieldContext_Organization_websiteUrl(ctx, field)
|
||||
case "headquarterAddress":
|
||||
return ec.fieldContext_Organization_headquarterAddress(ctx, field)
|
||||
case "createdAt":
|
||||
return ec.fieldContext_Organization_createdAt(ctx, field)
|
||||
case "updatedAt":
|
||||
return ec.fieldContext_Organization_updatedAt(ctx, field)
|
||||
case "members":
|
||||
return ec.fieldContext_Organization_members(ctx, field)
|
||||
case "invitations":
|
||||
return ec.fieldContext_Organization_invitations(ctx, field)
|
||||
case "samlConfigurations":
|
||||
return ec.fieldContext_Organization_samlConfigurations(ctx, field)
|
||||
case "viewerMembership":
|
||||
return ec.fieldContext_Organization_viewerMembership(ctx, field)
|
||||
case "permission":
|
||||
return ec.fieldContext_Organization_permission(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type Organization", field.Name)
|
||||
return nil, errors.New("field of type String does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
@@ -8807,16 +8592,12 @@ func (ec *executionContext) fieldContext_PersonalAPIKeyEdge_node(_ context.Conte
|
||||
return ec.fieldContext_PersonalAPIKey_id(ctx, field)
|
||||
case "name":
|
||||
return ec.fieldContext_PersonalAPIKey_name(ctx, field)
|
||||
case "lastUsedAt":
|
||||
return ec.fieldContext_PersonalAPIKey_lastUsedAt(ctx, field)
|
||||
case "expiresAt":
|
||||
return ec.fieldContext_PersonalAPIKey_expiresAt(ctx, field)
|
||||
case "createdAt":
|
||||
return ec.fieldContext_PersonalAPIKey_createdAt(ctx, field)
|
||||
case "scopes":
|
||||
return ec.fieldContext_PersonalAPIKey_scopes(ctx, field)
|
||||
case "organizations":
|
||||
return ec.fieldContext_PersonalAPIKey_organizations(ctx, field)
|
||||
case "token":
|
||||
return ec.fieldContext_PersonalAPIKey_token(ctx, field)
|
||||
case "permission":
|
||||
return ec.fieldContext_PersonalAPIKey_permission(ctx, field)
|
||||
}
|
||||
@@ -9218,35 +8999,6 @@ func (ec *executionContext) fieldContext_ResetPasswordPayload_success(_ context.
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _RevealPersonalAPIKeyTokenPayload_token(ctx context.Context, field graphql.CollectedField, obj *types.RevealPersonalAPIKeyTokenPayload) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_RevealPersonalAPIKeyTokenPayload_token,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.Token, nil
|
||||
},
|
||||
nil,
|
||||
ec.marshalNString2string,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_RevealPersonalAPIKeyTokenPayload_token(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "RevealPersonalAPIKeyTokenPayload",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type String does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _RevokeAllSessionsPayload_revokedCount(ctx context.Context, field graphql.CollectedField, obj *types.RevokeAllSessionsPayload) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
@@ -9276,30 +9028,30 @@ func (ec *executionContext) fieldContext_RevokeAllSessionsPayload_revokedCount(_
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _RevokePersonalAPIKeyPayload_success(ctx context.Context, field graphql.CollectedField, obj *types.RevokePersonalAPIKeyPayload) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _RevokePersonalAPIKeyPayload_personalAPIKeyId(ctx context.Context, field graphql.CollectedField, obj *types.RevokePersonalAPIKeyPayload) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_RevokePersonalAPIKeyPayload_success,
|
||||
ec.fieldContext_RevokePersonalAPIKeyPayload_personalAPIKeyId,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.Success, nil
|
||||
return obj.PersonalAPIKeyID, nil
|
||||
},
|
||||
nil,
|
||||
ec.marshalNBoolean2bool,
|
||||
ec.marshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_RevokePersonalAPIKeyPayload_success(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
func (ec *executionContext) fieldContext_RevokePersonalAPIKeyPayload_personalAPIKeyId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "RevokePersonalAPIKeyPayload",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type Boolean does not have child fields")
|
||||
return nil, errors.New("field of type ID does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
@@ -12757,7 +12509,7 @@ func (ec *executionContext) unmarshalInputCreatePersonalAPIKeyInput(ctx context.
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"name", "expiresAt", "organizationIds"}
|
||||
fieldsInOrder := [...]string{"name", "expiresAt"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
@@ -12778,13 +12530,6 @@ func (ec *executionContext) unmarshalInputCreatePersonalAPIKeyInput(ctx context.
|
||||
return it, err
|
||||
}
|
||||
it.ExpiresAt = data
|
||||
case "organizationIds":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("organizationIds"))
|
||||
data, err := ec.unmarshalNID2ᚕgoᚗproboᚗincᚋproboᚋpkgᚋgidᚐGIDᚄ(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.OrganizationIds = data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13200,33 +12945,6 @@ func (ec *executionContext) unmarshalInputResetPasswordInput(ctx context.Context
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputRevealPersonalAPIKeyTokenInput(ctx context.Context, obj any) (types.RevealPersonalAPIKeyTokenInput, error) {
|
||||
var it types.RevealPersonalAPIKeyTokenInput
|
||||
asMap := map[string]any{}
|
||||
for k, v := range obj.(map[string]any) {
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"tokenId"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch k {
|
||||
case "tokenId":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tokenId"))
|
||||
data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.TokenID = data
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputRevokePersonalAPIKeyInput(ctx context.Context, obj any) (types.RevokePersonalAPIKeyInput, error) {
|
||||
var it types.RevokePersonalAPIKeyInput
|
||||
asMap := map[string]any{}
|
||||
@@ -13234,20 +12952,20 @@ func (ec *executionContext) unmarshalInputRevokePersonalAPIKeyInput(ctx context.
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
fieldsInOrder := [...]string{"tokenId"}
|
||||
fieldsInOrder := [...]string{"personalAPIKeyId"}
|
||||
for _, k := range fieldsInOrder {
|
||||
v, ok := asMap[k]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch k {
|
||||
case "tokenId":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tokenId"))
|
||||
case "personalAPIKeyId":
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("personalAPIKeyId"))
|
||||
data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
it.TokenID = data
|
||||
it.PersonalAPIKeyID = data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15397,10 +15115,6 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||
return ec._Mutation_createPersonalAPIKey(ctx, field)
|
||||
})
|
||||
case "revealPersonalAPIKeyToken":
|
||||
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||
return ec._Mutation_revealPersonalAPIKeyToken(ctx, field)
|
||||
})
|
||||
case "revokePersonalAPIKey":
|
||||
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||
return ec._Mutation_revokePersonalAPIKey(ctx, field)
|
||||
@@ -15927,8 +15641,6 @@ func (ec *executionContext) _PersonalAPIKey(ctx context.Context, sel ast.Selecti
|
||||
if out.Values[i] == graphql.Null {
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "lastUsedAt":
|
||||
out.Values[i] = ec._PersonalAPIKey_lastUsedAt(ctx, field, obj)
|
||||
case "expiresAt":
|
||||
out.Values[i] = ec._PersonalAPIKey_expiresAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
@@ -15939,16 +15651,39 @@ func (ec *executionContext) _PersonalAPIKey(ctx context.Context, sel ast.Selecti
|
||||
if out.Values[i] == graphql.Null {
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "scopes":
|
||||
out.Values[i] = ec._PersonalAPIKey_scopes(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
case "token":
|
||||
field := field
|
||||
|
||||
innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._PersonalAPIKey_token(ctx, field, obj)
|
||||
return res
|
||||
}
|
||||
case "organizations":
|
||||
out.Values[i] = ec._PersonalAPIKey_organizations(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
|
||||
if field.Deferrable != nil {
|
||||
dfs, ok := deferred[field.Deferrable.Label]
|
||||
di := 0
|
||||
if ok {
|
||||
dfs.AddField(field)
|
||||
di = len(dfs.Values) - 1
|
||||
} else {
|
||||
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
|
||||
deferred[field.Deferrable.Label] = dfs
|
||||
}
|
||||
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
|
||||
return innerFunc(ctx, dfs)
|
||||
})
|
||||
|
||||
// don't run the out.Concurrently() call below
|
||||
out.Values[i] = graphql.Null
|
||||
continue
|
||||
}
|
||||
|
||||
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
|
||||
case "permission":
|
||||
field := field
|
||||
|
||||
@@ -16317,45 +16052,6 @@ func (ec *executionContext) _ResetPasswordPayload(ctx context.Context, sel ast.S
|
||||
return out
|
||||
}
|
||||
|
||||
var revealPersonalAPIKeyTokenPayloadImplementors = []string{"RevealPersonalAPIKeyTokenPayload"}
|
||||
|
||||
func (ec *executionContext) _RevealPersonalAPIKeyTokenPayload(ctx context.Context, sel ast.SelectionSet, obj *types.RevealPersonalAPIKeyTokenPayload) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, revealPersonalAPIKeyTokenPayloadImplementors)
|
||||
|
||||
out := graphql.NewFieldSet(fields)
|
||||
deferred := make(map[string]*graphql.FieldSet)
|
||||
for i, field := range fields {
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("RevealPersonalAPIKeyTokenPayload")
|
||||
case "token":
|
||||
out.Values[i] = ec._RevealPersonalAPIKeyTokenPayload_token(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
}
|
||||
out.Dispatch(ctx)
|
||||
if out.Invalids > 0 {
|
||||
return graphql.Null
|
||||
}
|
||||
|
||||
atomic.AddInt32(&ec.deferred, int32(len(deferred)))
|
||||
|
||||
for label, dfs := range deferred {
|
||||
ec.processDeferredGroup(graphql.DeferredGroup{
|
||||
Label: label,
|
||||
Path: graphql.GetPath(ctx),
|
||||
FieldSet: dfs,
|
||||
Context: ctx,
|
||||
})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
var revokeAllSessionsPayloadImplementors = []string{"RevokeAllSessionsPayload"}
|
||||
|
||||
func (ec *executionContext) _RevokeAllSessionsPayload(ctx context.Context, sel ast.SelectionSet, obj *types.RevokeAllSessionsPayload) graphql.Marshaler {
|
||||
@@ -16406,8 +16102,8 @@ func (ec *executionContext) _RevokePersonalAPIKeyPayload(ctx context.Context, se
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("RevokePersonalAPIKeyPayload")
|
||||
case "success":
|
||||
out.Values[i] = ec._RevokePersonalAPIKeyPayload_success(ctx, field, obj)
|
||||
case "personalAPIKeyId":
|
||||
out.Values[i] = ec._RevokePersonalAPIKeyPayload_personalAPIKeyId(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
@@ -18012,36 +17708,6 @@ func (ec *executionContext) marshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGI
|
||||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNID2ᚕgoᚗproboᚗincᚋproboᚋpkgᚋgidᚐGIDᚄ(ctx context.Context, v any) ([]gid.GID, error) {
|
||||
var vSlice []any
|
||||
vSlice = graphql.CoerceList(v)
|
||||
var err error
|
||||
res := make([]gid.GID, len(vSlice))
|
||||
for i := range vSlice {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
|
||||
res[i], err = ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, vSlice[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNID2ᚕgoᚗproboᚗincᚋproboᚋpkgᚋgidᚐGIDᚄ(ctx context.Context, sel ast.SelectionSet, v []gid.GID) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
for i := range v {
|
||||
ret[i] = ec.marshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, sel, v[i])
|
||||
}
|
||||
|
||||
for _, e := range ret {
|
||||
if e == graphql.Null {
|
||||
return graphql.Null
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v any) (int, error) {
|
||||
res, err := graphql.UnmarshalInt(v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
@@ -18345,50 +18011,6 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func (ec *executionContext) marshalNOrganization2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐOrganizationᚄ(ctx context.Context, sel ast.SelectionSet, v []*types.Organization) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
isLen1 := len(v) == 1
|
||||
if !isLen1 {
|
||||
wg.Add(len(v))
|
||||
}
|
||||
for i := range v {
|
||||
i := i
|
||||
fc := &graphql.FieldContext{
|
||||
Index: &i,
|
||||
Result: &v[i],
|
||||
}
|
||||
ctx := graphql.WithFieldContext(ctx, fc)
|
||||
f := func(i int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
if !isLen1 {
|
||||
defer wg.Done()
|
||||
}
|
||||
ret[i] = ec.marshalNOrganization2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐOrganization(ctx, sel, v[i])
|
||||
}
|
||||
if isLen1 {
|
||||
f(i)
|
||||
} else {
|
||||
go f(i)
|
||||
}
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, e := range ret {
|
||||
if e == graphql.Null {
|
||||
return graphql.Null
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNOrganization2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐOrganization(ctx context.Context, sel ast.SelectionSet, v *types.Organization) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
@@ -18487,11 +18109,6 @@ func (ec *executionContext) unmarshalNResetPasswordInput2goᚗproboᚗincᚋprob
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNRevealPersonalAPIKeyTokenInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐRevealPersonalAPIKeyTokenInput(ctx context.Context, v any) (types.RevealPersonalAPIKeyTokenInput, error) {
|
||||
res, err := ec.unmarshalInputRevealPersonalAPIKeyTokenInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNRevokePersonalAPIKeyInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐRevokePersonalAPIKeyInput(ctx context.Context, v any) (types.RevokePersonalAPIKeyInput, error) {
|
||||
res, err := ec.unmarshalInputRevokePersonalAPIKeyInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
@@ -18769,75 +18386,6 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S
|
||||
return res
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNTokenScope2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐTokenScope(ctx context.Context, v any) (types.TokenScope, error) {
|
||||
var res types.TokenScope
|
||||
err := res.UnmarshalGQL(v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNTokenScope2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐTokenScope(ctx context.Context, sel ast.SelectionSet, v types.TokenScope) graphql.Marshaler {
|
||||
return v
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNTokenScope2ᚕgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐTokenScopeᚄ(ctx context.Context, v any) ([]types.TokenScope, error) {
|
||||
var vSlice []any
|
||||
vSlice = graphql.CoerceList(v)
|
||||
var err error
|
||||
res := make([]types.TokenScope, len(vSlice))
|
||||
for i := range vSlice {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
|
||||
res[i], err = ec.unmarshalNTokenScope2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐTokenScope(ctx, vSlice[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNTokenScope2ᚕgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐTokenScopeᚄ(ctx context.Context, sel ast.SelectionSet, v []types.TokenScope) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
isLen1 := len(v) == 1
|
||||
if !isLen1 {
|
||||
wg.Add(len(v))
|
||||
}
|
||||
for i := range v {
|
||||
i := i
|
||||
fc := &graphql.FieldContext{
|
||||
Index: &i,
|
||||
Result: &v[i],
|
||||
}
|
||||
ctx := graphql.WithFieldContext(ctx, fc)
|
||||
f := func(i int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
if !isLen1 {
|
||||
defer wg.Done()
|
||||
}
|
||||
ret[i] = ec.marshalNTokenScope2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐTokenScope(ctx, sel, v[i])
|
||||
}
|
||||
if isLen1 {
|
||||
f(i)
|
||||
} else {
|
||||
go f(i)
|
||||
}
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, e := range ret {
|
||||
if e == graphql.Null {
|
||||
return graphql.Null
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalNUpdateMembershipInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐUpdateMembershipInput(ctx context.Context, v any) (types.UpdateMembershipInput, error) {
|
||||
res, err := ec.unmarshalInputUpdateMembershipInput(ctx, v)
|
||||
return res, graphql.ErrorOnPath(ctx, err)
|
||||
@@ -19436,13 +18984,6 @@ func (ec *executionContext) marshalOResetPasswordPayload2ᚖgoᚗproboᚗincᚋp
|
||||
return ec._ResetPasswordPayload(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalORevealPersonalAPIKeyTokenPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐRevealPersonalAPIKeyTokenPayload(ctx context.Context, sel ast.SelectionSet, v *types.RevealPersonalAPIKeyTokenPayload) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._RevealPersonalAPIKeyTokenPayload(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalORevokeAllSessionsPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐRevokeAllSessionsPayload(ctx context.Context, sel ast.SelectionSet, v *types.RevokeAllSessionsPayload) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
|
||||
@@ -78,9 +78,8 @@ type CreateOrganizationPayload struct {
|
||||
}
|
||||
|
||||
type CreatePersonalAPIKeyInput struct {
|
||||
Name string `json:"name"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
OrganizationIds []gid.GID `json:"organizationIds"`
|
||||
Name string `json:"name"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type CreatePersonalAPIKeyPayload struct {
|
||||
@@ -273,14 +272,12 @@ type PasswordRequired struct {
|
||||
func (PasswordRequired) IsAssumeOrganizationSessionResult() {}
|
||||
|
||||
type PersonalAPIKey struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LastUsedAt *time.Time `json:"lastUsedAt,omitempty"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Scopes []TokenScope `json:"scopes"`
|
||||
Organizations []*Organization `json:"organizations"`
|
||||
Permission bool `json:"permission"`
|
||||
ID gid.GID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Token *string `json:"token,omitempty"`
|
||||
Permission bool `json:"permission"`
|
||||
}
|
||||
|
||||
func (PersonalAPIKey) IsNode() {}
|
||||
@@ -312,24 +309,16 @@ type ResetPasswordPayload struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
type RevealPersonalAPIKeyTokenInput struct {
|
||||
TokenID gid.GID `json:"tokenId"`
|
||||
}
|
||||
|
||||
type RevealPersonalAPIKeyTokenPayload struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type RevokeAllSessionsPayload struct {
|
||||
RevokedCount int `json:"revokedCount"`
|
||||
}
|
||||
|
||||
type RevokePersonalAPIKeyInput struct {
|
||||
TokenID gid.GID `json:"tokenId"`
|
||||
PersonalAPIKeyID gid.GID `json:"personalAPIKeyId"`
|
||||
}
|
||||
|
||||
type RevokePersonalAPIKeyPayload struct {
|
||||
Success bool `json:"success"`
|
||||
PersonalAPIKeyID gid.GID `json:"personalAPIKeyId"`
|
||||
}
|
||||
|
||||
type RevokeSessionInput struct {
|
||||
|
||||
@@ -677,30 +677,17 @@ func (r *mutationResolver) CreatePersonalAPIKey(ctx context.Context, input types
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RevealPersonalAPIKeyToken is the resolver for the revealPersonalAPIKeyToken field.
|
||||
func (r *mutationResolver) RevealPersonalAPIKeyToken(ctx context.Context, input types.RevealPersonalAPIKeyTokenInput) (*types.RevealPersonalAPIKeyTokenPayload, error) {
|
||||
identity := IdentityFromContext(ctx)
|
||||
|
||||
token, err := r.iam.AccountService.RevealPersonalAPIKeyToken(ctx, identity.ID, input.TokenID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot reveal personal api key token", log.Error(err))
|
||||
return nil, gqlutils.InternalServerError(ctx)
|
||||
}
|
||||
|
||||
return &types.RevealPersonalAPIKeyTokenPayload{Token: token}, nil
|
||||
}
|
||||
|
||||
// RevokePersonalAPIKey is the resolver for the revokePersonalAPIKey field.
|
||||
func (r *mutationResolver) RevokePersonalAPIKey(ctx context.Context, input types.RevokePersonalAPIKeyInput) (*types.RevokePersonalAPIKeyPayload, error) {
|
||||
identity := IdentityFromContext(ctx)
|
||||
|
||||
err := r.iam.AccountService.DeletePersonalAPIKey(ctx, identity.ID, input.TokenID)
|
||||
err := r.iam.AccountService.DeletePersonalAPIKey(ctx, identity.ID, input.PersonalAPIKeyID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot delete personal api key", log.Error(err))
|
||||
return nil, gqlutils.InternalServerError(ctx)
|
||||
}
|
||||
|
||||
return &types.RevokePersonalAPIKeyPayload{Success: true}, nil
|
||||
return &types.RevokePersonalAPIKeyPayload{PersonalAPIKeyID: input.PersonalAPIKeyID}, nil
|
||||
}
|
||||
|
||||
// CreateOrganization is the resolver for the createOrganization field.
|
||||
@@ -1012,6 +999,15 @@ func (r *organizationResolver) HorizontalLogoURL(ctx context.Context, obj *types
|
||||
|
||||
// Members is the resolver for the members field.
|
||||
func (r *organizationResolver) Members(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MembershipOrderBy) (*types.MembershipConnection, error) {
|
||||
if err := r.iam.Authorizer.Authorize(ctx, iam.AuthorizeParams{
|
||||
Principal: IdentityFromContext(ctx).ID,
|
||||
Resource: obj.ID,
|
||||
Action: iam.ActionIAMOrganizationListMembers,
|
||||
ResourceAttributes: map[string]string{},
|
||||
}); err != nil {
|
||||
return nil, gqlutils.Forbidden(err)
|
||||
}
|
||||
|
||||
if gqlutils.OnlyTotalCountSelected(ctx) {
|
||||
return &types.MembershipConnection{
|
||||
Resolver: r,
|
||||
@@ -1112,6 +1108,19 @@ func (r *organizationResolver) Permission(ctx context.Context, obj *types.Organi
|
||||
return r.Resolver.Permission(ctx, obj, action)
|
||||
}
|
||||
|
||||
// Token is the resolver for the token field.
|
||||
func (r *personalAPIKeyResolver) Token(ctx context.Context, obj *types.PersonalAPIKey) (*string, error) {
|
||||
identity := IdentityFromContext(ctx)
|
||||
|
||||
token, err := r.iam.AccountService.RevealPersonalAPIKeyToken(ctx, identity.ID, obj.ID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot reveal personal api key token", log.Error(err))
|
||||
return nil, gqlutils.InternalServerError(ctx)
|
||||
}
|
||||
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
// Permission is the resolver for the permission field.
|
||||
func (r *personalAPIKeyResolver) Permission(ctx context.Context, obj *types.PersonalAPIKey, action string) (bool, error) {
|
||||
return r.Resolver.Permission(ctx, obj, action)
|
||||
@@ -1202,6 +1211,15 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
|
||||
|
||||
return types.NewSAMLConfiguration(samlConfiguration), nil
|
||||
}
|
||||
case coredata.PersonalAPIKeyEntityType:
|
||||
action = iam.ActionIAMPersonalAPIKeyGet
|
||||
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
|
||||
personalAPIKey, err := r.iam.GetPersonalAPIKey(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return types.NewPersonalAPIKey(personalAPIKey), nil
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported entity type: %d", id.EntityType())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user