From 9ea3ab99a7cf43df3838e62e65ee9aada6efa522 Mon Sep 17 00:00:00 2001 From: Adel Golghalyani <48685760+Ad96el@users.noreply.github.com> Date: Thu, 22 Feb 2024 09:47:57 +0100 Subject: [PATCH] chore: prepare for release (#14) --- .dockerignore | 3 +- .envrc | 1 - .github/workflows/dev-ci-cd.yml | 51 +-------- Cargo.toml | 9 ++ Dockerfile | 26 ++--- LICENSE.md | 9 ++ README.md | 72 +++++++++++- config_example.yaml | 46 ++++++++ docker-compose.yaml | 12 +- flake.lock | 115 -------------------- flake.nix | 64 ----------- frontend/src/App.tsx | 7 ++ frontend/src/api/credential.ts | 5 +- frontend/src/api/dataProvider.ts | 4 +- frontend/src/api/endpoints.ts | 19 ++++ frontend/src/api/session.ts | 7 +- frontend/src/components/AttestationList.tsx | 13 +-- frontend/src/components/Dashboard.tsx | 5 +- frontend/src/layout/Login.tsx | 3 +- frontend/src/utils/utils.ts | 24 +++- scripts/start.sh | 15 +++ src/configuration.rs | 8 +- src/main.rs | 20 ++-- src/routes/attestation_requests.rs | 7 +- src/routes/credentials.rs | 7 +- src/routes/endpoints.rs | 14 +++ src/routes/mod.rs | 2 + 27 files changed, 278 insertions(+), 290 deletions(-) delete mode 100644 .envrc create mode 100644 LICENSE.md create mode 100644 config_example.yaml delete mode 100644 flake.lock delete mode 100644 flake.nix create mode 100644 frontend/src/api/endpoints.ts create mode 100755 scripts/start.sh create mode 100644 src/routes/endpoints.rs diff --git a/.dockerignore b/.dockerignore index 6d3fce5..7ac93e4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,5 +2,4 @@ target /node_modules/ .env - - +/dist/ diff --git a/.envrc b/.envrc deleted file mode 100644 index 3550a30..0000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -use flake diff --git a/.github/workflows/dev-ci-cd.yml b/.github/workflows/dev-ci-cd.yml index 1f7099c..2e3d278 100644 --- a/.github/workflows/dev-ci-cd.yml +++ b/.github/workflows/dev-ci-cd.yml @@ -30,58 +30,15 @@ jobs: - name: Build image run: | - docker build --build-arg BACKEND_URL=$BACKEND_URL --build-arg AUTH_URL=$AUTH_URL --build-arg BUILD_FEATURE="" -t dena-attester_peregrine:latest . - env: - BACKEND_URL: ${{ vars.BACKEND_URL }} - AUTH_URL: ${{ vars.AUTH_URL }} - - - name: Tag, and push image to Amazon ECR - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: dena/attester_peregrine - IMAGE_TAG: ${{ github.sha }} - run: | - docker tag dena-attester_peregrine $ECR_REGISTRY/$ECR_REPOSITORY:latest - docker tag dena-attester_peregrine $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest - - deploy-prod: - name: Build image and push to Amazon ECR (Prod) - runs-on: ubuntu-latest - environment: production - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: eu-central-1 - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 - with: - mask-password: "true" - - - name: Build image - run: | - docker build --build-arg BACKEND_URL=$BACKEND_URL --build-arg AUTH_URL=$AUTH_URL -t dena-attester_spiritnet:latest . - env: - BACKEND_URL: ${{ vars.BACKEND_URL }} - AUTH_URL: ${{ vars.AUTH_URL }} + docker build -t dena-attester:latest . - name: Tag, and push image to Amazon ECR env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: dena/attester_spiritnet + ECR_REPOSITORY: dena/attester IMAGE_TAG: ${{ github.sha }} run: | - docker tag dena-attester_spiritnet $ECR_REGISTRY/$ECR_REPOSITORY:latest - docker tag dena-attester_spiritnet $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + docker tag dena-attester $ECR_REGISTRY/$ECR_REPOSITORY:latest + docker tag dena-attester $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest diff --git a/Cargo.toml b/Cargo.toml index fcf9468..33ccd92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,3 +51,12 @@ uuid = {version = "1.4.1", features = ["v4", "serde"]} [features] default = [] spiritnet = [] + +[[bin]] +features = ["spiritnet"] +name = "attester_spiritnet" +path = "src/main.rs" + +[[bin]] +name = "attester_peregrine" +path = "src/main.rs" diff --git a/Dockerfile b/Dockerfile index 375fc58..605caa4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,6 @@ # Frontend Build Stage FROM node:20.5.1 as frontend-build -ARG AUTH_URL=https://dev.opendid.kilt.io/api/v1/authorize -ARG BACKEND_URL=http://0.0.0.0:${port}/api/v1 -ARG WSS_ENDPOINT=wss://peregrine.kilt.io:443/parachain-public-ws - - -ENV VITE_SIMPLE_REST_URL=${BACKEND_URL} \ - VITE_AUTH_URL=${AUTH_URL} \ - VITE_WSS_ENDPOINT=${WSS_ENDPOINT} - WORKDIR /usr/src/app # Copy only package.json and yarn.lock first to leverage Docker cache @@ -24,8 +15,6 @@ RUN yarn build # Backend Build Stage FROM rust:buster as backend-build -ARG BUILD_FEATURE=--features=spiritnet - RUN apt-get update && \ apt-get -y upgrade && \ apt-get -y install libpq-dev @@ -34,9 +23,8 @@ WORKDIR /app COPY . /app/ - # Build backend -RUN cargo build --release --bin=attester-backend --package=attester-backend $BUILD_FEATURE +RUN cargo build --release --bins # Final Stage FROM rust:slim-buster @@ -49,13 +37,15 @@ WORKDIR /app COPY --from=frontend-build /usr/src/app/dist /usr/share/html # Copy backend build -COPY --from=backend-build /app/target/release/attester-backend /app/attester-backend +COPY --from=backend-build /app/target/release/attester_spiritnet /app/attester_spiritnet +COPY --from=backend-build /app/target/release/attester_peregrine /app/attester_peregrine -# Copy migrations and config -COPY /migrations /app/migrations +# Copy migrations config and scripts +COPY ./migrations /app/migrations +COPY ./scripts/start.sh /app/start.sh VOLUME /app/config.yaml EXPOSE ${PORT} -# Run migrations and start the application -CMD ["/app/attester-backend" , "/app/config.yaml"] +#start the application +CMD ["./start.sh" ] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..909eedf --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +Copyright (c) 2023-2024, Built on KILT. +All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software must display the following acknowledgement: Built on KILT. +4. Neither the name of KILT Protocol nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY KILT PROTOCOL AND ITS CONTRIBUTORS ‘’AS IS’' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL KILT PROTOCOL OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index ab6a4d0..250c44f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,71 @@ -# Hello +# Attester Service -send help! \ No newline at end of file +The Attester Service is responsible for generating various credentials for users, with an authentication mechanism that distinguishes between regular users and employees. Users can request different types of credentials, which can then be approved by employees. The service implements the KILT [Credential API](https://github.com/KILTprotocol/spec-ext-credential-api), allowing users to store their credentials in their identity wallet. Authentication is facilitated by fetching a JWT token from [OpenDID](https://github.com/KILTprotocol/opendid). Users can log in with a DID, while employees require additional credentials. + +A demonstration deployment for Peregrine can be accessed [here](https://dena-attester-dev.kilt.io/#/login), and a Spiritnet deployment is available [here](https://dena-attester.kilt.io/#/login). + +## Usage + +All environment variables must be configured in a `config.yaml` file. An example `config.yaml` file is provided [here](./config_example.yaml), with explanations of the variables included. + +### Local Debugging Frontend + +The frontend utilizes Vite as a bundler. To develop, simply run `yarn dev`. To build the frontend, execute `yarn build`. + +### Local Debugging Backend VsCode + +Create a `.vscode/launch.json` file and paste the following content: + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 'attester-backend'", + "cargo": { + "args": ["build", "--bins"], + "filter": { + "name": "attester_peregrine", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}", + "env": { + "CONFIG": "./config.yaml" + } + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 'attester-backend'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=attester-backend", + "--package=attester-backend" + ], + "filter": { + "name": "attester-backend", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} +``` + +Please note that the above configuration is tailored for Peregrine. If debugging on Spiritnet, adjust the filter accordingly. + +### Docker + +A docker-compose file is provided. To start the containers, run `docker-compose up`. + +### Database + +The Rust backend utilizes sqlx for database interactions. If a query is modified, update the metadata to support offline compile-time verification using the command `cargo sqlx prepare`. New migrations can be added with `cargo sqlx migrate add`, and existing migrations can be executed via CLI with `cargo sqlx migrate run`. The source code manages migrations automatically. diff --git a/config_example.yaml b/config_example.yaml new file mode 100644 index 0000000..8e79098 --- /dev/null +++ b/config_example.yaml @@ -0,0 +1,46 @@ +# Port on which the server runs +port: 5656 + +# The path to the bundled frontend. +frontEndPath: /path/to/your/frontend + +# The socket connection to the Kilt endpoint. +endpoint: wss://spiritnet.api.onfinality.io:443/public-ws + +# The application name required for the credential API. +appName: dena-attester + +# The PostgreSQL database URL. +databaseUrl: + +# The redirect URL which is needed by OpenDID. +redirectUrls: + +# Seed used for creating credentials. +attesterDidSeed: + +# Seed used for the attestation keys by the Attester DID. +attesterAttestationSeed: + +# The secret used to verify the JWT token from OpenDID. +jwtSecret: super-secret-jwt-secret + +# A payer seed, who pays to anchor the credentials to the blockchain. +payerSeed: + +# The URL endpoint for login. +authUrl: "https://opendid.kilt.io/api/v1/authorize" + +# Session variables for creating a secure session needed by the credential API. +session: + keyUri: + naclSecretKey: + naclPublicKey: + sessionKey: + +# The well-known DID configuration required by Sporran. +wellKnownDid: + did: + origin: + keyUri: + seed: diff --git a/docker-compose.yaml b/docker-compose.yaml index be1b779..4bb5421 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -11,15 +11,25 @@ services: - "5432:5432" volumes: - data:/var/lib/postgresql/data + networks: + - default server: build: ./ + environment: + RUNTIME: peregrine + CONFIG: /app/config.yaml ports: - "5656:5656" depends_on: - postgres volumes: - ./config.yaml:/app/config.yaml + networks: + - default + +networks: + default: volumes: - data: \ No newline at end of file + data: diff --git a/flake.lock b/flake.lock deleted file mode 100644 index c28c963..0000000 --- a/flake.lock +++ /dev/null @@ -1,115 +0,0 @@ -{ - "nodes": { - "flake-parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1693611461, - "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1693844670, - "narHash": "sha256-t69F2nBB8DNQUWHD809oJZJVE+23XBrth4QZuVd6IE0=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "3c15feef7770eb5500a4b8792623e2d6f598c9c1", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "dir": "lib", - "lastModified": 1693471703, - "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85", - "type": "github" - }, - "original": { - "dir": "lib", - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1691654369, - "narHash": "sha256-gSILTEx1jRaJjwZxRlnu3ZwMn1FVNk80qlwiCX8kmpo=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "ce5e4a6ef2e59d89a971bc434ca8ca222b9c7f5e", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs", - "systems": "systems", - "treefmt-nix": "treefmt-nix" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "treefmt-nix": { - "inputs": { - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1693817438, - "narHash": "sha256-fg3+n4Ky1gCzDtPm0MomMTFw0YkH05Y8ojy5t7bkfHg=", - "owner": "numtide", - "repo": "treefmt-nix", - "rev": "b8d3a059f5487d6767d07c3716386753e3132d9f", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "treefmt-nix", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 7aa6779..0000000 --- a/flake.nix +++ /dev/null @@ -1,64 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - flake-parts.url = "github:hercules-ci/flake-parts"; - systems.url = "github:nix-systems/default"; - - # Dev tools - treefmt-nix.url = "github:numtide/treefmt-nix"; - }; - - outputs = inputs: - inputs.flake-parts.lib.mkFlake { inherit inputs; } { - systems = import inputs.systems; - imports = [ - inputs.treefmt-nix.flakeModule - ]; - perSystem = { config, self', pkgs, lib, system, ... }: - let - cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); - nonRustDeps = [ - pkgs.libiconv - ]; - in - { - # Rust package - packages.default = pkgs.rustPlatform.buildRustPackage { - inherit (cargoToml.package) name version; - src = ./.; - cargoLock.lockFile = ./Cargo.lock; - }; - - # Rust dev environment - devShells.default = pkgs.mkShell { - inputsFrom = [ - config.treefmt.build.devShell - ]; - shellHook = '' - # For rust-analyzer 'hover' tooltips to work. - export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc} - ''; - buildInputs = nonRustDeps; - nativeBuildInputs = with pkgs; [ - just - rustc - cargo - cargo-watch - rust-analyzer - openssl - pkg-config - ]; - }; - - # Add your auto-formatters here. - # cf. https://numtide.github.io/treefmt/ - treefmt.config = { - projectRootFile = "flake.nix"; - programs = { - nixpkgs-fmt.enable = true; - rustfmt.enable = true; - }; - }; - }; - }; -} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3f634f2..eccc317 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react' import { Admin, Resource } from 'react-admin' import { Layout, Login } from './layout' @@ -5,8 +6,14 @@ import { darkTheme, lightTheme } from './layout/themes' import { dataProvider } from './api/dataProvider' import { authProvider } from './api/authProvider' import { AttestationCreate, AttestationList, Dashboard } from './components' +import { getEndpoints } from './api/endpoints' +import { storeEndpoints } from './utils/utils' export function App() { + useEffect(() => { + getEndpoints().then((endpoints) => storeEndpoints(endpoints)) + }, []) + return ( > { + + const origin = window.location.origin; + const backendUrl = `${origin}/api/v1` + const endpointUrl = `${backendUrl}/endpoints`; + const response = await axios.get>(endpointUrl) + + if (response.status !== 200) { + throw new Error("Could not fetch endpoints") + } + + const endpoints = response.data; + endpoints.push(backendUrl) + + return endpoints +} diff --git a/frontend/src/api/session.ts b/frontend/src/api/session.ts index 8c08e16..bcd7c71 100644 --- a/frontend/src/api/session.ts +++ b/frontend/src/api/session.ts @@ -1,3 +1,4 @@ +import { getBackendUrl } from '../utils/utils'; import { getAxiosClient } from './dataProvider' import { InjectedWindowProvider, PubSubSessionV1, PubSubSessionV2 } from '@kiltprotocol/kilt-extension-api' @@ -7,8 +8,8 @@ export async function getSession(provider: InjectedWindowProvider): Promise { const record = useRecordContext() // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -39,7 +41,6 @@ export default function AttestationList() { const ClaimButton = () => { const record = useRecordContext() - const apiURL = import.meta.env.VITE_SIMPLE_REST_URL const [isLoading, setIsLoading] = useState(false) const notify = useNotify() const refresh = useRefresh() @@ -51,7 +52,7 @@ export default function AttestationList() { setIsLoading(true) const client = await getAxiosClient() - await client.put(`${apiURL}/attestation_request/${record.id}/approve`) + await client.put(`${apiUrl}/attestation_request/${record.id}/approve`) setTimeout(() => { setIsLoading(false) refresh() @@ -81,7 +82,6 @@ export default function AttestationList() { const MarkApproveButton = () => { const record = useRecordContext() - const apiURL = import.meta.env.VITE_SIMPLE_REST_URL const [isLoading, setIsLoading] = useState(false) const notify = useNotify() const refresh = useRefresh() @@ -92,7 +92,7 @@ export default function AttestationList() { } setIsLoading(true) const client = await getAxiosClient() - await client.put(`${apiURL}/attestation_request/${record.id}/mark_approve`) + await client.put(`${apiUrl}/attestation_request/${record.id}/mark_approve`) refresh() notify('Marked as claimable') setIsLoading(false) @@ -119,7 +119,6 @@ export default function AttestationList() { const RevokeButton = () => { const record = useRecordContext() const [isLoading, setIsLoading] = useState(false) - const apiURL = import.meta.env.VITE_SIMPLE_REST_URL const notify = useNotify() const refresh = useRefresh() @@ -130,7 +129,7 @@ export default function AttestationList() { setIsLoading(true) const client = await getAxiosClient() - await client.put(`${apiURL}/attestation_request/${record.id}/revoke`) + await client.put(`${apiUrl}/attestation_request/${record.id}/revoke`) setTimeout(() => { setIsLoading(false) diff --git a/frontend/src/components/Dashboard.tsx b/frontend/src/components/Dashboard.tsx index dfd5e6e..10886b8 100644 --- a/frontend/src/components/Dashboard.tsx +++ b/frontend/src/components/Dashboard.tsx @@ -4,6 +4,7 @@ import Card from '@mui/material/Card' import Typography from '@mui/material/Typography' import { getAxiosClient } from '../api/dataProvider' import CircularProgress from '@mui/material/CircularProgress' +import { getBackendUrl } from '../utils/utils' interface AttestationChartData { date: string @@ -25,8 +26,8 @@ const Dashboard = () => { const fetchData = async () => { try { const client = await getAxiosClient() - const apiURL = import.meta.env.VITE_SIMPLE_REST_URL - const res = await client.get(`${apiURL}/attestation_request/metric/kpis`) + const apiUrl = getBackendUrl() + const res = await client.get(`${apiUrl}/attestation_request/metric/kpis`) setKpi(res.data) } catch (error) { diff --git a/frontend/src/layout/Login.tsx b/frontend/src/layout/Login.tsx index 6e6da63..fc265bc 100644 --- a/frontend/src/layout/Login.tsx +++ b/frontend/src/layout/Login.tsx @@ -4,13 +4,14 @@ import LockIcon from '@mui/icons-material/Lock' import { Utils } from '@kiltprotocol/sdk-js' import Box from '@mui/material/Box' import authProvider from '../api/authProvider' +import { getAuthorizeUrl } from '../utils/utils' export default function Login() { const handleSubmit = useCallback((clientId: string) => { const nonce = Utils.UUID.generate() const state = Utils.UUID.generate() - const url = new URL(import.meta.env.VITE_AUTH_URL) + const url = new URL(getAuthorizeUrl()) url.searchParams.append('response_type', 'id_token') url.searchParams.append('client_id', clientId as string) url.searchParams.append('redirect_uri', `${window.location.origin}/#/login`) diff --git a/frontend/src/utils/utils.ts b/frontend/src/utils/utils.ts index 292fef5..e79e391 100644 --- a/frontend/src/utils/utils.ts +++ b/frontend/src/utils/utils.ts @@ -2,7 +2,7 @@ import { ICType, CType, connect } from '@kiltprotocol/sdk-js' import authProvider from '../api/authProvider' export async function fetchCType(ctypeId: ICType['$id']): Promise { - await connect(import.meta.env.VITE_WSS_ENDPOINT) + await connect(getKiltEndpoint()) return CType.fetchFromChain(ctypeId) } @@ -10,3 +10,25 @@ export function isUserAdmin() { const role = authProvider.getRole() return role === 'admin' } + +export function storeEndpoints(endpoints: Array) { + const authorizeUrl = endpoints[0]; + const kiltEndpoint = endpoints[1]; + const backendUrl = endpoints[2]; + + localStorage.setItem("authorizeUrl", authorizeUrl); + localStorage.setItem("kiltEndpoint", kiltEndpoint); + localStorage.setItem("backendUrl", backendUrl); +} + +export function getBackendUrl(): string { + return localStorage.getItem("backendUrl") || "" +} + +export function getKiltEndpoint(): string { + return localStorage.getItem("kiltEndpoint") || "" +} + +export function getAuthorizeUrl(): string { + return localStorage.getItem("authorizeUrl") || "" +} diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100755 index 0000000..05be1a9 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,15 @@ +#! /bin/bash + +runtime="$RUNTIME" +export CONFIG="/app/config.yaml" + +if [ "$runtime" == "spiritnet" ]; then + /app/attester_spiritnet +fi + +if [ "$runtime" == "peregrine" ]; then + /app/attester_peregrine +fi + +echo "no valid runtime provided" +exit 1 diff --git a/src/configuration.rs b/src/configuration.rs index 9448e5a..35862a1 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -4,7 +4,6 @@ use subxt::{ ext::sp_core::{sr25519::Pair, Pair as PairTrait}, tx::PairSigner, utils::AccountId32, - OnlineClient, }; use crate::kilt::KiltConfig; @@ -13,7 +12,7 @@ use crate::kilt::KiltConfig; #[serde(rename_all = "camelCase")] pub struct Configuration { pub port: u16, - pub kilt_endpoint: String, + pub endpoint: String, pub session: SessionConfig, #[serde(rename = "wellKnownDid")] pub well_known_did_config: WellKnownDidConfig, @@ -24,6 +23,7 @@ pub struct Configuration { pub jwt_secret: String, pub payer_seed: String, pub app_name: String, + pub auth_url: String, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -55,10 +55,6 @@ impl Configuration { Ok(PairSigner::new(pair)) } - pub async fn get_client(&self) -> anyhow::Result> { - Ok(OnlineClient::::from_url(&self.kilt_endpoint).await?) - } - pub fn get_did(&self) -> anyhow::Result { let pair = Pair::from_string_with_seed(&self.attester_did_seed, None)?.0; Ok(pair.public().into()) diff --git a/src/main.rs b/src/main.rs index f5be7b0..2df90e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use clap::Parser; use sodiumoxide::crypto::box_::SecretKey; use sqlx::{Pool, Postgres}; use std::sync::Arc; -use subxt::{ext::sp_core::sr25519::Pair, tx::PairSigner, utils::AccountId32, OnlineClient}; +use subxt::{ext::sp_core::sr25519::Pair, tx::PairSigner, utils::AccountId32}; // internal imports use auth::jwt_validator; @@ -24,7 +24,7 @@ use cli::Cli; use configuration::{Configuration, SessionConfig}; use kilt::{create_well_known_did_config, KiltConfig, WellKnownDidConfig}; use routes::{ - get_attestation_request_scope, get_challenge_scope, get_credential_scope, + get_attestation_request_scope, get_challenge_scope, get_credential_scope, get_endpoint_scope, well_known_did_config_handler, }; @@ -35,12 +35,13 @@ pub struct AppState { pub signer: Arc>, pub app_name: String, pub jwt_secret: String, - pub chain_client: Arc>, pub db_executor: Arc>, pub attester_did: AccountId32, pub well_known_did_config: WellKnownDidConfig, pub session: SessionConfig, pub encryption_key: SecretKey, + pub auth_url: String, + pub endpoint: String, } #[actix_web::main] @@ -63,13 +64,13 @@ async fn main() -> anyhow::Result<()> { #[cfg(feature = "spiritnet")] log::info!( "Spiritnet features are enabled. WSS address is set to: {}", - &config.kilt_endpoint + &config.endpoint ); #[cfg(not(feature = "spiritnet"))] log::info!( "Peregrine features are enabled. WSS address is set to: {}", - &config.kilt_endpoint + &config.endpoint ); let signer = config @@ -80,11 +81,6 @@ async fn main() -> anyhow::Result<()> { .get_payer_signer() .context("Creating signer should not fail.")?; - let chain_client = config - .get_client() - .await - .context("Creation of online client failed")?; - let encryption_key = config .get_nacl_secret_key() .context("Creating of encryption key failed.")?; @@ -100,9 +96,10 @@ async fn main() -> anyhow::Result<()> { db_executor: Arc::new(db_executor), payer: Arc::new(payer), signer: Arc::new(signer), - chain_client: Arc::new(chain_client), attester_did, encryption_key, + auth_url: config.auth_url, + endpoint: config.endpoint, }; log::info!("started server at port: {}", port); @@ -119,6 +116,7 @@ async fn main() -> anyhow::Result<()> { .service(get_attestation_request_scope().wrap(auth.clone())) .service(get_challenge_scope().wrap(auth.clone())) .service(get_credential_scope().wrap(auth.clone())) + .service(get_endpoint_scope()) .service(well_known_did_config_handler) .service(actix_files::Files::new("/", &front_end_path).index_file("index.html")) }) diff --git a/src/routes/attestation_requests.rs b/src/routes/attestation_requests.rs index 2b93cff..7207156 100644 --- a/src/routes/attestation_requests.rs +++ b/src/routes/attestation_requests.rs @@ -4,7 +4,7 @@ use actix_web::{ HttpResponse, Scope, }; -use subxt::ext::sp_core::H256; +use subxt::{ext::sp_core::H256, OnlineClient}; use uuid::Uuid; use crate::{ @@ -20,6 +20,7 @@ use crate::{ }, }, error::AppError, + kilt::KiltConfig, utils::{is_user_admin, is_user_allowed_to_see_data, is_user_allowed_to_update_data}, AppState, }; @@ -124,7 +125,7 @@ async fn approve_attestation( let payer = state.payer.clone(); let did = state.attester_did.clone(); - let chain_client = state.chain_client.clone(); + let chain_client = OnlineClient::::from_url(&state.endpoint).await?; let signer = state.signer.clone(); log::info!( @@ -216,7 +217,7 @@ async fn revoke_attestation( let payer = state.payer.clone(); let did = state.attester_did.clone(); - let chain_client = state.chain_client.clone(); + let chain_client = OnlineClient::::from_url(&state.endpoint).await?; let signer = state.signer.clone(); log::info!( diff --git a/src/routes/credentials.rs b/src/routes/credentials.rs index a1ec228..30e1e75 100644 --- a/src/routes/credentials.rs +++ b/src/routes/credentials.rs @@ -1,6 +1,7 @@ use actix_web::{post, web, HttpResponse, Scope}; use sodiumoxide::crypto::box_; use sp_core::H256; +use subxt::OnlineClient; use uuid::Uuid; use crate::{ @@ -14,6 +15,7 @@ use crate::{ }, }, error::AppError, + kilt::KiltConfig, AppState, }; @@ -110,10 +112,11 @@ async fn request_attestation( } remove_session(&state.db_executor, &session_id).await?; + let chain_client = OnlineClient::::from_url(&state.endpoint).await?; let others_pubkey = crate::kilt::get_encryption_key_from_fulldid_key_uri( &encrypted_message.sender_key_uri, - &state.chain_client, + &chain_client, ) .await?; @@ -140,7 +143,7 @@ async fn request_attestation( let payer = state.payer.clone(); let did = state.attester_did.clone(); - let chain_client = state.chain_client.clone(); + let chain_client = OnlineClient::::from_url(&state.endpoint).await?; let signer = state.signer.clone(); crate::kilt::create_claim( diff --git a/src/routes/endpoints.rs b/src/routes/endpoints.rs new file mode 100644 index 0000000..b077a78 --- /dev/null +++ b/src/routes/endpoints.rs @@ -0,0 +1,14 @@ +use actix_web::{get, web, HttpResponse, Scope}; + +use crate::{error::AppError, AppState}; + +#[get("")] +async fn get_endpoints(state: web::Data) -> Result { + let auth_endpoint = state.auth_url.clone(); + let wss_endpoint = state.endpoint.clone(); + Ok(HttpResponse::Ok().json(vec![auth_endpoint, wss_endpoint])) +} + +pub fn get_endpoint_scope() -> Scope { + web::scope("/api/v1/endpoints").service(get_endpoints) +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index fcb11cd..5d12ddb 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,9 +1,11 @@ mod attestation_requests; mod challenge; mod credentials; +mod endpoints; mod well_known_did_config; pub use attestation_requests::get_attestation_request_scope; pub use challenge::get_challenge_scope; pub use credentials::get_credential_scope; +pub use endpoints::get_endpoint_scope; pub use well_known_did_config::well_known_did_config_handler;