Skip to content

Commit 7852d42

Browse files
authored
feat: add an api to validate the token's permissions (#2271)
1 parent 6e9dcdf commit 7852d42

File tree

8 files changed

+2226
-1848
lines changed

8 files changed

+2226
-1848
lines changed

connect-go/gen/proto/wg/cosmo/platform/v1/platform.pb.go

Lines changed: 2014 additions & 1846 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

connect-go/gen/proto/wg/cosmo/platform/v1/platformv1connect/platform.connect.go

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

connect/src/wg/cosmo/platform/v1/platform-PlatformService_connectquery.ts

Lines changed: 17 additions & 1 deletion
Large diffs are not rendered by default.

connect/src/wg/cosmo/platform/v1/platform_connect.ts

Lines changed: 12 additions & 1 deletion
Large diffs are not rendered by default.

connect/src/wg/cosmo/platform/v1/platform_pb.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23139,3 +23139,89 @@ export class UnlinkSubgraphResponse extends Message<UnlinkSubgraphResponse> {
2313923139
}
2314023140
}
2314123141

23142+
/**
23143+
* @generated from message wg.cosmo.platform.v1.VerifyAPIKeyGraphAccessRequest
23144+
*/
23145+
export class VerifyAPIKeyGraphAccessRequest extends Message<VerifyAPIKeyGraphAccessRequest> {
23146+
/**
23147+
* @generated from field: string federatedGraphId = 1;
23148+
*/
23149+
federatedGraphId = "";
23150+
23151+
constructor(data?: PartialMessage<VerifyAPIKeyGraphAccessRequest>) {
23152+
super();
23153+
proto3.util.initPartial(data, this);
23154+
}
23155+
23156+
static readonly runtime: typeof proto3 = proto3;
23157+
static readonly typeName = "wg.cosmo.platform.v1.VerifyAPIKeyGraphAccessRequest";
23158+
static readonly fields: FieldList = proto3.util.newFieldList(() => [
23159+
{ no: 1, name: "federatedGraphId", kind: "scalar", T: 9 /* ScalarType.STRING */ },
23160+
]);
23161+
23162+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): VerifyAPIKeyGraphAccessRequest {
23163+
return new VerifyAPIKeyGraphAccessRequest().fromBinary(bytes, options);
23164+
}
23165+
23166+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): VerifyAPIKeyGraphAccessRequest {
23167+
return new VerifyAPIKeyGraphAccessRequest().fromJson(jsonValue, options);
23168+
}
23169+
23170+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): VerifyAPIKeyGraphAccessRequest {
23171+
return new VerifyAPIKeyGraphAccessRequest().fromJsonString(jsonString, options);
23172+
}
23173+
23174+
static equals(a: VerifyAPIKeyGraphAccessRequest | PlainMessage<VerifyAPIKeyGraphAccessRequest> | undefined, b: VerifyAPIKeyGraphAccessRequest | PlainMessage<VerifyAPIKeyGraphAccessRequest> | undefined): boolean {
23175+
return proto3.util.equals(VerifyAPIKeyGraphAccessRequest, a, b);
23176+
}
23177+
}
23178+
23179+
/**
23180+
* @generated from message wg.cosmo.platform.v1.VerifyAPIKeyGraphAccessResponse
23181+
*/
23182+
export class VerifyAPIKeyGraphAccessResponse extends Message<VerifyAPIKeyGraphAccessResponse> {
23183+
/**
23184+
* @generated from field: wg.cosmo.platform.v1.Response response = 1;
23185+
*/
23186+
response?: Response;
23187+
23188+
/**
23189+
* @generated from field: bool hasOrganizationAdminOrDeveloperPermissions = 2;
23190+
*/
23191+
hasOrganizationAdminOrDeveloperPermissions = false;
23192+
23193+
/**
23194+
* @generated from field: bool hasWriteAccessToGraph = 3;
23195+
*/
23196+
hasWriteAccessToGraph = false;
23197+
23198+
constructor(data?: PartialMessage<VerifyAPIKeyGraphAccessResponse>) {
23199+
super();
23200+
proto3.util.initPartial(data, this);
23201+
}
23202+
23203+
static readonly runtime: typeof proto3 = proto3;
23204+
static readonly typeName = "wg.cosmo.platform.v1.VerifyAPIKeyGraphAccessResponse";
23205+
static readonly fields: FieldList = proto3.util.newFieldList(() => [
23206+
{ no: 1, name: "response", kind: "message", T: Response },
23207+
{ no: 2, name: "hasOrganizationAdminOrDeveloperPermissions", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
23208+
{ no: 3, name: "hasWriteAccessToGraph", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
23209+
]);
23210+
23211+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): VerifyAPIKeyGraphAccessResponse {
23212+
return new VerifyAPIKeyGraphAccessResponse().fromBinary(bytes, options);
23213+
}
23214+
23215+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): VerifyAPIKeyGraphAccessResponse {
23216+
return new VerifyAPIKeyGraphAccessResponse().fromJson(jsonValue, options);
23217+
}
23218+
23219+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): VerifyAPIKeyGraphAccessResponse {
23220+
return new VerifyAPIKeyGraphAccessResponse().fromJsonString(jsonString, options);
23221+
}
23222+
23223+
static equals(a: VerifyAPIKeyGraphAccessResponse | PlainMessage<VerifyAPIKeyGraphAccessResponse> | undefined, b: VerifyAPIKeyGraphAccessResponse | PlainMessage<VerifyAPIKeyGraphAccessResponse> | undefined): boolean {
23224+
return proto3.util.equals(VerifyAPIKeyGraphAccessResponse, a, b);
23225+
}
23226+
}
23227+

