Skip to content

Commit

Permalink
WIP - #305 - updating clone/pull permission checking
Browse files Browse the repository at this point in the history
  • Loading branch information
tegefaulkes committed Feb 24, 2022
1 parent c992caa commit c5d70d2
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 90 deletions.
2 changes: 1 addition & 1 deletion src/acl/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type PermissionIdString = Opaque<'PermissionIdString', string>;

type Permission = {
gestalt: GestaltActions;
vaults: Record<VaultId | string, VaultActions>; // FIXME: the string union on VaultId is to prevent some false errors.
vaults: Record<VaultId, VaultActions>;
};

type GestaltActions = Partial<Record<GestaltAction, null>>;
Expand Down
52 changes: 30 additions & 22 deletions src/agent/service/vaultsGitInfoGet.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import type { VaultName } from '../../vaults/types';
import type { VaultManager } from '../../vaults';
import type { ACL } from '../../acl';
import type { ConnectionInfoGetter } from '../../agent/types';
import * as grpc from '@grpc/grpc-js';
import { utils as idUtils } from '@matrixai/id';
import { utils as grpcUtils } from '../../grpc';
import { utils as vaultsUtils, errors as vaultsErrors } from '../../vaults';
import * as vaultsPB from '../../proto/js/polykey/v1/vaults/vaults_pb';
import * as validationUtils from '../../validation/utils';
import * as nodesUtils from '../../nodes/utils';
import { never } from '../../utils/utils';

