From cd0dfb6867a546e10f8a0087c367a871f5319124 Mon Sep 17 00:00:00 2001 From: Hank Stoever Date: Tue, 23 Jul 2019 20:53:56 -0700 Subject: [PATCH 1/4] include gaiaURL, username if validateUsername --- src/model.ts | 15 +++++++++++++++ test/helpers.js | 11 +++++++++++ test/model.test.js | 13 ++++++++++++- test/setup.js | 4 ++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/model.ts b/src/model.ts index 718fd6c..0ac42a7 100644 --- a/src/model.ts +++ b/src/model.ts @@ -30,6 +30,7 @@ export default class Model { public static defaults: any = {}; public static className?: string; public static emitter?: EventEmitter; + public static validateUsername = false; schema: Schema; _id: string; attrs: Attrs; @@ -127,6 +128,7 @@ export default class Model { const now = new Date().getTime(); this.attrs.createdAt = this.attrs.createdAt || now; this.attrs.updatedAt = now; + await this.setUsername(); await this.sign(); const encrypted = await this.encrypted(); const gaiaURL = await this.saveFile(encrypted); @@ -163,6 +165,19 @@ export default class Model { return path; } + setUsername() { + const { validateUsername } = this.constructor as typeof Model; + if (validateUsername) { + const userSession = requireUserSession(); + const { username, gaiaHubConfig } = userSession.loadUserData(); + this.attrs.username = username; + // eslint-disable-next-line @typescript-eslint/camelcase + const { url_prefix, address } = gaiaHubConfig; + // eslint-disable-next-line @typescript-eslint/camelcase + this.attrs.gaiaURL = `${url_prefix}${address}/${this.blockstackPath()}`; + } + } + async fetch({ decrypt = true } = {}): Promise { const query = { _id: this._id, diff --git a/test/helpers.js b/test/helpers.js index 920ec80..20ebf8c 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -30,3 +30,14 @@ export const fakeModel = () => { }); return model; }; + +export class ModelWithUsername extends Model { + static validateUsername = true; + + static schema = { + message: { + type: String, + decrypted: true, + }, + } +} diff --git a/test/model.test.js b/test/model.test.js index 45c9e75..9bf7b98 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -1,9 +1,10 @@ import './mocks/crypto'; import './setup'; import { verifyECDSA } from 'blockstack/lib/encryption'; -import { fakeModel, TestModel } from './helpers'; +import { fakeModel, TestModel, ModelWithUsername } from './helpers'; import User from '../src/models/user'; import SigningKey from '../src/models/signing-key'; +import { getConfig } from '../src/config'; test('encrypts data', async (t) => { // crypto.getRandomValues(16); @@ -90,3 +91,13 @@ test('it return null if model not found', async () => { const modelFindOne = await TestModel.findOne({ _id: 'notfound' }); expect(modelFindOne).toBe(undefined); }); + +test.only('it includes username if validateUsername', async () => { + const user = await User.createWithCurrentUser(); + const model = new ModelWithUsername({ message: 'hello' }); + await model.save(); + expect(model.attrs.username).toEqual(user.attrs.username); + expect(model.attrs.gaiaURL).not.toBeFalsy(); + const gaiaURL = `https://gaia.blockstack.org/hub/1Me2Zi84EioQJcwDg5Kjgy5YaXgqXjxJYS/ModelWithUsername/${model._id}`; + expect(model.attrs.gaiaURL).toEqual(gaiaURL); +}); diff --git a/test/setup.js b/test/setup.js index 7516b5c..98eef2a 100644 --- a/test/setup.js +++ b/test/setup.js @@ -96,6 +96,10 @@ beforeEach(async () => { userData: { appPrivateKey, username: faker.name.findName(), + gaiaHubConfig: { + url_prefix: 'https://gaia.blockstack.org/hub/', // eslint-disable-line @typescript-eslint/camelcase + address: '1Me2Zi84EioQJcwDg5Kjgy5YaXgqXjxJYS', + }, profile: { // TODO }, From d96f46f9c0a874403f85fb6959f065b43eb6e790 Mon Sep 17 00:00:00 2001 From: Hank Stoever Date: Tue, 23 Jul 2019 20:59:08 -0700 Subject: [PATCH 2/4] adds some docs --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index c348d3b..d4bf7d0 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,11 @@ A client-side framework for building model-driven decentralized applications on - [Saving a model](#saving-a-model) - [Deleting a model](#deleting-a-model) - [Querying models](#querying-models) + - [Counting models](#counting-models) - [Fetching models created by the current user](#fetching-models-created-by-the-current-user) - [Managing relational data](#managing-relational-data) - [Extending the user model](#extending-the-user-model) + - [Validating usernames](#validating-usernames) - [Collaboration](#collaboration) - [UserGroup Model](#usergroup-model) - [General Workflow](#general-workflow) @@ -412,6 +414,21 @@ class MyAppUserModel extends User { } ~~~ +### Validating usernames + +There are many situations where it is important to validate usernames that are associated with models in your app. For example, imagine a social network, where each post is tied to a specific Blockstack ID. You need to have some validation to ensure that the user who created that model is the same user who owns that Blockstack ID. Radiks can do this for you, as long as you add a special flag to your model, called `validateUsername`. + +~~~javascript +class ModelWithUsername extends Model { + static validateUsername = true + static schema = { ... } +} +~~~ + +If you include this special `validateUsername` flag, then Radiks will pass on the necessar information to Radiks-server. Radiks-server will then perform some validation to ensure that model was indeed created by the username that they claim to have. + +One important thing to note here is that this configuration requires you to use the `publish-data` scope when logging in with Blockstack. + ## Collaboration A key feature of Radiks is support for private collaboration between multiple users. Supporting collaboration with client-side encryption and user-owned storage can be complicated, but the patterns to implement it are generally the same for different apps. Radiks provides out-of-the box for collaboration, making it easy to build private, collaborative apps. From 75ffd0c3ccef2d214ba9ba4701b795c9cbf41145 Mon Sep 17 00:00:00 2001 From: Hank Stoever Date: Tue, 23 Jul 2019 21:08:20 -0700 Subject: [PATCH 3/4] publish beta version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3ef6256..ea63e6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "radiks", - "version": "0.2.1", + "version": "0.3.0-beta.1", "description": "Manage models with encryption and decentralized storage", "main": "lib/index.js", "author": "Hank Stoever", From daaf9525350375160d0376bb34ccc56894cbcc0d Mon Sep 17 00:00:00 2001 From: Hank Stoever Date: Tue, 23 Jul 2019 21:11:05 -0700 Subject: [PATCH 4/4] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 360c3ab..0bedec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Radiks-js Changelog +## 0.3.0-beta.1 - July 23, 2019 + +- Includes a static `validateUsername` property to model. If `true`, then the username is included in the model attributes. See [#44](https://github.com/blockstack-radiks/radiks/pull/44) + ## 0.2.1 - June 9th, 2019 Thanks to @pradel for contributing most of the work in this release!