Skip to content

Commit

Permalink
refactor: cleanup users table, remove users_addresses table
Browse files Browse the repository at this point in the history
  • Loading branch information
mistakia committed Apr 10, 2024
1 parent 953a5f5 commit 93aa64b
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 122 deletions.
9 changes: 3 additions & 6 deletions api/routes/auth/message.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,13 @@ router.post('/?', async (req, res) => {
}

// public_key can be a linked keypair or an existing nano account

const linked_accounts = await db('user_addresses')
.select('user_addresses.address')
.leftJoin('users', 'users.id', '=', 'user_addresses.user_id')
const linked_accounts = await db('accounts_keys')
.select('account')
.where({ public_key })

const nano_account = tools.publicKeyToAddress(public_key)

const all_accounts = [
...linked_accounts.map((row) => row.address),
...linked_accounts.map((row) => row.account),
nano_account
]

Expand Down
52 changes: 12 additions & 40 deletions api/routes/auth/register.mjs
Original file line number Diff line number Diff line change
@@ -1,38 +1,31 @@
import express from 'express'
import nano from 'nanocurrency'
import { tools } from 'nanocurrency-web'
import ed25519 from '@trashman/ed25519-blake2b'

const router = express.Router()
const USERNAME_RE = /^[A-Za-z][a-zA-Z0-9_]+$/

router.post('/?', async (req, res) => {
const { logger, db } = req.app.locals
try {
// public_key is a linked keypair for the given address
const required = ['public_key', 'address', 'signature', 'username']
const required = ['public_key', 'signature', 'username']
for (const prop of required) {
if (!req.body[prop]) {
return res.status(400).send({ error: `missing ${prop} param` })
}
}

const { public_key, signature, username } = req.body
let { address } = req.body

if (!nano.checkKey(public_key)) {
return res.status(401).send({ error: 'invalid public_key param' })
}

if (!nano.checkAddress(address)) {
return res.status(401).send({ error: 'invalid address param' })
}

if (!USERNAME_RE.test(username)) {
return res.status(401).send({ error: 'invalid username param' })
}

const publicKey = tools.addressToPublicKey(address)
const valid_signature = tools.verify(publicKey, signature, public_key)
const valid_signature = ed25519.verify(signature, public_key, public_key)
if (!valid_signature) {
return res.status(401).send({ error: 'invalid signature' })
}
Expand All @@ -44,45 +37,24 @@ router.post('/?', async (req, res) => {
return res.status(401).send({ error: 'username exists' })
}

// TODO verify address exists on ledger

address = address.replace('xrb_', 'nano_')

const exists = await db('user_addresses').where({ address })
let user_id = exists.length ? exists[0].user_id : null

if (!user_id) {
const result = await db('users')
.insert({
public_key,
username,
last_visit: Math.round(Date.now() / 1000)
})
.onConflict()
.merge()
user_id = result[0]
} else {
await db('users')
.update({ last_visit: Math.round(Date.now() / 1000), username })
.where({ id: user_id })
}

await db('user_addresses')
const result = await db('users')
.insert({
user_id,
address,
signature
public_key,
username,
signature,
last_visit: Math.round(Date.now() / 1000)
})
.onConflict()
.merge()

const user_id = result[0]

return res.send({
user_id,
address,
username,
signature
username
})
} catch (error) {
console.log(error)
logger(error)
res.status(500).send('Internal server error')
}
Expand Down
32 changes: 16 additions & 16 deletions db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ CREATE TABLE `accounts` (

-- --------------------------------------------------------

--
-- Table structure for table `accounts_keys`
--

DROP TABLE IF EXISTS `accounts_keys`;

CREATE TABLE `accounts_keys` (
`account` char(65) CHARACTER SET utf8 NOT NULL,
`public_key` varchar(255) DEFAULT NULL,
`signature` varchar(255) DEFAULT NULL,
`created_at` int(11) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci;

-- --------------------------------------------------------

--
-- Table structure for table `accounts_delegators`
--
Expand Down Expand Up @@ -618,29 +633,14 @@ CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`public_key` varchar(64) NOT NULL,
`signature` varchar(255) NOT NULL,
`last_visit` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `public_key` (`public_key`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci;

-- --------------------------------------------------------

--
-- Table structure for table `user_addresses`
--

DROP TABLE IF EXISTS `user_addresses`;

CREATE TABLE `user_addresses` (
`user_id` int(11) NOT NULL,
`address` char(65) NOT NULL,
`signature` varchar(255) NOT NULL,
KEY (`user_id`),
UNIQUE KEY `address` (`address`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci;

-- --------------------------------------------------------

--
-- Table structure for table `voting_weight`
--
Expand Down
100 changes: 40 additions & 60 deletions test/auth.register.test.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* global describe before it */
import chai from 'chai'
import crypto from 'crypto'
import chaiHTTP from 'chai-http'
import { tools, wallet } from 'nanocurrency-web'
import ed25519 from '@trashman/ed25519-blake2b'

import server from '#api/server.mjs'
import knex from '#db'
Expand All @@ -22,34 +21,19 @@ describe('API /auth/register', () => {
.request(server)
.post('/api/auth/register')
.send({
address: 'someaddress',
signature: 'somesignature',
username: 'test_username'
}) // missing public_key
expect(response).to.have.status(400)
expect(response.body.error).to.include('missing public_key param')
})

it('should return 400 if address field is missing', async () => {
const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: 'somepub',
signature: 'somesignature',
username: 'test_username'
}) // missing address
expect(response).to.have.status(400)
expect(response.body.error).to.include('missing address param')
})

it('should return 400 if signature field is missing', async () => {
const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: 'somepub',
address: 'someaddress',
username: 'test_username'
}) // missing signature
expect(response).to.have.status(400)
Expand All @@ -62,47 +46,25 @@ describe('API /auth/register', () => {
.post('/api/auth/register')
.send({
public_key: 'somepub',
address: 'someaddress',
signature: 'somesignature'
}) // missing username
expect(response).to.have.status(400)
expect(response.body.error).to.include('missing username param')
})

it('should return 401 if pub param is invalid', async () => {
const w = wallet.generate()
const account = w.accounts[0]

const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: 'invalidpub',
address: account.address,
signature: 'somesignature',
username: 'test_username'
})
expect(response).to.have.status(401)
expect(response.body.error).to.equal('invalid public_key param')
})

it('should return 401 if address param is invalid', async () => {
const w = wallet.generate()
const account = w.accounts[0]

const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: account.publicKey,
address: 'invalidaddress',
signature: 'somesignature',
username: 'test_username'
})
expect(response).to.have.status(401)
expect(response.body.error).to.equal('invalid address param')
})

const invalid_usernames = [
'contains space',
'constains@character',
Expand All @@ -116,15 +78,17 @@ describe('API /auth/register', () => {

invalid_usernames.forEach((username) => {
it(`should return 401 if username param is invalid: ${username}`, async () => {
const w = wallet.generate()
const account = w.accounts[0]
const private_key = Buffer.from(
'0000000000000000000000000000000000000000000000000000000000000000',
'hex'
)
const public_key = ed25519.publicKey(private_key)

const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: account.publicKey,
address: account.address,
public_key: public_key.toString('hex'),
signature: 'somesignature',
username
})
Expand All @@ -134,46 +98,62 @@ describe('API /auth/register', () => {
})

it('should return 401 if signature is invalid', async () => {
const w = wallet.generate()
const account = w.accounts[0]

const signature = tools.sign(account.privateKey, 'some message')
const private_key = Buffer.from(
'0000000000000000000000000000000000000000000000000000000000000000',
'hex'
)
const public_key = ed25519.publicKey(private_key)
const signature = ed25519.sign(public_key, private_key, public_key)

const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: account.publicKey,
address: account.address,
signature,
public_key: public_key.toString('hex'),
signature: signature.toString('hex'),
username: 'test_username'
})
expect(response).to.have.status(401)
expect(response.body.error).to.equal('invalid signature')
})

it('should return 401 if username already exists', async () => {
const seed = crypto.randomBytes(64).toString('hex')
const accounts = wallet.accounts(seed, 0, 1)
const account_0 = accounts[0]
const account_1 = accounts[1]
const private_key_0 = Buffer.from(
'0000000000000000000000000000000000000000000000000000000000000000',
'hex'
)
const public_key_0 = ed25519.publicKey(private_key_0)
const signature_0 = ed25519.sign(
public_key_0.toString('hex'),
private_key_0,
public_key_0
)

const private_key_1 = Buffer.from(
'0000000000000000000000000000000000000000000000000000000000000001',
'hex'
)
const public_key_1 = ed25519.publicKey(private_key_1)
const signature_1 = ed25519.sign(
public_key_1.toString('hex'),
private_key_1,
public_key_1
)

await knex('users').insert({
id: 1,
username: 'existing_username',
public_key: account_0.publicKey,
public_key: public_key_0.toString('hex'),
signature: signature_0.toString('hex'),
last_visit: Math.floor(Date.now() / 1000)
})

const signature = tools.sign(account_1.privateKey, account_1.publicKey)

const response = await chai
.request(server)
.post('/api/auth/register')
.send({
public_key: account_1.publicKey,
address: account_1.address,
signature,
public_key: public_key_1.toString('hex'),
signature: signature_1.toString('hex'),
username: 'existing_username'
})
expect(response).to.have.status(401)
Expand Down

0 comments on commit 93aa64b

Please sign in to comment.