Skip to content

Commit

Permalink
First pass on auth
Browse files Browse the repository at this point in the history
  • Loading branch information
ekalvi committed Apr 12, 2024
1 parent effe0d9 commit 2e7b6a3
Show file tree
Hide file tree
Showing 21 changed files with 301 additions and 37 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/meya.check-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This workflow will install Python and Meya dependencies, runs tests and code formatting check
name: Meya build

on:
repository_dispatch:
push:
branches:
- '*'

jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10
uses: actions/setup-python@v1
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade \
--extra-index-url https://meya:${{ secrets.auth_token }}@grid.meya.ai/registry/pypi \
"pygit2==1.1.1" \
"meya-sdk>=2.0.0" \
"meya-cli>=2.0.0"
- name: Authenticate using Meya auth token
run: |
meya auth add --grid-url https://grid.meya.ai --auth-token ${{ secrets.auth_token }}
- name: Connect to production app
run: |
meya connect --grid-url https://grid.meya.ai --app-id ${{ secrets.app_id }}
- name: Check code syntax and formatting
run: meya check
- name: Run tests
run: meya test
- name: Deploy
if: github.ref == 'refs/heads/master'
run: meya push --force --build-image
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.direnv
.idea/*
.meya
.pytest_cache
__pycache__
node_modules
venv
vault.secret*.yaml
scratch/
10 changes: 10 additions & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[settings]
force_single_line=True
include_trailing_comma=True
use_parentheses=True
multi_line_output=3
line_width=79
lines_between_types=1
skip=.direnv,.idea,.git,.meya,.pytest_cache,dist,node_modules,venv,__pycache__
not_skip=__init__.py
known_first_party=*
1 change: 1 addition & 0 deletions .meyaignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.github/
39 changes: 2 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,5 @@
![Meya build](https://github.com/meya-ai/grid-template-hello-world/workflows/Meya%20build/badge.svg)

# hello-world

Basic template BFML and Python code that runs on Meya.

## Setup

```shell script
brew install python@3 libgit2
MEYA_AUTH_TOKEN=your_meya_auth_token
MEYA_APP_ID=app-your_app_id
# you can optionally setup a venv instead as well
python3 -m venv venv # optional
. venv/bin/activate # optional
pip3 install --upgrade \
--extra-index-url https://meya:$MEYA_AUTH_TOKEN@grid.meya.ai/registry/pypi \
"pygit2>=1.2.1" \
"meya-sdk>=2.0.0" \
"meya-cli>=2.0.0"
# auth (if needed)
meya auth add --auth-token $MEYA_AUTH_TOKEN
# connect to existing app
meya connect --app-id $MEYA_APP_ID
```

## Workflow
```shell script
meya check
meya format
meya test --watch
# to download secrets
meya vault download --file vault.secret.yaml
# if new secrets (after changing the yaml file)
meya vault upload --file vault.secret.yaml
meya push --watch
# for a full rebuild (useful for production deployments)
meya push --force --build-image
```
# auth-bot

Basic template BFML and Python code that runs on Meya.
6 changes: 6 additions & 0 deletions bot/default.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type: meya.bot.element
name: Auth Bot
avatar:
image: https://i.postimg.cc/brYQscPR/key.png
crop: square
markdown: true
22 changes: 22 additions & 0 deletions component/is_expired.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import time

from dataclasses import dataclass
from meya.component.element import Component
from meya.element.field import element_field
from meya.entry import Entry
from typing import List


def is_timestamp_expired(timestamp):
current_timestamp = int(time.time())
return timestamp < current_timestamp


@dataclass
class IsExpiredComponentElement(Component):
timestamp: int = element_field()

async def start(self) -> List[Entry]:
return self.respond(
data=dict(result=is_timestamp_expired(self.timestamp))
)
27 changes: 27 additions & 0 deletions component/sha256.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import hashlib
import hmac

from dataclasses import dataclass
from meya.component.element import Component
from meya.element.field import element_field
from meya.entry import Entry
from typing import List


def compute_hmac_sha256_signature(string, secret):
hashed = hmac.new(
secret.encode("utf-8"), string.encode("utf-8"), hashlib.sha256
)
return hashed.digest().hex()


@dataclass
class Sha256ComponentElement(Component):
string: str = element_field()
secret: str = element_field()

async def start(self) -> List[Entry]:
computed_signature = compute_hmac_sha256_signature(
self.string, self.secret
)
return self.respond(data=dict(result=computed_signature))
24 changes: 24 additions & 0 deletions component/sha256_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import pytest

from component.sha256 import compute_hmac_sha256_signature

# check online with https://devglan.com/online-tools/hmac-sha256-online


@pytest.mark.parametrize(
("string", "secret", "computed_hash"),
[
(
"erik@meya.ai",
"solar_eclipse",
"c99b34b1bf5f83f61f3d77f0bae4633a49ee857061634e0e67668edd45944911",
),
(
"erik@meya.ai1712881476",
"solar_eclipse",
"031d822ebc78950a475b0ba8f117b0aae44c6df5a7045e993324b9c46ae94a88",
),
],
)
def test_square(string: str, secret: str, computed_hash: str):
assert compute_hmac_sha256_signature(string, secret) == computed_hash
24 changes: 24 additions & 0 deletions examples/generate_page_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import time

from component.sha256 import compute_hmac_sha256_signature

# example
ttl = 5 * 60 # 5 minutes
current_timestamp = int(time.time()) + ttl
user_id = "erik@meya.ai"
secret = "solar_eclipse"
computed_signature = compute_hmac_sha256_signature(
f"{user_id}{current_timestamp}", secret
)
print(computed_signature)
print(
f"""
pageContext: {{
expires: {current_timestamp},
user_hash: "{computed_signature}"
}}
"""
)

# confirm with https://www.freeformatter.com/hmac-generator.html#before-output
# assert computed_signature == "fbafc1e2deedcf00932e64f8112b82774b6c4fa1430ee2543337b1d6da135e19"
5 changes: 5 additions & 0 deletions flow/catchall.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
triggers:
- catchall

steps:
- say: Sorry, I don't understand.
8 changes: 8 additions & 0 deletions flow/leak.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
triggers:
- keyword: leak

steps:
- say: Your secret is...
- typing: on
- delay: 1
- say: (@ user.secret )
37 changes: 37 additions & 0 deletions flow/open_page.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
triggers:
- type: page_open
when: (@ not thread.open_page )

steps:
- thread_set:
open_page: true

# Orb userId
- type: meya.user.component.try_reverse_lookup
integration: integration.orb
- flow_set: user_id

# verify identity (sha256 is valid not expired)
- type: component.sha256
string: (@ flow.user_id )(@ flow.context.expires )
secret: (@ vault.verification_secret )
- flow_set: user_hash
- type: component.is_expired
timestamp: (@ flow.context.expires )
- flow_set: is_expired

- if: (@ flow.user_hash == flow.context.user_hash and not flow.is_expired)
then:
jump: verified
else:
jump: not_verified

- (verified)
- say: ✅ Welcome, (@ flow.user_id )!
- end

- (not_verified)
- say: ⛔️You are denied.
- thread_set:
mode: blocked
- end
7 changes: 7 additions & 0 deletions flow/secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
triggers:
- keyword: secret

steps:
- ask: What's your secret?
- user_set: secret
- say: Ok, I'll remember that. Say "leak" to hear it.
1 change: 1 addition & 0 deletions integration/db.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
type: meya.db.integration
15 changes: 15 additions & 0 deletions integration/orb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
type: meya.orb.integration
identity_verification: false
theme:
brand_color: '#004a95'
launcher:
type: message
icon: ''
text: 🤐
composer:
character_limit:
length: 64
error_text: You've gone too far 😡
upload:
progress_text: Discombobulating ☢️
error_text: File couldn't upload 😭
1 change: 1 addition & 0 deletions integration/sensitive_data.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
type: meya.sensitive_data.integration
42 changes: 42 additions & 0 deletions page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test-bot-auth</title>
</head>
<body>
<script type="text/javascript">
window.orbConfig = {
connectionOptions: {
gridUrl: "https://grid.meya.ai",
appId: "app-dd40e256fb214703adbfa55cab3ca21f",
integrationId: "integration.orb",
connect: false,
// plain text userId (email in this case)
// Note, that this should be encrypted and decrypted in production
userId: "erik@meya.ai",
// this forces a new thread per page load (no chat history)
threadId: window.crypto.randomUUID(),
// HMAC 256 hash of the userId+exiry timestamp using a secret
// example hmac("erik@meya.ai1712881476") == "031d822ebc78950a475b0ba8f117b0aae44c6df5a7045e993324b9c46ae94a88"
pageContext: {
expires: 1712883389,
user_hash: "c43c7011eabaa5fc7bae5a18470ef1e31b35faec89c1715cecfadfa1a00b0917"
}
},
windowApi: true,
};
(function () {
var script = document.createElement("script");
script.type = "text/javascript";
script.async = true;
script.src = "https://cdn.meya.ai/v2/orb.js";
document.body.appendChild(script);
var fontStyleSheet = document.createElement("link");
fontStyleSheet.rel = "stylesheet";
fontStyleSheet.href = "https://cdn.meya.ai/font/inter.css";
document.body.appendChild(fontStyleSheet);
})();
</script>
</body>
</html>
15 changes: 15 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[tool.black]
exclude = '''
/(
\.direnv
| \.git
| \.meya
| \.idea
| node_modules
| venv
| __pycache__
| \.pytest_cache
)/
'''
line-length = 79
target-version = ['py37']
5 changes: 5 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[pytest]
python_files = *_test.py
norecursedirs = .direnv .git .idea .meya .pytest_cache node_modules venv __pycache__
filterwarnings = ignore::DeprecationWarning:jinja2
asyncio_mode = auto
1 change: 1 addition & 0 deletions vault.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
verification_secret: solar_eclipse

0 comments on commit 2e7b6a3

Please sign in to comment.