Skip to content

Commit

Permalink
fix: headers, env vars and secrets in expressions (#1544)
Browse files Browse the repository at this point in the history
  • Loading branch information
davenewza authored Aug 6, 2024
1 parent a7d6985 commit 99355f8
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 32 deletions.
10 changes: 10 additions & 0 deletions integration/testdata/env_vars/schema.keel
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,15 @@ model Person {
@set(person.name = ctx.env.PERSON_NAME)
@permission(expression: true)
}
get getBob(id) {
@permission(expression: ctx.env.PERSON_NAME == "Bob")
}
get getPedro(id) {
@permission(expression: ctx.env.PERSON_NAME == "Pedro")
}
list listPedros() {
@where(person.name == ctx.env.PERSON_NAME)
@permission(expression: true)
}
}
}
23 changes: 21 additions & 2 deletions integration/testdata/env_vars/tests.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import { actions, resetDatabase } from "@teamkeel/testing";
import { actions, resetDatabase, models } from "@teamkeel/testing";
import { beforeEach, expect, test } from "vitest";

beforeEach(resetDatabase);

test("create person with env var name", async () => {
test("set with env var", async () => {
const person = await actions.createPerson({});

expect(person.name).toEqual("Pedro");
});

test("permissions with env var", async () => {
const person = await actions.createPerson({});

await expect(
actions.getPedro({ id: person.id })
).not.toHaveAuthorizationError();

await expect(actions.getBob({ id: person.id })).toHaveAuthorizationError();
});

test("where with env var", async () => {
await models.person.create({ name: "Pedro" });
await models.person.create({ name: "Bob" });

const pedros = await actions.listPedros();
expect(pedros.results.length).toEqual(1);
expect(pedros.results[0].name).toEqual("Pedro");
});
22 changes: 22 additions & 0 deletions integration/testdata/headers/schema.keel
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
model Person {
fields {
name Text
}

actions {
create createPerson() {
@set(person.name = ctx.headers.PERSON_NAME)
@permission(expression: true)
}
get getBob(id) {
@permission(expression: ctx.headers.PERSON_NAME == "Bob")
}
get getPedro(id) {
@permission(expression: ctx.headers.PERSON_NAME == "Pedro")
}
list listPedros() {
@where(person.name == ctx.headers.PERSON_NAME)
@permission(expression: true)
}
}
}
90 changes: 90 additions & 0 deletions integration/testdata/headers/tests.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { actions, resetDatabase, models } from "@teamkeel/testing";
import { beforeEach, expect, test } from "vitest";

beforeEach(resetDatabase);

test("set with http header", async () => {
const response = await fetch(
process.env.KEEL_TESTING_ACTIONS_API_URL + "/createPerson",
{
body: "{}",
method: "POST",
headers: {
"Content-Type": "application/json",
PERSON_NAME: "Pedro",
},
}
);

expect(response.status).toEqual(200);
const data = await response.json();
expect(data?.name).toEqual("Pedro");
});

test("permissions with http header", async () => {
const response = await fetch(
process.env.KEEL_TESTING_ACTIONS_API_URL + "/createPerson",
{
body: "{}",
method: "POST",
headers: {
"Content-Type": "application/json",
PERSON_NAME: "Pedro",
},
}
);

expect(response.status).toEqual(200);
const person = await response.json();

const getPedro = await fetch(
process.env.KEEL_TESTING_ACTIONS_API_URL + "/getPedro",
{
body: `{ "id": "${person.id}"}`,
method: "POST",
headers: {
"Content-Type": "application/json",
PERSON_NAME: "Pedro",
},
}
);

expect(getPedro.status).toEqual(200);

const getBob = await fetch(
process.env.KEEL_TESTING_ACTIONS_API_URL + "/getBob",
{
body: `{ "id": "${person.id}"}`,
method: "POST",
headers: {
"Content-Type": "application/json",
PERSON_NAME: "Pedro",
},
}
);

expect(getBob.status).toEqual(403);
});

test("where with http header", async () => {
await models.person.create({ name: "Pedro" });
await models.person.create({ name: "Bob" });

const listPedros = await fetch(
process.env.KEEL_TESTING_ACTIONS_API_URL + "/listPedros",
{
body: "{}",
method: "POST",
headers: {
"Content-Type": "application/json",
PERSON_NAME: "Pedro",
},
}
);

expect(listPedros.status).toEqual(200);

const pedros = await listPedros.json();
expect(pedros.results.length).toEqual(1);
expect(pedros.results[0].name).toEqual("Pedro");
});
2 changes: 0 additions & 2 deletions integration/testdata/secrets/keelconfig.yaml

This file was deleted.

12 changes: 0 additions & 12 deletions integration/testdata/secrets/schema.keel

This file was deleted.

11 changes: 0 additions & 11 deletions integration/testdata/secrets/tests.test.ts

This file was deleted.

3 changes: 3 additions & 0 deletions runtime/actions/authorization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/teamkeel/keel/proto"
"github.com/teamkeel/keel/runtime/actions"
"github.com/teamkeel/keel/runtime/auth"
"github.com/teamkeel/keel/runtime/runtimectx"
"github.com/teamkeel/keel/schema/parser"
)

Expand Down Expand Up @@ -1427,6 +1428,8 @@ func TestPermissionQueryBuilder(t *testing.T) {
ctx = auth.WithIdentity(ctx, testCase.identity)
}

ctx = runtimectx.WithSecrets(ctx, map[string]string{"MY_SECRET": "1234"})

scope, _, _, err := generateQueryScope(ctx, testCase.keelSchema, testCase.actionName)
if err != nil {
require.NoError(t, err)
Expand Down
13 changes: 8 additions & 5 deletions runtime/expressions/operand.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,12 +316,15 @@ func (resolver *OperandResolver) GetOperandType() (proto.Type, bool, error) {
return proto.Type_TYPE_UNKNOWN, false, fmt.Errorf("could not find explicit input %s on action %s", inputName, action.Name)
}
return field.Type.Type, field.Type.Repeated, nil
case resolver.operand.Ident.IsContextNowField():
return proto.Type_TYPE_TIMESTAMP, false, nil
case resolver.operand.Ident.IsContextEnvField():
return proto.Type_TYPE_STRING, false, nil
case resolver.operand.Ident.IsContextSecretField():
return proto.Type_TYPE_STRING, false, nil
case resolver.operand.Ident.IsContextHeadersField():
return proto.Type_TYPE_STRING, false, nil
case operand.Ident.IsContext():
fragmentCount := len(operand.Ident.Fragments)
if fragmentCount > 2 && operand.Ident.IsContextEnvField() {
return proto.Type_TYPE_STRING, false, nil
}

fieldName := operand.Ident.Fragments[1].Fragment
return runtimectx.ContextFieldTypes[fieldName], false, nil
default:
Expand Down

0 comments on commit 99355f8

Please sign in to comment.