function vaultsGitInfoGet({
vaultManager,
acl,
connectionInfoGetter,
}: {
vaultManager: VaultManager;
acl: ACL;
connectionInfoGetter: ConnectionInfoGetter;
}) {
return async (
call: grpc.ServerWritableStream<vaultsPB.InfoRequest, vaultsPB.PackChunk>,
Expand All @@ -25,11 +29,6 @@ function vaultsGitInfoGet({
await genWritable.throw({ code: grpc.status.NOT_FOUND });
return;
}
const nodeMessage = request.getNode();
if (nodeMessage == null) {
await genWritable.throw({ code: grpc.status.NOT_FOUND });
return;
}
let vaultName;
const vaultNameOrId = vaultMessage.getNameOrId();
let vaultId = await vaultManager.getVaultId(vaultNameOrId as VaultName);
Expand All @@ -43,24 +42,33 @@ function vaultsGitInfoGet({
return;
}
}
const nodeId = validationUtils.parseNodeId(nodeMessage.getNodeId());
const actionType = request.getAction();
const perms = await acl.getNodePerm(nodeId);
if (!perms) {
await genWritable.throw(new vaultsErrors.ErrorVaultsPermissionDenied());
// Getting the NodeId from the ReverseProxy connection info
const connectionInfo = connectionInfoGetter(call.getPeer());
// If this is getting run the connection exists
// It SHOULD exist here
if (connectionInfo == null) never();
const nodeId = connectionInfo.nodeId;
const nodeIdEncoded = nodesUtils.encodeNodeId(nodeId);
const actionType = validationUtils.parseVaultAction(request.getAction());
const permissions = await acl.getNodePerm(nodeId);
if (permissions == null) {
await genWritable.throw(
new vaultsErrors.ErrorVaultsPermissionDenied(
`No permissions found for ${nodeIdEncoded}`,
),
);
return;
}
const vaultPerms = perms.vaults[idUtils.toString(vaultId)];
try {
if (vaultPerms[actionType] !== null) {
await genWritable.throw(new vaultsErrors.ErrorVaultsPermissionDenied());
return;
}
} catch (err) {
if (err instanceof TypeError) {
await genWritable.throw(new vaultsErrors.ErrorVaultsPermissionDenied());
return;
}
const vaultPerms = permissions.vaults[vaultId];
if (vaultPerms[actionType] !== null) {
await genWritable.throw(
new vaultsErrors.ErrorVaultsPermissionDenied(
`${nodeIdEncoded} does not have permission to ${actionType} from vault ${vaultsUtils.encodeVaultId(
vaultId,
)}`,
),
);
return;
}
const meta = new grpc.Metadata();
meta.set('vaultName', vaultName);
Expand Down
46 changes: 44 additions & 2 deletions src/agent/service/vaultsGitPackGet.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import type * as grpc from '@grpc/grpc-js';
import type { VaultName } from '../../vaults/types';
import type { VaultManager } from '../../vaults';
import type { ConnectionInfoGetter } from '../../agent/types';
import type ACL from '../../acl/ACL';
import { never } from '../../utils/utils';
import * as nodesUtils from '../../nodes/utils';
import { errors as grpcErrors, utils as grpcUtils } from '../../grpc';
import { utils as vaultsUtils, errors as vaultsErrors } from '../../vaults';
import * as vaultsPB from '../../proto/js/polykey/v1/vaults/vaults_pb';
import * as validationUtils from '../../validation/utils';

function vaultsGitPackGet({ vaultManager }: { vaultManager: VaultManager }) {
function vaultsGitPackGet({
vaultManager,
acl,
connectionInfoGetter,
}: {
vaultManager: VaultManager;
acl: ACL;
connectionInfoGetter: ConnectionInfoGetter;
}) {
return async (
call: grpc.ServerDuplexStream<vaultsPB.PackChunk, vaultsPB.PackChunk>,
) => {
Expand All @@ -15,14 +28,43 @@ function vaultsGitPackGet({ vaultManager }: { vaultManager: VaultManager }) {
clientBodyBuffers.push(clientRequest!.getChunk_asU8());
const body = Buffer.concat(clientBodyBuffers);
const meta = call.metadata;
// Getting the NodeId from the ReverseProxy connection info
const connectionInfo = connectionInfoGetter(call.getPeer());
// If this is getting run the connection exists
// It SHOULD exist here
if (connectionInfo == null) never();
const nodeId = connectionInfo.nodeId;
const nodeIdEncoded = nodesUtils.encodeNodeId(nodeId);
// Getting vaultId
const vaultNameOrId = meta.get('vaultNameOrId').pop()!.toString();
if (vaultNameOrId == null) {
throw new grpcErrors.ErrorGRPC('vault-name not in metadata');
}
let vaultId = await vaultManager.getVaultId(vaultNameOrId as VaultName);
vaultId = vaultId ?? vaultsUtils.decodeVaultId(vaultNameOrId);
if (vaultId == null) {
await genDuplex.throw(new vaultsErrors.ErrorVaultsVaultUndefined());
await genDuplex.throw(
// Throwing permission error to hide information about vaults existence
new vaultsErrors.ErrorVaultsPermissionDenied(
`No permissions found for ${nodeIdEncoded}`,
),
);
return;
}
// Checking permissions
const permissions = await acl.getNodePerm(nodeId);
const vaultPerms = permissions?.vaults[vaultId];
const actionType = validationUtils.parseVaultAction(
meta.get('vaultAction').pop(),
);
if (vaultPerms?.[actionType] !== null) {
await genDuplex.throw(
new vaultsErrors.ErrorVaultsPermissionDenied(
`${nodeIdEncoded} does not have permission to ${actionType} from vault ${vaultsUtils.encodeVaultId(
vaultId,
)}`,
),
);
return;
}
const response = new vaultsPB.PackChunk();
Expand Down
6 changes: 0 additions & 6 deletions src/proto/js/polykey/v1/vaults/vaults_pb.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,6 @@ export class InfoRequest extends jspb.Message {
clearVault(): void;
getVault(): Vault | undefined;
setVault(value?: Vault): InfoRequest;

hasNode(): boolean;
clearNode(): void;
getNode(): polykey_v1_nodes_nodes_pb.Node | undefined;
setNode(value?: polykey_v1_nodes_nodes_pb.Node): InfoRequest;
getAction(): string;
setAction(value: string): InfoRequest;

Expand All @@ -439,7 +434,6 @@ export class InfoRequest extends jspb.Message {
export namespace InfoRequest {
export type AsObject = {
vault?: Vault.AsObject,
node?: polykey_v1_nodes_nodes_pb.Node.AsObject,
action: string,
}
}
Expand Down
51 changes: 0 additions & 51 deletions src/proto/js/polykey/v1/vaults/vaults_pb.js
Original file line number Diff line number Diff line change
Expand Up @@ -3343,7 +3343,6 @@ proto.polykey.v1.vaults.InfoRequest.prototype.toObject = function(opt_includeIns
proto.polykey.v1.vaults.InfoRequest.toObject = function(includeInstance, msg) {
var f, obj = {
vault: (f = msg.getVault()) && proto.polykey.v1.vaults.Vault.toObject(includeInstance, f),
node: (f = msg.getNode()) && polykey_v1_nodes_nodes_pb.Node.toObject(includeInstance, f),
action: jspb.Message.getFieldWithDefault(msg, 3, "")
};

Expand Down Expand Up @@ -3386,11 +3385,6 @@ proto.polykey.v1.vaults.InfoRequest.deserializeBinaryFromReader = function(msg,
reader.readMessage(value,proto.polykey.v1.vaults.Vault.deserializeBinaryFromReader);
msg.setVault(value);
break;
case 2:
var value = new polykey_v1_nodes_nodes_pb.Node;
reader.readMessage(value,polykey_v1_nodes_nodes_pb.Node.deserializeBinaryFromReader);
msg.setNode(value);
break;
case 3:
var value = /** @type {string} */ (reader.readString());
msg.setAction(value);
Expand Down Expand Up @@ -3432,14 +3426,6 @@ proto.polykey.v1.vaults.InfoRequest.serializeBinaryToWriter = function(message,
proto.polykey.v1.vaults.Vault.serializeBinaryToWriter
);
}
f = message.getNode();
if (f != null) {
writer.writeMessage(
2,
f,
polykey_v1_nodes_nodes_pb.Node.serializeBinaryToWriter
);
}
f = message.getAction();
if (f.length > 0) {
writer.writeString(
Expand Down Expand Up @@ -3487,43 +3473,6 @@ proto.polykey.v1.vaults.InfoRequest.prototype.hasVault = function() {
};


/**
* optional polykey.v1.nodes.Node node = 2;
* @return {?proto.polykey.v1.nodes.Node}
*/
proto.polykey.v1.vaults.InfoRequest.prototype.getNode = function() {
return /** @type{?proto.polykey.v1.nodes.Node} */ (
jspb.Message.getWrapperField(this, polykey_v1_nodes_nodes_pb.Node, 2));
};


/**
* @param {?proto.polykey.v1.nodes.Node|undefined} value
* @return {!proto.polykey.v1.vaults.InfoRequest} returns this
*/
proto.polykey.v1.vaults.InfoRequest.prototype.setNode = function(value) {
return jspb.Message.setWrapperField(this, 2, value);
};


/**
* Clears the message field making it undefined.
* @return {!proto.polykey.v1.vaults.InfoRequest} returns this
*/
proto.polykey.v1.vaults.InfoRequest.prototype.clearNode = function() {
return this.setNode(undefined);
};


/**
* Returns whether this field is set.
* @return {boolean}
*/
proto.polykey.v1.vaults.InfoRequest.prototype.hasNode = function() {
return jspb.Message.getField(this, 2) != null;
};


/**
* optional string action = 3;
* @return {string}
Expand Down
1 change: 0 additions & 1 deletion src/proto/schemas/polykey/v1/vaults/vaults.proto
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ message LogEntry {
// Agent specific.
message InfoRequest {
Vault vault = 1;
polykey.v1.nodes.Node node = 2;
string action = 3;
}

Expand Down
15 changes: 8 additions & 7 deletions src/vaults/VaultInternal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
CommitLog,
FileSystemReadable,
FileSystemWritable,
VaultAction,
VaultId,
VaultIdEncoded,
VaultName,
Expand All @@ -31,8 +32,8 @@ import * as nodesUtils from '../nodes/utils';
import * as validationUtils from '../validation/utils';
import { withF, withG } from '../utils/context';
import { RWLock } from '../utils/locks';
import * as nodesPB from '../proto/js/polykey/v1/nodes/nodes_pb';
import * as vaultsPB from '../proto/js/polykey/v1/vaults/vaults_pb';
import { never } from '../utils/utils';

// TODO: this might be temp?
export type RemoteInfo = {
Expand Down Expand Up @@ -131,6 +132,7 @@ class VaultInternal {
const [request, vaultName, remoteVaultId] = await vault.request(
client,
targetVaultNameOrId,
'clone',
);
await git.clone({
fs: efs,
Expand Down Expand Up @@ -656,6 +658,7 @@ class VaultInternal {
const [request, , remoteVaultId] = await this.request(
client,
pullVaultNameOrId!,
'pull',
);
await git.pull({
fs: this.efs,
Expand Down Expand Up @@ -802,12 +805,11 @@ class VaultInternal {
protected async request(
client: GRPCClientAgent,
vaultNameOrId: VaultId | VaultName,
vaultAction: VaultAction,
): Promise<any[]> {
const requestMessage = new vaultsPB.InfoRequest();
const vaultMessage = new vaultsPB.Vault();
const nodeMessage = new nodesPB.Node();
nodeMessage.setNodeId(nodesUtils.encodeNodeId(this.keyManager.getNodeId()));
requestMessage.setAction('clone');
requestMessage.setAction(vaultAction);
if (typeof vaultNameOrId === 'string') {
vaultMessage.setNameOrId(vaultNameOrId);
} else {
Expand All @@ -816,7 +818,6 @@ class VaultInternal {
vaultMessage.setNameOrId(vaultsUtils.encodeVaultId(vaultNameOrId));
}
requestMessage.setVault(vaultMessage);
requestMessage.setNode(nodeMessage);
const response = client.vaultsGitInfoGet(requestMessage);
let vaultName, remoteVaultId;
response.stream.on('metadata', async (meta) => {
Expand All @@ -832,6 +833,7 @@ class VaultInternal {
infoResponse.push(resp.getChunk_asU8());
}
const metadata = new grpc.Metadata();
metadata.set('vaultAction', vaultAction);
if (typeof vaultNameOrId === 'string') {
metadata.set('vaultNameOrId', vaultNameOrId);
} else {
Expand Down Expand Up @@ -884,8 +886,7 @@ class VaultInternal {
statusMessage: 'OK',
};
} else {
// FIXME: proper error
throw new Error('Method not supported');
never();
}
},
vaultName,
Expand Down

0 comments on commit c5d70d2

Please sign in to comment.