Commit f796fe10 authored by Jefferson Sofarelli's avatar Jefferson Sofarelli
Browse files

v0.1

parent 259b9e4b
build
data
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[{package.json,*.yml}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
ADMIN_JWT_SECRET=your_admin_jwt_secret
JWT_SECRET=your_jwt_secret
ISSUER_DID=your_did
ISSUER_KEY=content_of_your_jwk_file
VERIFICATION_METHOD=your_did_verification_method
TWITTER_APP_KEY=your_twitter_app_key
TWITTER_APP_SECRET=your_twitter_app_secret
DATABASE_HOST=127.0.0.1
DATABASE_PORT=3306
DATABASE_NAME=honorbox-db
DATABASE_USERNAME=root
DATABASE_PASSWORD=your_database_root_password
DATABASE_SSL=false
FRONTEND_URL=http://localhost:3000
PROFILE_URL=http://localhost:3001
PREVIEW_HOST=http://localhost:3002
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
MAIL_HOST=your_mail_host
MAIL_PORT=your_mail_port
MAIL_SECURE=true
MAIL_USER=your_email_user
MAIL_PASS=your_email_password
MAIL_FROM=your_admin_email_address
MAIL_REPLY_TO=your_reply_email_address
URL=https://your-ngrok-url-prefix.ngrok.io
.cache
build
**/node_modules/**
{
"parser": "babel-eslint",
"extends": "eslint:recommended",
"env": {
"commonjs": true,
"es6": true,
"node": true,
"browser": false
},
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": false
},
"sourceType": "module"
},
"globals": {
"strapi": true
},
"rules": {
"indent": ["error", 2, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"no-console": 0,
"quotes": ["error", "single"],
"semi": ["error", "always"]
}
}
############################
# OS X
############################
.DS_Store
.AppleDouble
.LSOverride
Icon
.Spotlight-V100
.Trashes
._*
############################
# Linux
############################
*~
############################
# Windows
############################
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
############################
# Packages
############################
*.7z
*.csv
*.dat
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
*.com
*.class
*.dll
*.exe
*.o
*.seed
*.so
*.swo
*.swp
*.swn
*.swm
*.out
*.pid
############################
# Logs and databases
############################
.tmp
*.log
*.sql
*.sqlite
*.sqlite3
############################
# Misc.
############################
*#
ssl
.idea
nbproject
############################
# Node.js
############################
lib-cov
lcov.info
pids
logs
results
node_modules
.node_history
############################
# Tests
############################
testApp
coverage
############################
# Strapi
############################
.env
.env.example
.env.production
license.txt
exports
*.cache
build
.strapi-updater.json
############################
# Data backup
############################
data
FROM ubuntu:20.04
RUN apt-get update \
&& apt-get install -y curl gnupg build-essential \
&& curl --silent --location https://deb.nodesource.com/setup_14.x | bash - \
&& apt-get update \
&& apt-get install -y nodejs
ENV NODE_ENV production
WORKDIR /honorbox-api
COPY ./ .
RUN npm run build
EXPOSE 1337
CMD ["npm", "start"]
# SourceCheck HonorBox API
API server for the [HonorBox
Project](https://github.com/SourceCheckOrg/honorbox), based on
[Strapi](https://strapi.io).
## Development environment setup
### Database
The HonorBox API requires a MySQL database and a Redis database. By default, both
are assumed to be running on the localhost, at their default ports. In
production this would, of course, not be recommended.
A simple dockerized setup for both can be found at [HonorBox
DB](https://github.com/SourceCheckOrg/honorbox-db).
### Node.js
A recent version of Node.js is required to run the API server.
We recommend installing Node.js in Linux and MacOS environments by using Node
Version Manager [nvm](https://github.com/nvm-sh/nvm).
The code was created using `v14.15.3`.
### SSI tooling
The HonorBox project depends on SSI (Self-Sovereign Identities) components developed by [Spruce
Systems](https://spruceid.dev/docs/) (USA).
The API server makes use of DIDKit Node library for handling DIDs and
signatures. Configuring it to issue credentials already requires a DID to sign
them, which can be done using DIDKit's command line interface. If any of the
following fails to build, check the latest instructions on their [DIDKit
repository](https://github.com/spruceid/didkit/).
DIDKit is written in [Rust](https://www.rust-lang.org/), which should be
installed using [Rustup](https://rustup.rs/).
Clone Spruce's ssi repository:
```
$ git clone https://github.com/spruceid/ssi --recurse-submodules
```
Clone Spruce's DIDKit repository:
```
$ git clone https://github.com/spruceid/didkit
```
Build DIDKit using Cargo:
```
$ cd didkit
$ cargo build
```
Once DIDKit is built, use it to generate a DID and a DID keypair:
```
$ ./target/debug/didkit generate-ed25519-key > key.jwk
$ ./target/debug/didkit key-to-did key -k key.jwk > did.txt
$ ./target/debug/didkit key-to-verification-method key > verificationMethod.txt
```
*NOTE:* Save the files `key.jwk`, `did.txt` and `verificationMethod.txt` in a safe
place. The content of the file `key.jwt` will be used as the environment variable
`ISSUER_KEY` and the content of the file `did.txt` will be used as the environment
variable `ISSUER_DID`. These values should be set in the file `.env` of this repository.
More information on that below.
### ngrok
The API server should be visible through a public URL so Spruce's Credible
wallet can interact with it.
One way to do that is to use [ngrok](https://ngrok.com/).
It's necessary to create a tunnel pointing to the `HTTP` port `1337`:
```
./ngrok http 1337
```
The address created by ngrok (something like https://020cd6dc2d4a.ngrok.io)
should be set as the environment variable `URL` in the file `.env`.
## Installing HonorBox API code
Clone this repository:
```
$ git clone https://github.com/SourceCheckOrg/honorbox-api.git
```
Install the dependencies
```
$ cd honorbox-api
$ npm i
```
Set up the environment variables mentioned before in the file called `.env`. For
security reasons, this file is not pushed to git, but a sample one is provided
with the name `.env.sample`. You can use it as starting point and change the
values according to your environment:
```
ADMIN_JWT_SECRET=your_admin_jwt_secret
JWT_SECRET=your_jwt_secret
ISSUER_DID=your_did
ISSUER_KEY=content_of_your_jwk_file
DATABASE_HOST=127.0.0.1
DATABASE_PORT=3306
DATABASE_NAME=honorbox-db
DATABASE_USERNAME=root
DATABASE_PASSWORD=your_database_root_password
DATABASE_SSL=false
FRONTEND_URL=http://localhost:3000
PROFILE_URL=http://localhost:3001
PREVIEW_URL=http://localhost:3002
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
URL=url_generated_by_ngrok
```
Build the administration panel using the provided configuration:
```
$ npm run build
```
Start the API server:
```
$ npm run develop
```
## Initial configuration
### Admin panel
Access the administration panel through the URL `http://localhost:1337/admin`.
You can also use the host provided by ngrok instead of `localhost`.
### Create admin user
When you are accessing the administration panel for the first time, you will be
requested to create an admin user.
Fill in the form and click on the button "LET'S START"
### Configure email authentication
From the administration panel, click on "Settings"
* Under USERS & PERMISSIONS PLUGIN, click on "Advanced Settings"
* Make sure the following options are set to `ON`:
* `One account per email address`
* `Enable sign-ups`
* `Enable email confirmation`
* Set the `Redirection url` field to HonorBox UI sign-in page
* For a development environment the value should be `http://localhost:3000/sign-in`
### Permissions configuration
For now, some initial permissions should be configured manually.
From the administration panel, click on "Settings"
* Under USERS & PERMISSIONS PLUGIN, click on "Roles"
* Click on the "Edit" button (Pencil) for the **Authenticated** role
* Under Permissions/Application/Domain, check `find`, `verify`
* Under Permissions/Application/Publication, check `create`, `delete`, `embed`, `find`,
`findone`, `update`
* Under Permissions/Application/Publisher, check `create`, `find`, `findone`, `update`
* Under Permissions/Application/Twitter, check `find`, `verify`
* Under Permissions/EthereumAuth/EthereumAuth, check `updateuser`
* Under USERS & PERMISSIONS PLUGIN, click on "Roles" again
* Click on the "Edit" button (Pencil) for the **Public** role
* Under Permissions/Application/Profile, check `fetch`
* Under Permissions/EthereumAuth/EthereumAuth, check `signin`, `signinnonce`, `signup`, `signupemailverification`, `signupnonce`
Congratulations! The API server is properly configured, up and running!
{
"routes": [
{
"method": "GET",
"path": "/domains",
"handler": "domain.find",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/domains/count",
"handler": "domain.count",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/domains/:id",
"handler": "domain.findOne",
"config": {
"policies": []
}
},
{
"method": "POST",
"path": "/domains",
"handler": "domain.create",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/domains/:id",
"handler": "domain.update",
"config": {
"policies": []
}
},
{
"method": "DELETE",
"path": "/domains/:id",
"handler": "domain.delete",
"config": {
"policies": []
}
},
{
"method": "POST",
"path": "/domain/verify",
"handler": "domain.verify",
"config": {
"policies": []
}
}
]
}
'use strict';
const { ethers } = require("ethers");
const { sanitizeEntity } = require('strapi-utils');
const dns = require('dns')
const dnsPromises = dns.promises;
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/concepts/controllers.html#core-controllers)
* to customize this controller
*/
module.exports = {
async find(ctx) {
let query = { 'owner.id': ctx.state.user.id };
let domain = await strapi.services.domain.findOne(query)
return sanitizeEntity(domain, { model: strapi.models.domain });
},
async verify(ctx) {
const { user } = ctx.state;
const { eth_addr: ethAddr } = user;
const { domainName } = ctx.request.body;
const message = `Ethereum Signed Message: ${domainName} is linked to ${ethAddr}`
let signature;
let verified = false;
let errorMsg = 'DNS TXT record not found!';
const txtRecords = await dnsPromises.resolve(domainName, 'TXT');
txtRecords.forEach(subRecords => {
subRecords.forEach(async txtRecord => {
if (txtRecord.startsWith('sc-profile-verification')) {
signature = txtRecord.substring(24);
const signerAddr = ethers.utils.verifyMessage(message, signature);
if (ethAddr === signerAddr) {
verified = true;
} else {
errorMsg = 'Invalid signature found on DNS TXT record!';
}
return;
}
});
});
if (!verified) {
return ctx.badRequest(errorMsg);
}
// Issue VC (Verifiable Credential)
const credService = strapi.plugins['sourcecheck'].services.credential;
const credential = await credService.issueDomainVC({
ethAddr: ethAddr,
domainName
});
// Save verification on database
const data = {
owner: user,
domain: domainName,
signature,
credential,
date: new Date()
}
await strapi.services.domain.create(data);
return {
result: 'ok',
domainName: domainName
};
}
};
'use strict';
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/concepts/models.html#lifecycle-hooks)
* to customize this model
*/
module.exports = {};
{
"kind": "collectionType",
"collectionName": "domains",
"info": {
"name": "Domain",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
"attributes": {
"owner": {
"via": "domains",
"plugin": "users-permissions",
"model": "user"
},
"domain": {
"type": "string"
},
"signature": {
"type": "string"
},
"credential": {
"type": "json"
},
"date": {
"type": "datetime"
}
}
}
'use strict';
/**
* Read the documentation (https://strapi.io/documentation/developer-docs/latest/concepts/services.html#core-services)
* to customize this service
*/
module.exports = {};
{
"routes": [
{
"method": "GET",
"path": "/profiles",
"handler": "profile.find",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/profiles/count",
"handler": "profile.count",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/profiles/:id",
"handler": "profile.findOne",
"config": {
"policies": []
}
},
{
"method": "POST",
"path": "/profiles",
"handler": "profile.create",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/profiles/:id",
"handler": "profile.update",
"config": {
"policies": []
}
},
{
"method": "DELETE",
"path": "/profiles/:id",
"handler": "profile.delete",