Skip to content

Commit b42fac2

Browse files
authored
Merge pull request #43 from G-Core/secrets
feat: ✨ Secret Storage accessor methods added
2 parents 8af8102 + a5ceb7d commit b42fac2

File tree

18 files changed

+284
-2
lines changed

18 files changed

+284
-2
lines changed

create-wit-bindings.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
3+
~/.cargo/bin/wit-bindgen c --out-dir runtime/fastedge/host-api/bindings --world bindings runtime/fastedge/host-api/wit
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { getEnv } from 'fastedge::env';
2+
import { getSecret } from 'fastedge::secret';
3+
4+
async function eventHandler(event) {
5+
const username = getEnv('USERNAME');
6+
const password = getSecret('PASSWORD');
7+
8+
return new Response(`Username: ${username}, Password: ${password}`);
9+
}
10+
11+
addEventListener('fetch', (event) => {
12+
event.respondWith(eventHandler(event));
13+
});

docs/src/content/docs/examples/main-examples.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ as we build out more functionality.
2727
title='Header manipulation with environment variables'
2828
href='/FastEdge-sdk-js/examples/headers/'
2929
/>
30+
<LinkCard
31+
title='Environment variables and secrets'
32+
href='/FastEdge-sdk-js/examples/variables-and-secrets/'
33+
/>
3034
</CardGrid>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
title: Environment Variables and Secrets
3+
description: An example using environment variables and secrets.
4+
prev:
5+
link: /FastEdge-sdk-js/examples/main-examples/
6+
label: Back to examples
7+
---
8+
9+
import { Code } from '@astrojs/starlight/components';
10+
import importedCode from '/examples/variables-and-secrets.js?raw';
11+
12+
<Code code={importedCode} lang='js' title='docs/examples/variables-and-secrets.js' />
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
title: FastEdge::secret
3+
description: How to use FastEdge secret variables.
4+
---
5+
6+
### Secret Variables
7+
8+
To access secret variables, set during deployment on the FastEdge network.
9+
10+
```js
11+
import { getSecret } from 'fastedge::secret';
12+
13+
async function eventHandler(event) {
14+
const secretToken = getSecret('MY_SECRET_TOKEN');
15+
return new Response({ secretToken });
16+
}
17+
18+
addEventListener('fetch', (event) => {
19+
event.respondWith(eventHandler(event));
20+
});
21+
```
22+
23+
```js title="SYNTAX"
24+
getSecret(secretName);
25+
```
26+
27+
##### Parameters
28+
29+
- `secretName` (required)
30+
31+
A string containing the name of the key you want to retrieve the value of.
32+
33+
##### Return Value
34+
35+
A string containing the value of the key. If the key does not exist, null is returned.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
3838
"build:monkey:prod": "./runtime/fastedge/build.sh",
3939
"build:libs": "node esbuild/fastedge-libs.js",
4040
"build:types": "tsc -p ./src/static-server/tsconfig.json",
41-
"FIX:build:wasm": "node build-examples-config.js",
42-
"FIX:generate:wit-world": "make -j8 -C fastedge-runtime/cbindings generate-wit-bindgen",
41+
"generate:wit-world": "./create-wit-bindings.sh",
4342
"lint": "npx eslint -c ./config/eslint/repo/.eslintrc.cjs .",
4443
"semantic-release": "semantic-release",
4544
"test:solo": "NODE_ENV=test jest -c ./config/jest/jest.config.js --",

