From ab2db30bc422bacf2b94f083a6f4d674efab35ab Mon Sep 17 00:00:00 2001 From: Tom Russell Date: Sat, 20 Oct 2018 12:20:10 +0100 Subject: [PATCH] Allow requesting user API key --- app/src/frontend/app.js | 12 +++++++- app/src/frontend/my-account.js | 55 ++++++++++++++++++++++++++++++---- app/src/server.js | 16 +++++++++- app/src/user.js | 23 ++++++++++++-- migrations/001.core.up.sql | 4 ++- 5 files changed, 99 insertions(+), 11 deletions(-) diff --git a/app/src/frontend/app.js b/app/src/frontend/app.js index 72c6f35d..2c23248b 100644 --- a/app/src/frontend/app.js +++ b/app/src/frontend/app.js @@ -27,6 +27,7 @@ class App extends React.Component { building: props.building, }; this.login = this.login.bind(this); + this.updateUser = this.updateUser.bind(this); this.logout = this.logout.bind(this); this.selectBuilding = this.selectBuilding.bind(this); } @@ -39,6 +40,11 @@ class App extends React.Component { this.setState({user: user}); } + updateUser(user){ + console.log(user); + this.setState({user: { ...this.state.user, ...user }}); + } + logout() { this.setState({user: undefined}); } @@ -94,7 +100,11 @@ class App extends React.Component { - + diff --git a/app/src/frontend/my-account.js b/app/src/frontend/my-account.js index 9d588b29..ec7faed2 100644 --- a/app/src/frontend/my-account.js +++ b/app/src/frontend/my-account.js @@ -9,10 +9,11 @@ class MyAccountPage extends Component { this.state = { error: undefined }; - this.handleSubmit = this.handleSubmit.bind(this); + this.handleLogout = this.handleLogout.bind(this); + this.handleGenerateKey = this.handleGenerateKey.bind(this); } - handleSubmit(event) { + handleLogout(event) { event.preventDefault(); this.setState({error: undefined}); @@ -32,24 +33,66 @@ class MyAccountPage extends Component { ); } + handleGenerateKey(event) { + event.preventDefault(); + this.setState({error: undefined}); + + fetch('/api/key', { + method: 'POST', + credentials: 'same-origin' + }).then( + res => res.json() + ).then(function(res){ + if (res.error) { + this.setState({error: res.error}) + } else { + this.props.updateUser(res); + } + }.bind(this)).catch( + (err) => this.setState({error: err}) + ); + } + render() { if (this.props.user && !this.props.user.error) { return (

Welcome, {this.props.user.username}!

-

+

Colouring London is under active development, please report any bugs on GitHub.

- Start colouring -
- + +
+ Start colouring + +
+ +
+ +

My Details

+

Username

+

{this.props.user.username}

+

Email Address

+

{this.props.user.email? this.props.user.email : '-'}

+

Registered

+

{this.props.user.registered.toString()}

+ +
+ +

Experimental features

+

API key

+

{this.props.user.api_key? this.props.user.api_key : '-'}

+
+ +
+
); diff --git a/app/src/server.js b/app/src/server.js index e42e8c50..db018ff3 100644 --- a/app/src/server.js +++ b/app/src/server.js @@ -16,7 +16,7 @@ import pgConnect from 'connect-pg-simple'; import App from './frontend/app'; import db from './db'; -import { authUser, createUser, getUserById } from './user'; +import { authUser, createUser, getUserById, getNewUserAPIKey } from './user'; import { queryBuildingsAtPoint, queryBuildingsByReference, getBuildingById, saveBuilding } from './building'; import tileserver from './tileserver'; @@ -281,4 +281,18 @@ server.get('/users/me', function(req, res){ }); }); +// POST generate API key +server.post('/api/key', function(req, res){ + if (!req.session.user_id) { + res.send({error: 'Must be logged in'}); + return + } + + getNewUserAPIKey(req.session.user_id).then(function(api_key){ + res.send(api_key); + }).catch(function(error){ + res.send(error); + }); +}) + export default server; diff --git a/app/src/user.js b/app/src/user.js index 79a35436..b20e6ef4 100644 --- a/app/src/user.js +++ b/app/src/user.js @@ -73,7 +73,7 @@ function authUser(username, password) { function getUserById(user_id) { return db.one( `SELECT - username, email, registered + username, email, registered, api_key FROM users WHERE user_id = $1 @@ -86,4 +86,23 @@ function getUserById(user_id) { }); } -export { getUserById, createUser, authUser } +function getNewUserAPIKey(user_id) { + return db.one( + `UPDATE + users + SET + api_key = gen_random_uuid() + WHERE + user_id = $1 + RETURNING + api_key + `, [ + user_id + ] + ).catch(function(error){ + console.error('Error:', error) + return {error: 'Failed to generate new API key.'}; + }); +} + +export { getUserById, createUser, authUser, getNewUserAPIKey } diff --git a/migrations/001.core.up.sql b/migrations/001.core.up.sql index 19bf0e6d..519a9851 100644 --- a/migrations/001.core.up.sql +++ b/migrations/001.core.up.sql @@ -89,7 +89,9 @@ CREATE TABLE IF NOT EXISTS users ( -- user category (optional, self-selected) category integer REFERENCES user_categories NOT NULL DEFAULT 1, -- user access level (essential, default untrusted) - access_level integer REFERENCES user_access_levels NOT NULL DEFAULT 1 + access_level integer REFERENCES user_access_levels NOT NULL DEFAULT 1, + -- user API key - to give limited query/edit access + api_key uuid UNIQUE default NULL ); ALTER TABLE users ADD CONSTRAINT users_username_len CHECK (length(username) < 30); ALTER TABLE users ADD CONSTRAINT users_email_len CHECK (length(email) < 50);