Skip to content
This repository was archived by the owner on Apr 20, 2023. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 24 additions & 24 deletions api/src/db/migrations/20220114134044_initial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ export async function up(knex: Knex): Promise<void> {

await knex.raw(ON_UPDATE_TIMESTAMP_FUNCTION);

const usersTableName = "users";
await knex.schema.createTable(usersTableName, (builder) => {
const imbuerTableName = "imbuer";
await knex.schema.createTable(imbuerTableName, (builder) => {
/**
* We need to be able to capture users who are just casually creating
* a Project without any web3 functionality yet. So we lazily require
Expand All @@ -17,35 +17,35 @@ export async function up(knex: Knex): Promise<void> {
builder.text("display_name");

auditFields(knex, builder);
}).then(onUpdateTrigger(knex, usersTableName));
}).then(onUpdateTrigger(knex, imbuerTableName));

/**
* Without at least one of these, a usr can't really do much beyond saving
* a draft proposal.
*/
const web3AccountsTableName = "web3_accounts";
const web3AccountsTableName = "web3_account";
await knex.schema.createTable(web3AccountsTableName, (builder) => {
builder.text("address");
builder.integer("user_id").notNullable();
builder.integer("imbuer_id").notNullable();

builder.text("type");
builder.text("challenge");

builder.primary(["address"]);
builder.foreign("user_id").references("users.id");
builder.foreign("imbuer_id").references("imbuer.id");

auditFields(knex, builder);
}).then(onUpdateTrigger(knex, web3AccountsTableName));

const federatedCredsTableName = "federated_credentials";
const federatedCredsTableName = "federated_credential";
await knex.schema.createTable(federatedCredsTableName, (builder) => {
builder.integer("id");
builder.text("issuer");
builder.text("subject");
builder.primary(["issuer", "subject"]);

builder.foreign("id")
.references("users.id")
.references("imbuer.id")
.onDelete("CASCADE")
.onUpdate("CASCADE");

Expand Down Expand Up @@ -75,8 +75,8 @@ export async function up(knex: Knex): Promise<void> {
* create_block_number: BlockNumber,
* }
*/
const projectsTableName = "projects";
await knex.schema.createTable(projectsTableName, (builder: Knex.CreateTableBuilder) => {
const projectTableName = "project";
await knex.schema.createTable(projectTableName, (builder: Knex.CreateTableBuilder) => {
builder.increments("id", { primaryKey: true });
builder.text("name"); // project name
builder.text("logo"); // URL or dataURL (i.e., base64 encoded)
Expand Down Expand Up @@ -123,12 +123,12 @@ export async function up(knex: Knex): Promise<void> {
* create an account and associate it with a web3 address, we can update
* all of the projects whose "owner" is a `web3_account` associated with
* the `usr` account. Likewise, when a user decides to delete their
* account, we don't CASCADE in that case -- only nullify the `user_id`
* account, we don't CASCADE in that case -- only nullify the `imbuer_id`
* here, as it wouldn't point to anything useful.
*/
builder.integer("user_id");
builder.foreign("user_id")
.references("users.id")
builder.integer("imbuer_id");
builder.foreign("imbuer_id")
.references("imbuer.id")
.onUpdate("CASCADE")
.onDelete("SET NULL");

Expand All @@ -150,7 +150,7 @@ export async function up(knex: Knex): Promise<void> {
builder.bigInteger("create_block_number").nullable(); //.unsigned();

auditFields(knex, builder);
}).then(onUpdateTrigger(knex, projectsTableName));
}).then(onUpdateTrigger(knex, projectTableName));

/**
* pub struct Milestone {
Expand All @@ -161,13 +161,13 @@ export async function up(knex: Knex): Promise<void> {
* is_approved: bool
* }
*/
const milestonesTableName = "milestones";
await knex.schema.createTable(milestonesTableName, (builder) => {
const milestoneTableName = "milestone";
await knex.schema.createTable(milestoneTableName, (builder) => {
builder.integer("milestone_index");
builder.integer("project_id").notNullable();
builder.primary(["project_id","milestone_index"]);
builder.foreign("project_id")
.references("projects.id")
.references("project.id")
.onDelete("CASCADE")
.onUpdate("CASCADE");

Expand All @@ -176,7 +176,7 @@ export async function up(knex: Knex): Promise<void> {
builder.boolean("is_approved").defaultTo(false);

auditFields(knex, builder);
}).then(onUpdateTrigger(knex, milestonesTableName));
}).then(onUpdateTrigger(knex, milestoneTableName));

/**
* TODO: ? votes and contributions
Expand Down Expand Up @@ -230,11 +230,11 @@ export async function up(knex: Knex): Promise<void> {


export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists("milestones");
await knex.schema.dropTableIfExists("projects");
await knex.schema.dropTableIfExists("milestone");
await knex.schema.dropTableIfExists("project");
await knex.schema.dropTableIfExists("project_status");
await knex.schema.dropTableIfExists("federated_credentials");
await knex.schema.dropTableIfExists("web3_accounts");
await knex.schema.dropTableIfExists("users");
await knex.schema.dropTableIfExists("federated_credential");
await knex.schema.dropTableIfExists("web3_account");
await knex.schema.dropTableIfExists("imbuer");
await knex.raw(DROP_ON_UPDATE_TIMESTAMP_FUNCTION);
}
18 changes: 11 additions & 7 deletions api/src/middleware/authentication/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,36 @@ import express from "express";
import passport from "passport";
import { googleOIDCStrategy, googleOIDCRouter } from "./strategies/google-oidc";
import { polkadotJsAuthRouter, polkadotJsStrategy } from "./strategies/web3/polkadot-js";
import type { User, Web3Account } from "../../models";
import type { Imbuer, Web3Account } from "../../models";
import db from "../../db";


passport.use(googleOIDCStrategy);
passport.use(polkadotJsStrategy);

/**
* The `user` term here is specific to the passport workflow, but in this
* system we refer to "users" as `imbuer` instead.
*/
passport.serializeUser((user, done) => {
if (!(user as any).id) {
return done(
new Error("Failed to serialize User: no `id` found.")
);
}
return done(null, (user as User).id);
return done(null, (user as Imbuer).id);
});

passport.deserializeUser(async (id: string, done) => {
try {
const user = await db.select().from<User>("users").where({"id": Number(id)}).first();
if (!user) {
const imbuer = await db.select().from<Imbuer>("imbuer").where({"id": Number(id)}).first();
if (!imbuer) {
done(new Error(`No user found with id: ${id}`));
} else {
user.web3Accounts = await db<Web3Account>("web3_accounts").select().where({
user_id: user.id
imbuer.web3Accounts = await db<Web3Account>("web3_account").select().where({
imbuer_id: imbuer.id
});
return done(null, user);
return done(null, imbuer);
}
} catch (e) {
return done(
Expand Down
4 changes: 2 additions & 2 deletions api/src/middleware/authentication/strategies/google-oidc.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import express from "express";
import type { Session } from "express-session";
import passport from "passport";
import { getOrCreateFederatedUser } from "../../../models";
import { getOrCreateFederatedImbuer } from "../../../models";
import config from "../../../config";

// No @types yet :(
Expand Down Expand Up @@ -48,7 +48,7 @@ export const googleOIDCStrategy = new GoogleOIDCStrategy(
// state: true,
},
(issuer: string, profile: Record<string,any>, done: CallableFunction) => {
return getOrCreateFederatedUser(issuer, profile.id, profile.displayName, done);
return getOrCreateFederatedImbuer(issuer, profile.id, profile.displayName, done);
}
);

Expand Down
30 changes: 15 additions & 15 deletions api/src/middleware/authentication/strategies/web3/polkadot-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { decodeAddress, encodeAddress } from "@polkadot/keyring";
import { hexToU8a, isHex } from '@polkadot/util';

import {
fetchUser,
fetchImbuer,
fetchWeb3Account,
upsertWeb3Challenge,
User,
getOrCreateFederatedUser
Imbuer,
getOrCreateFederatedImbuer
} from "../../../../models";
import db from "../../../../db";

Expand Down Expand Up @@ -44,20 +44,20 @@ export class Web3Strategy extends passport.Strategy {
if (!web3Account) {
this.fail();
} else {
const user = await fetchUser(web3Account.user_id)(tx);
if (user?.id) {
const imbuer = await fetchImbuer(web3Account.imbuer_id)(tx);
if (imbuer?.id) {
if (
signatureVerify(
web3Account.challenge,
solution.signature,
solution.address
).isValid
) {
this.success(user);
this.success(imbuer);
} else {
const challenge = uuid();
const [web3Account, _] = await upsertWeb3Challenge(
user,
imbuer,
req.body.address,
req.body.type,
challenge
Expand Down Expand Up @@ -128,34 +128,34 @@ polkadotJsAuthRouter.post("/", (req, res, next) => {
next(err);
}

// If no address can be found, create a `users` and then a
// If no address can be found, create an `imbuer` and then a
// `federated_credential`
getOrCreateFederatedUser(
getOrCreateFederatedImbuer(
req.body.meta.source,
address,
req.body.meta.name,
async (err: Error, user: User) => {
async (err: Error, imbuer: Imbuer) => {
if (err) {
next(err);
}

if (!user) {
next(new Error("No user provided."));
if (!imbuer) {
next(new Error("No imbuer provided."));
}

try {
// create a `challenge` uuid and insert it into the users
// create a `challenge` uuid and insert it into the `imbuer`
// table respond with the challenge
db.transaction(async tx => {
const challenge = uuid();
const [web3Account, isInsert] = await upsertWeb3Challenge(
user, address, req.body.type, challenge
imbuer, address, req.body.type, challenge
)(tx);

if (isInsert) {
res.status(201);
}
res.send({user, web3Account});
res.send({imbuer, web3Account});
});
} catch (e) {
next(new Error(
Expand Down
Loading