Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions libraries/grpc-sdk/src/modules/authorization/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,8 @@ export class Authorization extends ConduitModule<typeof AuthorizationDefinition>
createResourceAccessList(data: ResourceAccessListRequest): Promise<Empty> {
return this.client!.createResourceAccessList(data);
}

getAuthorizedQuery(data: ResourceAccessListRequest): Promise<any> {
return this.client!.getAuthorizedQuery(data).then(r => JSON.parse(r.query));
}
}
15 changes: 15 additions & 0 deletions modules/authorization/src/Authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
Decision,
DeleteResourceRequest,
FindRelationRequest,
GetAuthorizedQueryResponse,
PermissionCheck,
PermissionRequest,
Relation,
Expand Down Expand Up @@ -55,6 +56,7 @@ export default class Authorization extends ManagedModule<Config> {
getAllowedResources: this.getAllowedResources.bind(this),
can: this.can.bind(this),
createResourceAccessList: this.createResourceAccessList.bind(this),
getAuthorizedQuery: this.getAuthorizedQuery.bind(this),
},
};
protected metricsSchema = metricsSchema;
Expand Down Expand Up @@ -205,6 +207,19 @@ export default class Authorization extends ManagedModule<Config> {
callback(null);
}

async getAuthorizedQuery(
call: GrpcRequest<ResourceAccessListRequest>,
callback: GrpcResponse<GetAuthorizedQueryResponse>,
) {
const { subject, action, resourceType } = call.request;
const query = await this.permissionsController.getAuthorizedQuery(
subject,
action,
resourceType,
);
callback(null, { query: JSON.stringify(query) });
}

async can(call: GrpcRequest<PermissionCheck>, callback: GrpcResponse<Decision>) {
const { subject, resource, actions } = call.request;
let allow = false;
Expand Down
5 changes: 5 additions & 0 deletions modules/authorization/src/authorization.proto
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ message AllowedResourcesResponse {
int32 count = 2;
}

message GetAuthorizedQueryResponse {
string query = 1;
}

message ResourceAccessListRequest {
string subject = 1;
string action = 2;
Expand Down Expand Up @@ -101,4 +105,5 @@ service Authorization {
rpc Can(PermissionCheck) returns (Decision);
rpc GetAllowedResources(AllowedResourcesRequest) returns (AllowedResourcesResponse);
rpc CreateResourceAccessList(ResourceAccessListRequest) returns (google.protobuf.Empty);
rpc GetAuthorizedQuery(ResourceAccessListRequest) returns (GetAuthorizedQueryResponse);
}
145 changes: 29 additions & 116 deletions modules/authorization/src/controllers/permissions.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import ConduitGrpcSdk from '@conduitplatform/grpc-sdk';
import {
AccessListQueryParams,
checkRelation,
computePermissionTuple,
getMongoAccessListQuery,
getPostgresAccessListQuery,
getSQLAccessListQuery,
} from '../utils';
Expand Down Expand Up @@ -65,21 +67,21 @@ export class PermissionsController {
}
// if the actor is the object itself, all permissions are provided
if (subject === object) {
await RuleCache.storeResolution(this.grpcSdk, computedTuple, true);
RuleCache.storeResolution(this.grpcSdk, computedTuple, true);
return true;
}

const permission = await Permission.getInstance().findOne({
computedTuple,
});
if (permission) {
await RuleCache.storeResolution(this.grpcSdk, computedTuple, true);
RuleCache.storeResolution(this.grpcSdk, computedTuple, true);
return true;
}

const index = await this.indexController.findIndex(subject, action, object);

await RuleCache.storeResolution(this.grpcSdk, computedTuple, index ?? false);
RuleCache.storeResolution(this.grpcSdk, computedTuple, index ?? false);

return index ?? false;
}
Expand Down Expand Up @@ -128,124 +130,35 @@ export class PermissionsController {
}

async createAccessList(subject: string, action: string, objectType: string) {
const computedTuple = `${subject}#${action}@${objectType}`;
const objectTypeCollection = await this.grpcSdk
.database!.getSchema(objectType)
.then(r => r.collectionName);
const dbType = await this.grpcSdk.database!.getDatabaseType().then(r => r.result);
const query = await this.getAuthorizedQuery(subject, action, objectType);
await this.grpcSdk.database?.createView(
objectType,
createHash('sha256').update(`${objectType}_${subject}_${action}`).digest('hex'),
['Permission', 'ActorIndex', 'ObjectIndex'],
{
mongoQuery: [
// permissions lookup won't work this way
{
$lookup: {
from: 'cnd_permissions',
let: { x_id: { $toString: '$_id' } },
pipeline: [
{
$match: {
$expr: {
$eq: [
'$computedTuple',
{ $concat: [`${subject}#${action}@${objectType}:`, '$$x_id'] },
],
},
},
},
],
as: 'permissions',
},
},
{
$lookup: {
from: 'cnd_actorindexes',
let: {
subject: subject,
},
pipeline: [
{
$match: {
$expr: {
$eq: ['$subject', '$$subject'],
},
},
},
],
as: 'actors',
},
},
{
$lookup: {
from: 'cnd_objectindexes',
let: {
id_action: {
$concat: [`${objectType}:`, { $toString: '$_id' }, `#${action}`],
},
entities: '$actors.entity',
},
pipeline: [
{
$match: {
$and: [
{
$expr: {
$eq: ['$subject', '$$id_action'],
},
},
{
$expr: {
$in: ['$entity', '$$entities'],
},
},
],
},
},
],
as: 'intersection',
},
},
{
$match: {
$or: [
{
'intersection.0': { $exists: true },
},
{
'permissions.0': { $exists: true },
},
],
},
},
{
$project: {
actors: 0,
objects: 0,
permissions: 0,
intersection: 0,
},
},
],
sqlQuery:
dbType === 'PostgreSQL'
? getPostgresAccessListQuery(
objectTypeCollection,
computedTuple,
subject,
objectType,
action,
)
: getSQLAccessListQuery(
objectTypeCollection,
computedTuple,
subject,
objectType,
action,
),
},
query,
);
return;
}

async getAuthorizedQuery(subject: string, action: string, objectType: string) {
const computedTuple = `${subject}#${action}@${objectType}`;
const objectTypeCollection = await this.grpcSdk
.database!.getSchema(objectType)
.then(r => r.collectionName);
const dbType = await this.grpcSdk.database!.getDatabaseType().then(r => r.result);
const params: AccessListQueryParams = {
objectTypeCollection,
computedTuple,
subject,
objectType,
action,
};
return {
mongoQuery: getMongoAccessListQuery(params),
sqlQuery:
dbType === 'PostgreSQL'
? getPostgresAccessListQuery(params)
: getSQLAccessListQuery(params),
};
}
}
Loading