controlplane/src/core/bufservices/PlatformService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ import { validateAndFetchPluginData } from './plugin/validateAndFetchPluginData.
169169
import { linkSubgraph } from './subgraph/linkSubgraph.js';
170170
import { unlinkSubgraph } from './subgraph/unlinkSubgraph.js';
171171
import { getWorkspace } from './workspace/getWorkspace.js';
172+
import { verifyAPIKeyGraphAccess } from './api-key/verifyAPIKeyGraphAccess.js';
172173

173174
export default function (opts: RouterOptions): Partial<ServiceImpl<typeof PlatformService>> {
174175
return {
@@ -854,5 +855,9 @@ export default function (opts: RouterOptions): Partial<ServiceImpl<typeof Platfo
854855
unlinkSubgraph: (req, ctx) => {
855856
return unlinkSubgraph(opts, req, ctx);
856857
},
858+
859+
verifyAPIKeyGraphAccess: (req, ctx) => {
860+
return verifyAPIKeyGraphAccess(opts, req, ctx);
861+
},
857862
};
858863
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { PlainMessage } from '@bufbuild/protobuf';
2+
import { HandlerContext } from '@connectrpc/connect';
3+
import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common/common_pb';
4+
import {
5+
VerifyAPIKeyGraphAccessRequest,
6+
VerifyAPIKeyGraphAccessResponse,
7+
} from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb';
8+
import { UnauthorizedError } from '../../errors/errors.js';
9+
import type { RouterOptions } from '../../routes.js';
10+
import { enrichLogger, getLogger, handleError } from '../../util.js';
11+
import { FederatedGraphRepository } from '../../repositories/FederatedGraphRepository.js';
12+
13+
export function verifyAPIKeyGraphAccess(
14+
opts: RouterOptions,
15+
req: VerifyAPIKeyGraphAccessRequest,
16+
ctx: HandlerContext,
17+
): Promise<PlainMessage<VerifyAPIKeyGraphAccessResponse>> {
18+
let logger = getLogger(ctx, opts.logger);
19+
20+
return handleError<PlainMessage<VerifyAPIKeyGraphAccessResponse>>(ctx, logger, async () => {
21+
const authContext = await opts.authenticator.authenticate(ctx.requestHeader);
22+
logger = enrichLogger(ctx, logger, authContext);
23+
24+
if (authContext.organizationDeactivated) {
25+
throw new UnauthorizedError();
26+
}
27+
28+
const fedRepo = new FederatedGraphRepository(logger, opts.db, authContext.organizationId);
29+
const federatedGraph = await fedRepo.byId(req.federatedGraphId);
30+
if (!federatedGraph) {
31+
return {
32+
response: {
33+
code: EnumStatusCode.OK,
34+
},
35+
hasOrganizationAdminOrDeveloperPermissions: false,
36+
hasWriteAccessToGraph: false,
37+
};
38+
}
39+
40+
return {
41+
response: {
42+
code: EnumStatusCode.OK,
43+
},
44+
hasOrganizationAdminOrDeveloperPermissions: authContext.rbac.isOrganizationAdminOrDeveloper,
45+
hasWriteAccessToGraph: authContext.rbac.hasFederatedGraphWriteAccess(federatedGraph),
46+
};
47+
});
48+
}

proto/wg/cosmo/platform/v1/platform.proto

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2950,6 +2950,16 @@ message UnlinkSubgraphResponse {
29502950
Response response = 1;
29512951
}
29522952

2953+
message VerifyAPIKeyGraphAccessRequest {
2954+
string federatedGraphId = 1;
2955+
}
2956+
2957+
message VerifyAPIKeyGraphAccessResponse {
2958+
Response response = 1;
2959+
bool hasOrganizationAdminOrDeveloperPermissions = 2;
2960+
bool hasWriteAccessToGraph = 3;
2961+
}
2962+
29532963
service PlatformService {
29542964
// PlaygroundScripts
29552965
rpc CreatePlaygroundScript(CreatePlaygroundScriptRequest) returns (CreatePlaygroundScriptResponse) {}
@@ -3324,4 +3334,6 @@ service PlatformService {
33243334
rpc LinkSubgraph(LinkSubgraphRequest) returns (LinkSubgraphResponse) {}
33253335
// UnlinkSubgraph unlinks one subgraph from another
33263336
rpc UnlinkSubgraph(UnlinkSubgraphRequest) returns (UnlinkSubgraphResponse) {}
3337+
// VerifyAPIKeyGraphAccess checks if the token or the jwt has organization admin or developer and checks if the token has permissions to write to the graph
3338+
rpc VerifyAPIKeyGraphAccess(VerifyAPIKeyGraphAccessRequest) returns (VerifyAPIKeyGraphAccessResponse) {}
33273339
}

0 commit comments

Comments
 (0)