runtime/fastedge/builtins/fastedge.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace fastedge::fastedge {
2525

2626
JS::PersistentRooted<JSObject *> FastEdge::env;
2727
JS::PersistentRooted<JSObject *> FastEdge::fs;
28+
JS::PersistentRooted<JSObject *> FastEdge::secret;
2829

2930
bool FastEdge::getEnv(JSContext* cx, unsigned argc, JS::Value* vp) {
3031
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
@@ -96,6 +97,33 @@ bool FastEdge::readFileSync(JSContext *cx, unsigned argc, JS::Value *vp) {
9697
return true;
9798
}
9899

100+
bool FastEdge::getSecret(JSContext* cx, unsigned argc, JS::Value* vp) {
101+
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
102+
if (!args.requireAtLeast(cx, "getSecret", 1)) {
103+
JS_ReportErrorUTF8(cx, "getSecret() -> requires at least 1 argument");
104+
return false;
105+
}
106+
// Convert the first argument to a string
107+
JS::RootedString jsKey(cx, JS::ToString(cx, args[0]));
108+
if (!jsKey) {
109+
return false;
110+
}
111+
// Encode the JS string to a C++ string
112+
JS::UniqueChars keyChars = JS_EncodeStringToUTF8(cx, jsKey);
113+
if (!keyChars) {
114+
return false;
115+
}
116+
auto secretValue = host_api::get_secret_vars(keyChars.get());
117+
if (!secretValue.size()) {
118+
args.rval().setNull();
119+
return true;
120+
}
121+
122+
JS::RootedString secretValueStr(cx, JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(secretValue.begin(), secretValue.size())));
123+
args.rval().setString(secretValueStr);
124+
return true;
125+
}
126+
99127
const JSPropertySpec FastEdge::properties[] = {
100128
JS_PSG("env", getEnv, JSPROP_ENUMERATE),
101129
JS_PS_END
@@ -118,6 +146,7 @@ bool install(api::Engine *engine) {
118146
const JSFunctionSpec methods[] = {
119147
JS_FN("getEnv", FastEdge::getEnv, 1, JSPROP_ENUMERATE),
120148
JS_FN("readFileSync", FastEdge::readFileSync, 1, JSPROP_ENUMERATE),
149+
JS_FN("getSecret", FastEdge::getSecret, 1, JSPROP_ENUMERATE),
121150
JS_FS_END
122151
};
123152

@@ -156,6 +185,20 @@ bool install(api::Engine *engine) {
156185
return false;
157186
}
158187

188+
// fastedge:secret
189+
RootedValue get_secret_val(engine->cx());
190+
if (!JS_GetProperty(engine->cx(), fastedge, "getSecret", &get_secret_val)) {
191+
return false;
192+
}
193+
RootedObject secret_builtin(engine->cx(), JS_NewObject(engine->cx(), nullptr));
194+
if (!JS_SetProperty(engine->cx(), secret_builtin, "getSecret", get_secret_val)) {
195+
return false;
196+
}
197+
RootedValue secret_builtin_val(engine->cx(), JS::ObjectValue(*secret_builtin));
198+
if (!engine->define_builtin_module("fastedge::secret", secret_builtin_val)) {
199+
return false;
200+
}
201+
159202
return true;
160203
}
161204

runtime/fastedge/builtins/fastedge.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ class FastEdge : public builtins::BuiltinNoConstructor<FastEdge> {
2323

2424
static JS::PersistentRooted<JSObject *> env;
2525
static JS::PersistentRooted<JSObject *> fs;
26+
static JS::PersistentRooted<JSObject *> secret;
2627

2728
static const JSPropertySpec properties[];
2829

2930
static bool readFileSync(JSContext *cx, unsigned argc, JS::Value *vp);
3031
static bool getEnv(JSContext *cx, unsigned argc, JS::Value *vp);
32+
static bool getSecret(JSContext *cx, unsigned argc, JS::Value *vp);
3133

3234
};
3335

runtime/fastedge/host-api/bindings/bindings.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
__attribute__((__import_module__("gcore:fastedge/dictionary"), __import_name__("get")))
66
extern void __wasm_import_gcore_fastedge_dictionary_get(int32_t, int32_t, int32_t);
77

8+
__attribute__((__import_module__("gcore:fastedge/secret"), __import_name__("get")))
9+
extern void __wasm_import_gcore_fastedge_secret_get(int32_t, int32_t, int32_t);
10+
811
__attribute__((__import_module__("wasi:cli/environment@0.2.0"), __import_name__("get-environment")))
912
extern void __wasm_import_wasi_cli_0_2_0_environment_get_environment(int32_t);
1013

@@ -544,6 +547,23 @@ void gcore_fastedge_dictionary_option_string_free(gcore_fastedge_dictionary_opti
544547
}
545548
}
546549

550+
void gcore_fastedge_secret_error_free(gcore_fastedge_secret_error_t *ptr) {
551+
switch ((int32_t) ptr->tag) {
552+
case 2: {
553+
bindings_string_free(&ptr->val.other);
554+
break;
555+
}
556+
}
557+
}
558+
559+
void gcore_fastedge_secret_result_option_string_error_free(gcore_fastedge_secret_result_option_string_error_t *ptr) {
560+
if (!ptr->is_err) {
561+
gcore_fastedge_dictionary_option_string_free(&ptr->val.ok);
562+
} else {
563+
gcore_fastedge_secret_error_free(&ptr->val.err);
564+
}
565+
}
566+
547567
void wasi_cli_0_2_0_environment_tuple2_string_string_free(wasi_cli_0_2_0_environment_tuple2_string_string_t *ptr) {
548568
bindings_string_free(&ptr->f0);
549569
bindings_string_free(&ptr->f1);
@@ -1718,6 +1738,61 @@ bool gcore_fastedge_dictionary_get(bindings_string_t *name, bindings_string_t *r
17181738
return option.is_some;
17191739
}
17201740

1741+
bool gcore_fastedge_secret_get(bindings_string_t *key, gcore_fastedge_dictionary_option_string_t *ret, gcore_fastedge_secret_error_t *err) {
1742+
__attribute__((__aligned__(4)))
1743+
uint8_t ret_area[16];
1744+
int32_t ptr = (int32_t) &ret_area;
1745+
__wasm_import_gcore_fastedge_secret_get((int32_t) (*key).ptr, (int32_t) (*key).len, ptr);
1746+
gcore_fastedge_secret_result_option_string_error_t result;
1747+
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
1748+
case 0: {
1749+
result.is_err = false;
1750+
gcore_fastedge_dictionary_option_string_t option;
1751+
switch ((int32_t) (*((uint8_t*) (ptr + 4)))) {
1752+
case 0: {
1753+
option.is_some = false;
1754+
break;
1755+
}
1756+
case 1: {
1757+
option.is_some = true;
1758+
option.val = (bindings_string_t) { (uint8_t*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
1759+
break;
1760+
}
1761+
}
1762+
1763+
result.val.ok = option;
1764+
break;
1765+
}
1766+
case 1: {
1767+
result.is_err = true;
1768+
gcore_fastedge_secret_error_t variant;
1769+
variant.tag = (int32_t) (*((uint8_t*) (ptr + 4)));
1770+
switch ((int32_t) variant.tag) {
1771+
case 0: {
1772+
break;
1773+
}
1774+
case 1: {
1775+
break;
1776+
}
1777+
case 2: {
1778+
variant.val.other = (bindings_string_t) { (uint8_t*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
1779+
break;
1780+
}
1781+
}
1782+
1783+
result.val.err = variant;
1784+
break;
1785+
}
1786+
}
1787+
if (!result.is_err) {
1788+
*ret = result.val.ok;
1789+
return 1;
1790+
} else {
1791+
*err = result.val.err;
1792+
return 0;
1793+
}
1794+
}
1795+
17211796
void wasi_cli_0_2_0_environment_get_environment(wasi_cli_0_2_0_environment_list_tuple2_string_string_t *ret) {
17221797
__attribute__((__aligned__(4)))
17231798
uint8_t ret_area[8];

runtime/fastedge/host-api/bindings/bindings.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ typedef struct {
2020
bindings_string_t val;
2121
} gcore_fastedge_dictionary_option_string_t;
2222

23+
// The set of errors which may be raised by functions in this interface
24+
typedef struct {
25+
uint8_t tag;
26+
union {
27+
bindings_string_t other;
28+
} val;
29+
} gcore_fastedge_secret_error_t;
30+
31+
// The requesting component does not have access to the specified key
32+
// (which may or may not exist).
33+
#define GCORE_FASTEDGE_SECRET_ERROR_ACCESS_DENIED 0
34+
// Decryption error.
35+
#define GCORE_FASTEDGE_SECRET_ERROR_DECRYPT_ERROR 1
36+
// Some implementation-specific error has occurred (e.g. I/O)
37+
#define GCORE_FASTEDGE_SECRET_ERROR_OTHER 2
38+
39+
typedef struct {
40+
bool is_err;
41+
union {
42+
gcore_fastedge_dictionary_option_string_t ok;
43+
gcore_fastedge_secret_error_t err;
44+
} val;
45+
} gcore_fastedge_secret_result_option_string_error_t;
46+
2347
typedef struct {
2448
bindings_string_t f0;
2549
bindings_string_t f1;
@@ -1538,6 +1562,11 @@ typedef wasi_http_0_2_0_types_own_response_outparam_t exports_wasi_http_0_2_0_in
15381562
// Returns `ok(none)` if the key does not exist.
15391563
extern bool gcore_fastedge_dictionary_get(bindings_string_t *name, bindings_string_t *ret);
15401564

1565+
// Imported Functions from `gcore:fastedge/secret`
1566+
// Get the secret associated with the specified `key`
1567+
// Returns `ok(none)` if the key does not exist.
1568+
extern bool gcore_fastedge_secret_get(bindings_string_t *key, gcore_fastedge_dictionary_option_string_t *ret, gcore_fastedge_secret_error_t *err);
1569+
15411570
// Imported Functions from `wasi:cli/environment@0.2.0`
15421571
// Get the POSIX-style environment variables.
15431572
//
@@ -3040,6 +3069,10 @@ extern bool wasi_io_0_2_0_streams_method_output_stream_write(wasi_io_0_2_0_strea
30403069

30413070
void gcore_fastedge_dictionary_option_string_free(gcore_fastedge_dictionary_option_string_t *ptr);
30423071

3072+
void gcore_fastedge_secret_error_free(gcore_fastedge_secret_error_t *ptr);
3073+
3074+
void gcore_fastedge_secret_result_option_string_error_free(gcore_fastedge_secret_result_option_string_error_t *ptr);
3075+
30433076
void wasi_cli_0_2_0_environment_tuple2_string_string_free(wasi_cli_0_2_0_environment_tuple2_string_string_t *ptr);
30443077

30453078
void wasi_cli_0_2_0_environment_list_tuple2_string_string_free(wasi_cli_0_2_0_environment_list_tuple2_string_string_t *ptr);
Binary file not shown.

runtime/fastedge/host-api/host_api.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,6 +1260,18 @@ HostString get_env_vars(std::string_view name) {
12601260
return bindings_string_to_host_string(value_str);
12611261
}
12621262

1263+
HostString get_secret_vars(std::string_view name) {
1264+
auto key_str = string_view_to_world_string(name);
1265+
gcore_fastedge_dictionary_option_string_t ret{};
1266+
gcore_fastedge_secret_error_t err;
1267+
auto has_value = gcore_fastedge_secret_get(&key_str, &ret, &err);
1268+
if (has_value && ret.is_some) {
1269+
return bindings_string_to_host_string(ret.val);
1270+
}
1271+
return nullptr;
1272+
}
1273+
1274+
12631275
} // namespace host_api
12641276

12651277
static host_api::HttpIncomingRequest::RequestHandler REQUEST_HANDLER = nullptr;

runtime/fastedge/host-api/host_api_fastedge.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct JSErrorFormatString;
3131
namespace host_api {
3232

3333
HostString get_env_vars(std::string_view name);
34+
HostString get_secret_vars(std::string_view name);
3435

3536
} // namespace host_api
3637

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
interface secret {
2+
/// Get the secret associated with the specified `key`
3+
/// Returns `ok(none)` if the key does not exist.
4+
get: func(key: string) -> result<option<string>, error>;
5+
6+
/// The set of errors which may be raised by functions in this interface
7+
variant error {
8+
/// The requesting component does not have access to the specified key
9+
/// (which may or may not exist).
10+
access-denied,
11+
/// Decryption error.
12+
decrypt-error,
13+
/// Some implementation-specific error has occurred (e.g. I/O)
14+
other(string)
15+
}
16+
}

runtime/fastedge/host-api/wit/deps/fastedge/world.wit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ package gcore:fastedge;
22

33
world imports {
44
import dictionary;
5+
import secret;
56
}

src/componentize/es-bundle.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ const fastedgePackagePlugin = {
1515
case 'fs': {
1616
return { contents: `export const readFileSync = globalThis.fastedge.readFileSync;` };
1717
}
18+
case 'secret': {
19+
return { contents: `export const getSecret = globalThis.fastedge.getSecret;` };
20+
}
1821
default: {
1922
return { contents: '' };
2023
}

0 commit comments

Comments
 (0)