diff options
-rw-r--r-- | client/components/sidebar/sidebar.jade | 10 | ||||
-rw-r--r-- | client/components/sidebar/sidebar.js | 3 | ||||
-rw-r--r-- | sandstorm.js | 144 |
3 files changed, 153 insertions, 4 deletions
diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index 4f5586cb..51600acf 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -30,10 +30,12 @@ template(name="membersWidget") .board-widget-content each currentBoard.activeMembers +userAvatar(userId=this.userId showStatus=true) - unless isSandstorm - if currentUser.isBoardAdmin - a.member.add-member.js-manage-board-members - i.fa.fa-plus + if isSandstorm + a.member.add-member.sandstorm-powerbox-request-identity + i.fa.fa-plus + else if currentUser.isBoardAdmin + a.member.add-member.js-manage-board-members + i.fa.fa-plus .clearfix if isInvited hr diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js index 0af32f8f..f32a27c5 100644 --- a/client/components/sidebar/sidebar.js +++ b/client/components/sidebar/sidebar.js @@ -163,6 +163,9 @@ Template.membersWidget.helpers({ Template.membersWidget.events({ 'click .js-member': Popup.open('member'), 'click .js-manage-board-members': Popup.open('addMember'), + 'click .sandstorm-powerbox-request-identity'() { + window.sandstormRequestIdentity(); + }, 'click .js-member-invite-accept'() { const boardId = Session.get('currentBoard'); Meteor.user().removeInvite(boardId); diff --git a/sandstorm.js b/sandstorm.js index d1b7b300..40a80dc6 100644 --- a/sandstorm.js +++ b/sandstorm.js @@ -21,6 +21,79 @@ const sandstormBoard = { }; if (isSandstorm && Meteor.isServer) { + const Capnp = require('capnp'); + const Powerbox = Capnp.importSystem('sandstorm/powerbox.capnp'); + const Identity = Capnp.importSystem('sandstorm/identity.capnp'); + const SandstormHttpBridge = + Capnp.importSystem('sandstorm/sandstorm-http-bridge.capnp').SandstormHttpBridge; + + let httpBridge = null; + + function getHttpBridge() { + if (!httpBridge) { + const capnpConnection = Capnp.connect('unix:/tmp/sandstorm-api'); + httpBridge = capnpConnection.restore(null, SandstormHttpBridge); + } + return httpBridge; + } + + Meteor.methods({ + sandstormClaimIdentityRequest(token, descriptor) { + check(token, String); + check(descriptor, String); + + const parsedDescriptor = Capnp.parse( + Powerbox.PowerboxDescriptor, + new Buffer(descriptor, 'base64'), + { packed: true }); + + const tag = Capnp.parse(Identity.Identity.PowerboxTag, parsedDescriptor.tags[0].value); + const permissions = []; + if (tag.permissions[1]) { + permissions.push('configure'); + } + + if (tag.permissions[0]) { + permissions.push('participate'); + } + + const sessionId = this.connection.sandstormSessionId(); + const httpBridge = getHttpBridge(); + const session = httpBridge.getSessionContext(sessionId).context; + const api = httpBridge.getSandstormApi(sessionId).api; + + Meteor.wrapAsync((done) => { + session.claimRequest(token).then((response) => { + const identity = response.cap.castAs(Identity.Identity); + const promises = [api.getIdentityId(identity), identity.getProfile()]; + return Promise.all(promises).then((responses) => { + const identityId = responses[0].id.toString('hex').slice(0, 32); + const profile = responses[1].profile; + return profile.picture.getUrl().then((response) => { + const sandstormInfo = { + id: identityId, + name: profile.displayName.defaultText, + permissions, + picture: `${response.protocol}://${response.hostPath}`, + preferredHandle: profile.preferredHandle, + pronouns: profile.pronouns, + }; + + const login = Accounts.updateOrCreateUserFromExternalService( + 'sandstorm', sandstormInfo, + { profile: { name: sandstormInfo.name, fullname: sandstormInfo.name } }); + + updateUserPermissions(login.userId, permissions); + done(); + }); + }); + }).catch((e) => { + done(e, null); + }); + })(); + }, + }); + function updateUserPermissions(userId, permissions) { const isActive = permissions.indexOf('participate') > -1; const isAdmin = permissions.indexOf('configure') > -1; @@ -137,6 +210,77 @@ if (isSandstorm && Meteor.isServer) { } if (isSandstorm && Meteor.isClient) { + let rpcCounter = 0; + const rpcs = {}; + + window.addEventListener('message', (event) => { + if (event.source === window) { + // Meteor likes to postmessage itself. + return; + } + + if ((event.source !== window.parent) || + typeof event.data !== 'object' || + typeof event.data.rpcId !== 'number') { + throw new Error(`got unexpected postMessage: ${event}`); + } + + const handler = rpcs[event.data.rpcId]; + if (!handler) { + throw new Error(`no such rpc ID for event ${event}`); + } + + delete rpcs[event.data.rpcId]; + handler(event.data); + }); + + function sendRpc(name, message) { + const id = rpcCounter++; + message.rpcId = id; + const obj = {}; + obj[name] = message; + window.parent.postMessage(obj, '*'); + return new Promise((resolve, reject) => { + rpcs[id] = (response) => { + if (response.error) { + reject(new Error(response.error)); + } else { + resolve(response); + } + }; + }); + } + + const powerboxDescriptors = { + identity: 'EAhQAQEAABEBF1EEAQH_GN1RqXqYhMAAQAERAREBAQ', + // Generated using the following code: + // + // Capnp.serializePacked( + // Powerbox.PowerboxDescriptor, + // { tags: [ { + // id: "13872380404802116888", + // value: Capnp.serialize(Identity.PowerboxTag, { permissions: [true, false] }) + // }]}).toString('base64') + // .replace(/\//g, "_") + // .replace(/\+/g, "-"); + }; + + function doRequest(serializedPowerboxDescriptor, onSuccess) { + return sendRpc('powerboxRequest', { + query: [serializedPowerboxDescriptor], + }).then((response) => { + if (!response.canceled) { + onSuccess(response); + } + }); + } + + window.sandstormRequestIdentity = function () { + doRequest(powerboxDescriptors.identity, (response) => { + Meteor.call('sandstormClaimIdentityRequest', response.token, response.descriptor); + }); + }; + // Since the Sandstorm grain is displayed in an iframe of the Sandstorm shell, // we need to explicitly expose meta data like the page title or the URL path // so that they could appear in the browser window. |