Skip to content

Commit

Permalink
Fixes after CR
Browse files Browse the repository at this point in the history
  • Loading branch information
samuel committed May 13, 2024
1 parent 5ae3334 commit 00d36bd
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 43 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ npx nx g @nx/nest:application --tags type:application
For more information about code generation, please refer to the [NX
documentation](https://nx.dev/nx-api/nx).


# NPM Auth Variables

The `.npmrc` file is needed for private registry credentials.
This file is NOT in git, but it's necessary for the build

Expand All @@ -99,10 +99,13 @@ This file is NOT in git, but it's necessary for the build
# Troubleshooting

## Docker

Common issues you might encounter w/ docker

### DB URL in env variable fails when using `docker run`, but works when running outside docker

If using `docker run --env-file .env ...`, the env file cannot include quotes around values. The quotes will be included in the value.

### Localhost postgres url cannot connect

Inside docker, localhost points to the container not your computer. Change `localhost` to `host.docker.internal` to reference your computer's localhost ip.
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import {
Action,
EvaluationRequest,
FIXTURE,
GrantPermissionAction,
SignMessageAction,
SignRawAction,
SignTransactionAction,
SignTypedDataAction
} from '@narval/policy-engine-shared'
import { InputType, decode } from '@narval/transaction-request-intent'
import {
generateGrantPermissionRequest,
generateSignMessageRequest,
generateSignRawRequest,
generateSignTransactionRequest,
Expand Down Expand Up @@ -201,6 +203,46 @@ describe('toInput', () => {
expect(input.approvals).toEqual(approvals)
})
})

describe(`when action is ${Action.GRANT_PERMISSION}`, () => {
let evaluation: EvaluationRequest

beforeEach(async () => {
evaluation = await generateGrantPermissionRequest()
})

it('maps action', () => {
const input = toInput({ evaluation, principal, approvals })

expect(input.action).toEqual(Action.GRANT_PERMISSION)
})

it('maps principal', () => {
const input = toInput({ evaluation, principal, approvals })

expect(input.principal).toEqual(principal)
})

it('maps resource', () => {
const input = toInput({ evaluation, principal, approvals })
const request = evaluation.request as GrantPermissionAction

expect(input.resource).toEqual({ uid: request.resourceId })
})

it('maps approvals', () => {
const input = toInput({ evaluation, principal, approvals })

expect(input.approvals).toEqual(approvals)
})

it('maps permissions', async () => {
const input = toInput({ evaluation, principal, approvals })
const request = evaluation.request as GrantPermissionAction

expect(input.permissions).toEqual(request.permissions)
})
})
})

describe('toData', () => {
Expand Down
27 changes: 26 additions & 1 deletion apps/policy-engine/src/shared/testing/evaluation.testing.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { Action, EvaluationRequest, FIXTURE, Request, TransactionRequest } from '@narval/policy-engine-shared'
import {
Action,
EvaluationRequest,
FIXTURE,
Permission,
Request,
Resource,
TransactionRequest
} from '@narval/policy-engine-shared'
import { Alg, Payload, hash, privateKeyToJwk, signJwt } from '@narval/signature'
import { randomBytes } from 'crypto'
import { UNSAFE_PRIVATE_KEY } from 'packages/policy-engine-shared/src/lib/dev.fixture'
Expand Down Expand Up @@ -155,3 +163,20 @@ export const generateSignTypedDataRequest = async (): Promise<EvaluationRequest>
approvals: [bobSignature, carolSignature]
}
}

export const generateGrantPermissionRequest = async (): Promise<EvaluationRequest> => {
const request: Request = {
action: Action.GRANT_PERMISSION,
nonce: uuid(),
resourceId: Resource.VAULT,
permissions: [Permission.WALLET_CREATE, Permission.WALLET_READ, Permission.WALLET_IMPORT]
}

const { aliceSignature, bobSignature, carolSignature } = await sign(request)

return {
authentication: aliceSignature,
request,
approvals: [bobSignature, carolSignature]
}
}
1 change: 0 additions & 1 deletion apps/vault/src/main.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { RawAesWrappingSuiteIdentifier } from '@aws-crypto/client-node'

export const REQUEST_HEADER_API_KEY = 'x-api-key'
export const REQUEST_HEADER_CLIENT_ID = 'x-client-id'
export const REQUEST_ADMIN_TOKEN = 'x-admin-token'

export const ENCRYPTION_KEY_NAMESPACE = 'armory.vault'
export const ENCRYPTION_KEY_NAME = 'storage-encryption'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Permission } from '@narval/policy-engine-shared'
import { Body, Controller, HttpStatus, Post, UseGuards } from '@nestjs/common'
import { ClientId } from '../../../../shared/decorator/client-id.decorator'
import { Permissions } from '../../../../shared/decorator/permissions.decorator'
Expand All @@ -9,7 +10,7 @@ import { ImportPrivateKeyDto } from '../dto/import-private-key-dto'
import { ImportPrivateKeyResponseDto } from '../dto/import-private-key-response-dto'

@Controller('/import')
@Permissions(['wallet:import'])
@Permissions([Permission.WALLET_IMPORT])
@UseGuards(AuthorizationGuard)
export class ImportController {
constructor(private importService: ImportService) {}
Expand Down
25 changes: 4 additions & 21 deletions packages/signature/src/lib/__test__/unit/verify.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ describe('checkDataHash', () => {
})
})

describe('checkPermissions', () => {
describe('checkAccess', () => {
it('returns true when the access is valid', () => {
const payload: Payload = {
access: [
Expand All @@ -819,33 +819,16 @@ describe('checkPermissions', () => {
]
})
).toEqual(true)
})

it('throws JwtError when the access is invalid', () => {
const payload: Payload = {
access: [
{
resource: 'vault',
permissions: ['wallet:create', 'wallet:read']
}
]
}

expect(() =>
checkAccess(payload, {
access: []
})
).toThrow(JwtError)

expect(() =>
expect(
checkAccess(payload, {
access: [
{
resource: 'vault',
permissions: ['wallet:create', 'foo:bar']
permissions: ['wallet:read']
}
]
})
).toThrow(JwtError)
).toEqual(true)
})
})
15 changes: 7 additions & 8 deletions packages/signature/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ export const JwsdHeader = z.object({
})
export type JwsdHeader = z.infer<typeof JwsdHeader>

export const PayloadAccessSchema = z.object({
resource: z.string(),
permissions: z.array(z.string())
})
export type PayloadAccessSchema = z.infer<typeof PayloadAccessSchema>

/**
* Defines the payload of JWT.
*
Expand Down Expand Up @@ -226,14 +232,7 @@ export const Payload = z.intersection(
cnf: publicKeySchema.optional(),
requestHash: z.string().optional(),
data: z.string().optional(),
access: z
.array(
z.object({
resource: z.string(),
permissions: z.array(z.string())
})
)
.optional()
access: z.array(PayloadAccessSchema).optional()
})
)
export type Payload = z.infer<typeof Payload>
Expand Down
20 changes: 10 additions & 10 deletions packages/signature/src/lib/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,16 @@ export const checkDataHash = (payload: Payload, opts: JwtVerifyOptions): boolean

export const checkAccess = (payload: Payload, opts: JwtVerifyOptions): boolean => {
if (opts.access) {
if (
!opts.access.length ||
!payload.access ||
!payload.access.length ||
!opts.access.every((oa) => {
const payloadAccess = payload.access?.find((pa) => pa.resource === oa.resource)
return payloadAccess && oa.permissions.every((p) => payloadAccess.permissions.includes(p))
})
) {
throw new JwtError({ message: 'Invalid permissions', context: { payload } })
for (const access of opts.access) {
const payloadAccess = payload.access?.find(
({ resource }) => resource.toLowerCase() === access.resource.toLowerCase()
)
const missingPermissions = access.permissions.filter(
(permission) => !payloadAccess?.permissions.includes(permission)
)
if (missingPermissions.length) {
throw new JwtError({ message: 'Invalid permissions', context: { payload, missingPermissions } })
}
}
}
return true
Expand Down

0 comments on commit 00d36bd

Please sign in to comment.