mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-08-01 07:21:15 +00:00
accessControl basic framework for party system
This commit is contained in:
parent
9723f3c37a
commit
a67c82aab5
5 changed files with 159 additions and 35 deletions
|
|
@ -10,8 +10,8 @@ content = require './content'
|
||||||
scoring = require './scoring'
|
scoring = require './scoring'
|
||||||
schema = require './schema'
|
schema = require './schema'
|
||||||
helpers = require './helpers'
|
helpers = require './helpers'
|
||||||
helpers.viewHelpers view
|
|
||||||
browser = require './browser'
|
browser = require './browser'
|
||||||
|
helpers.viewHelpers view
|
||||||
_ = require('underscore')
|
_ = require('underscore')
|
||||||
|
|
||||||
setupListReferences = (model) ->
|
setupListReferences = (model) ->
|
||||||
|
|
@ -65,9 +65,9 @@ get '/', (page, model, next) ->
|
||||||
setupModelFns(model)
|
setupModelFns(model)
|
||||||
|
|
||||||
# Subscribe to friends
|
# Subscribe to friends
|
||||||
if !_.isEmpty(obj.party)
|
# if obj.party?.current?
|
||||||
model.subscribe model.query('users').party(obj.party), (err, party) ->
|
# model.subscribe model.query('users').party(obj.party), (err, party) ->
|
||||||
model.ref '_party', party
|
# model.ref '_party', party
|
||||||
|
|
||||||
page.render()
|
page.render()
|
||||||
|
|
||||||
|
|
@ -96,6 +96,8 @@ ready (model) ->
|
||||||
browser.setupTour(model)
|
browser.setupTour(model)
|
||||||
browser.setupGrowlNotifications(model) unless model.get('_view.mobileDevice')
|
browser.setupGrowlNotifications(model) unless model.get('_view.mobileDevice')
|
||||||
|
|
||||||
|
require('./party')(exports, model)
|
||||||
|
|
||||||
require('../server/private').app(exports, model)
|
require('../server/private').app(exports, model)
|
||||||
|
|
||||||
user.on 'set', 'tasks.*.completed', (i, completed, previous, isLocal, passed) ->
|
user.on 'set', 'tasks.*.completed', (i, completed, previous, isLocal, passed) ->
|
||||||
|
|
@ -282,34 +284,16 @@ ready (model) ->
|
||||||
batch.commit()
|
batch.commit()
|
||||||
resetDom(model)
|
resetDom(model)
|
||||||
|
|
||||||
exports.closeKickstarterNofitication = (e, el) ->
|
exports.closeKickstarterNofitication = (e, el) -> user.set('flags.kickstarter', 'hide')
|
||||||
user.set('flags.kickstarter', 'hide')
|
|
||||||
|
|
||||||
exports.setMale = -> user.set('preferences.gender', 'm')
|
exports.setMale = -> user.set('preferences.gender', 'm')
|
||||||
exports.setFemale = -> user.set('preferences.gender', 'f')
|
exports.setFemale = -> user.set('preferences.gender', 'f')
|
||||||
exports.setArmorsetV1 = -> user.set('preferences.armorSet', 'v1')
|
exports.setArmorsetV1 = -> user.set('preferences.armorSet', 'v1')
|
||||||
exports.setArmorsetV2 = -> user.set('preferences.armorSet', 'v2')
|
exports.setArmorsetV2 = -> user.set('preferences.armorSet', 'v2')
|
||||||
|
|
||||||
exports.addParty = ->
|
|
||||||
id = model.get('_newPartyMember').replace(/[\s"]/g, '')
|
|
||||||
debugger
|
|
||||||
return if _.isEmpty(id)
|
|
||||||
if user.get('party').indexOf(id) != -1
|
|
||||||
model.set "_view.addPartyError", "#{id} already in party."
|
|
||||||
return
|
|
||||||
query = model.query('users').party([id])
|
|
||||||
model.fetch query, (err, users) ->
|
|
||||||
partyMember = users.at(0).get()
|
|
||||||
if partyMember?.id?
|
|
||||||
user.push('party', id)
|
|
||||||
$('#add-party-modal').modal('hide')
|
|
||||||
window.location.reload() #TODO break old subscription, setup new subscript, remove this reload
|
|
||||||
model.set '_newPartyMember', ''
|
|
||||||
else
|
|
||||||
model.set "_view.addPartyError", "User with id #{id} not found."
|
|
||||||
|
|
||||||
exports.emulateNextDay = ->
|
exports.emulateNextDay = ->
|
||||||
yesterday = +moment().subtract('days', 1).toDate()
|
yesterday = +moment().subtract('days', 1).toDate()
|
||||||
user.set 'lastCron', yesterday
|
user.set 'lastCron', yesterday
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
55
src/app/party.coffee
Normal file
55
src/app/party.coffee
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
_ = require('underscore')
|
||||||
|
|
||||||
|
module.exports = (appExports, model) ->
|
||||||
|
user = model.at('_user')
|
||||||
|
|
||||||
|
appExports.partyCreate = ->
|
||||||
|
newParty = model.get("_newParty")
|
||||||
|
id = model.add 'parties', { name: newParty, leader: user.get('id'), members: [], invites: [] }
|
||||||
|
user.set 'party', {current: id, invitation: null, leader: true}
|
||||||
|
|
||||||
|
appExports.partyInvite = ->
|
||||||
|
id = model.get('_newPartyMember').replace(/[\s"]/g, '')
|
||||||
|
return if _.isEmpty(id)
|
||||||
|
|
||||||
|
obj = user.get()
|
||||||
|
query = model.query('users').party([id])
|
||||||
|
model.fetch query, (err, users) ->
|
||||||
|
partyMember = users.at(0).get()
|
||||||
|
if !partyMember?.id?
|
||||||
|
model.set "_view.partyError", "User with id #{id} not found."
|
||||||
|
return
|
||||||
|
else if partyMember.party.current?.id? or partyMember.party.invitation?
|
||||||
|
model.set "_view.partyError", "User already in a party or pending invitation."
|
||||||
|
return
|
||||||
|
else
|
||||||
|
model.push "parties.#{obj.party.current}.invites", id
|
||||||
|
model.set "users.#{id}.party.invitation", obj.party.current
|
||||||
|
$.bootstrapGrowl "Invitation Sent."
|
||||||
|
$('#party-modal').modal('hide')
|
||||||
|
#model.set '_newPartyMember', ''
|
||||||
|
#window.location.reload() #TODO break old subscription, setup new subscript, remove this reload
|
||||||
|
|
||||||
|
appExports.partyAccept = ->
|
||||||
|
invitation = user.get('party.invitation')
|
||||||
|
user.set 'party.current', invitation
|
||||||
|
user.set 'party.invitation', null
|
||||||
|
model.push "parties.#{invitation}.members", user.get('id')
|
||||||
|
window.location.reload()
|
||||||
|
|
||||||
|
appExports.partyReject = ->
|
||||||
|
user.set 'party.invitation', null
|
||||||
|
# TODO notify sender
|
||||||
|
|
||||||
|
appExports.partyLeave = ->
|
||||||
|
id = user.get('party.current')
|
||||||
|
user.set 'party.current', null
|
||||||
|
members = model.get ("parties.#{id}.members")
|
||||||
|
index = members.indexOf(user.get('id'))
|
||||||
|
model.set "parties.#{id}.members", members.slice(index)
|
||||||
|
window.location.reload()
|
||||||
|
|
||||||
|
appExports.partyDisband = ->
|
||||||
|
|
||||||
|
|
||||||
|
user.on 'set', 'parties.invitation', (after, before) ->
|
||||||
|
|
@ -8,6 +8,7 @@ serverError = require './serverError'
|
||||||
MongoStore = require('connect-mongo')(express)
|
MongoStore = require('connect-mongo')(express)
|
||||||
auth = require 'derby-auth'
|
auth = require 'derby-auth'
|
||||||
priv = require './private'
|
priv = require './private'
|
||||||
|
habitrpgStore = require('./store')
|
||||||
|
|
||||||
## Run server cron ##
|
## Run server cron ##
|
||||||
require('./cron').deleteStaleAccounts()
|
require('./cron').deleteStaleAccounts()
|
||||||
|
|
@ -31,7 +32,6 @@ derby.use(require 'racer-db-mongo')
|
||||||
store = derby.createStore
|
store = derby.createStore
|
||||||
db: {type: 'Mongo', uri: process.env.NODE_DB_URI, safe:true}
|
db: {type: 'Mongo', uri: process.env.NODE_DB_URI, safe:true}
|
||||||
listen: server
|
listen: server
|
||||||
require('./store')(store) #setup custom accessControl
|
|
||||||
|
|
||||||
ONE_YEAR = 1000 * 60 * 60 * 24 * 365
|
ONE_YEAR = 1000 * 60 * 60 * 24 * 365
|
||||||
root = path.dirname path.dirname __dirname
|
root = path.dirname path.dirname __dirname
|
||||||
|
|
@ -48,6 +48,7 @@ options =
|
||||||
domain: process.env.BASE_URL || 'http://localhost:3000'
|
domain: process.env.BASE_URL || 'http://localhost:3000'
|
||||||
allowPurl: true
|
allowPurl: true
|
||||||
schema: require('../app/schema').newUserObject()
|
schema: require('../app/schema').newUserObject()
|
||||||
|
customAccessControl: habitrpgStore.customAccessControl
|
||||||
|
|
||||||
mongo_store = new MongoStore {url: process.env.NODE_DB_URI}, ->
|
mongo_store = new MongoStore {url: process.env.NODE_DB_URI}, ->
|
||||||
expressApp
|
expressApp
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,41 @@
|
||||||
Setup read / write access
|
Setup read / write access
|
||||||
@param store
|
@param store
|
||||||
###
|
###
|
||||||
module.exports = (store) ->
|
|
||||||
|
module.exports.customAccessControl = (store) ->
|
||||||
|
|
||||||
|
# store.readPathAccess "users.*", () -> # captures, next
|
||||||
|
# next = arguments[arguments.length-1]
|
||||||
|
# return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37
|
||||||
|
# return next(true)
|
||||||
|
|
||||||
|
store.readPathAccess "users.*", -> # captures, next) ->
|
||||||
|
return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37
|
||||||
|
console.log arguments
|
||||||
|
captures = arguments[0]
|
||||||
|
next = arguments[arguments.length - 1]
|
||||||
|
sameSession = captures is @session.userId
|
||||||
|
isServer = false #!this.req.socket; //TODO how to determine if request came from server, as in REST?
|
||||||
|
next sameSession or isServer
|
||||||
|
|
||||||
|
store.writeAccess "*", "users.*", -> # captures, value, next) ->
|
||||||
|
return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37
|
||||||
|
[captures, next] = [arguments[0].split('.'), arguments[arguments.length-1]]
|
||||||
|
uid = captures.shift()
|
||||||
|
attrPath = captures.join('.') # new array shifted left, after shift() was run
|
||||||
|
|
||||||
|
# TODO the server can write to anything - aka, REST
|
||||||
|
#return next(true) if !this.req.socket;
|
||||||
|
|
||||||
|
# public access to users.*.party.invitation (TODO, lock down a bit more)
|
||||||
|
console.log attrPath
|
||||||
|
return next(true) if (attrPath == 'party.invitation')
|
||||||
|
|
||||||
|
# Same session (user.id = this.session.userId)
|
||||||
|
return next(true) if uid is @session.userId
|
||||||
|
|
||||||
|
next(false)
|
||||||
|
|
||||||
|
|
||||||
# store.writeAccess "*", "users.*.balance", (id, newBalance, next) ->
|
# store.writeAccess "*", "users.*.balance", (id, newBalance, next) ->
|
||||||
# return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37
|
# return unless @session and @session.userId # https://github.com/codeparty/racer/issues/37
|
||||||
|
|
@ -34,7 +68,19 @@ module.exports = (store) ->
|
||||||
###
|
###
|
||||||
store.query.expose "users", "party", (ids) ->
|
store.query.expose "users", "party", (ids) ->
|
||||||
@where("id").within(ids)
|
@where("id").within(ids)
|
||||||
.only('stats', 'preferences.gender', 'preferences.armorSet', 'items', 'auth.local.username', 'auth.facebook.displayName')
|
.only('stats',
|
||||||
|
'items',
|
||||||
|
'party',
|
||||||
|
'preferences.gender',
|
||||||
|
'preferences.armorSet',
|
||||||
|
'auth.local.username',
|
||||||
|
'auth.facebook.displayName')
|
||||||
|
|
||||||
store.queryAccess "users", "party", (ids, next) ->
|
store.queryAccess "users", "party", (ids, next) ->
|
||||||
next(true) # no harm in public user stats
|
next(true) # no harm in public user stats
|
||||||
|
|
||||||
|
store.readPathAccess "parties.*", ->
|
||||||
|
arguments[arguments.length-1](true)
|
||||||
|
|
||||||
|
store.writeAccess "*", "parties.*", ->
|
||||||
|
arguments[arguments.length-1](true)
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@
|
||||||
</label>
|
</label>
|
||||||
{/}
|
{/}
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
<a class='btn btn-danger' data-target="#reset-modal" data-toggle="modal">Reset</a>
|
||||||
|
|
||||||
<@footer>
|
<@footer>
|
||||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||||
</@footer>
|
</@footer>
|
||||||
|
|
@ -108,14 +111,47 @@
|
||||||
</@footer>
|
</@footer>
|
||||||
</app:myModal>
|
</app:myModal>
|
||||||
|
|
||||||
<app:myModal modalId="add-party-modal" header="Add Party Member">
|
<app:myModal modalId="party-modal" header="Party">
|
||||||
<form x-bind="submit: addParty">
|
<h2>
|
||||||
{#if _view.addPartyError}
|
{#if _user.party.current}Invite Member To "{_user.party.current}"
|
||||||
<div class='alert alert-danger'>{_view.addPartyError}</div>
|
{else if _user.party.invitation}You're Invited To A Party
|
||||||
|
{else}Create A Party{/}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{#if _user.party.current}
|
||||||
|
<!-- In a party -->
|
||||||
|
{#each _party as :member}
|
||||||
|
{:member.id}
|
||||||
|
{/}
|
||||||
|
<form x-bind="submit: partyInvite">
|
||||||
|
{#if _view.partyError}
|
||||||
|
<div class='alert alert-danger'>{_view.partyError}</div>
|
||||||
{/}
|
{/}
|
||||||
<input type="text" class="input-medium search-query" value="{_newPartyMember}">
|
<input type="text" class="input-medium search-query" value="{_newPartyMember}">
|
||||||
<input type="submit" class="btn" value="Add" />
|
<input type="submit" class="btn" value="Add" />
|
||||||
</form>
|
</form>
|
||||||
|
<a class='btn btn-danger' x-bind="click: partyLeave">Leave</a>
|
||||||
|
|
||||||
|
{else}
|
||||||
|
|
||||||
|
<!-- Not in a party , pending invitation -->
|
||||||
|
{#if _user.party.invitation}
|
||||||
|
<!-- TODO show by whom -->
|
||||||
|
<a class='btn btn-success' x-bind="click: partyAccept">Accept</a>
|
||||||
|
<a class='btn btn-danger' x-bind="click: partyReject">Reject</a>
|
||||||
|
{else}
|
||||||
|
<!-- Not in a party , no invites - create a new one -->
|
||||||
|
<form x-bind="submit: partyCreate">
|
||||||
|
{#if _view.partyError}
|
||||||
|
<div class='alert alert-danger'>{_view.partyError}</div>
|
||||||
|
{/}
|
||||||
|
<input type="text" class="input-medium search-query" value="{_newParty}">
|
||||||
|
<input type="submit" class="btn" value="Add" />
|
||||||
|
</form>
|
||||||
|
{/}
|
||||||
|
{/}
|
||||||
|
|
||||||
|
|
||||||
</app:myModal>
|
</app:myModal>
|
||||||
|
|
||||||
<alerts:>
|
<alerts:>
|
||||||
|
|
@ -142,13 +178,16 @@
|
||||||
<a href="#" class="btn btn-small btn-info" data-target="#login-modal" data-toggle="modal">Login / Register</a>
|
<a href="#" class="btn btn-small btn-info" data-target="#login-modal" data-toggle="modal">Login / Register</a>
|
||||||
{else}
|
{else}
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-small">{username(_user.auth)}</button>
|
<button class="btn btn-small">
|
||||||
|
{#if _user.party.invitation}<span class="badge badge-success">1</span>{/}
|
||||||
|
{username(_user.auth)}
|
||||||
|
</button>
|
||||||
<button class="btn btn-small dropdown-toggle" data-toggle="dropdown">
|
<button class="btn btn-small dropdown-toggle" data-toggle="dropdown">
|
||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="#" data-target="#settings-modal" data-toggle="modal">Settings</a></li>
|
<li><a href="#" data-target="#settings-modal" data-toggle="modal">Settings</a></li>
|
||||||
<li><a href="#" data-target="#reset-modal" data-toggle="modal">Reset</a></li>
|
<li><a href="#" data-target="#party-modal" data-toggle="modal">Party{#if _user.party.invitation}<span class="badge badge-success">1</span>{/}</a></li>
|
||||||
<li><a href='/logout'>Logout</a></li>
|
<li><a href='/logout'>Logout</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -200,7 +239,6 @@
|
||||||
<div id="lvl"><span class="badge badge-info">Lvl {:member.stats.lvl}</span></div>
|
<div id="lvl"><span class="badge badge-info">Lvl {:member.stats.lvl}</span></div>
|
||||||
</td>
|
</td>
|
||||||
{/}
|
{/}
|
||||||
<td><a class="btn" id="add-party-button" data-target="#add-party-modal" data-toggle="modal"><i class="icon-user"></i></a></td>
|
|
||||||
{/}
|
{/}
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue