• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nightscout / cgm-remote-monitor / 5642

pending completion
5642

push

travis-ci

web-flow
Merge pull request #1868 from nightscout/wip/tokens

Token/Role based access controls

616 of 616 new or added lines in 25 files covered. (100.0%)

6938 of 9183 relevant lines covered (75.55%)

9398.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.36
/lib/authorization/storage.js
1
'use strict';
2

3
var _ = require('lodash');
3✔
4
var crypto = require('crypto');
3✔
5
var shiroTrie = require('shiro-trie');
3✔
6
var ObjectID = require('mongodb').ObjectID;
3✔
7

8
var find_options = require('../query');
3✔
9

10
function init (env, ctx) {
3✔
11
  var storage = { };
45✔
12

13
  var rolesCollection = ctx.store.collection(env.authentication_collections_prefix + 'roles');
45✔
14
  var subjectsCollection = ctx.store.collection(env.authentication_collections_prefix + 'subjects');
45✔
15

16
  storage.queryOpts = {
45✔
17
    dateField: 'created_at'
18
    , noDateFilter: true
19
  };
20

21
  function query_for (opts) {
3✔
22
    return find_options(opts, storage.queryOpts);
84✔
23
  }
24

25
  function create (collection) {
3✔
26
    function doCreate(obj, fn) {
3✔
27
      if (!obj.hasOwnProperty('created_at')) {
×
28
        obj.created_at = (new Date()).toISOString();
×
29
      }
30
      collection.insert(obj, function (err, doc) {
×
31
        storage.reload(function loaded() {
×
32
          fn(null, doc.ops);
×
33
        });
34
      });
35
    }
36
    return doCreate;
90✔
37
  }
38

39
  function list (collection) {
3✔
40
    function doList(opts, fn) {
3✔
41
      // these functions, find, sort, and limit, are used to
42
      // dynamically configure the request, based on the options we've
43
      // been given
44

45
      // determine sort options
46
      function sort() {
3✔
47
        return opts && opts.sort || {date: -1};
84✔
48
      }
49

50
      // configure the limit portion of the current query
51
      function limit() {
3✔
52
        if (opts && opts.count) {
84✔
53
          return this.limit(parseInt(opts.count));
×
54
        }
55
        return this;
84✔
56
      }
57

58
      // handle all the results
59
      function toArray(err, entries) {
3✔
60
        fn(err, entries);
84✔
61
      }
62

63
      // now just stitch them all together
64
      limit.call(collection
84✔
65
          .find(query_for(opts))
66
          .sort(sort())
67
      ).toArray(toArray);
68
    }
69

70
    return doList;
90✔
71
  }
72

73
  function remove (collection) {
3✔
74
    function doRemove (_id, callback) {
3✔
75
      collection.remove({ '_id': new ObjectID(_id) }, function (err) {
×
76
        storage.reload(function loaded() {
×
77
          callback(err, null);
×
78
        });
79
      });
80
    }
81
    return doRemove;
90✔
82
  }
83

84
  function save (collection) {
3✔
85
    function doSave (obj, callback) {
3✔
86
      obj._id = new ObjectID(obj._id);
×
87
      if (!obj.created_at) {
×
88
        obj.created_at = (new Date()).toISOString();
×
89
      }
90
      collection.save(obj, function (err) {
×
91
        //id should be added for new docs
92
        storage.reload(function loaded() {
×
93
          callback(err, obj);
×
94
        });
95
      });
96
    }
97
    return doSave;
90✔
98
  }
99

100
  storage.createSubject = create(subjectsCollection);
45✔
101
  storage.saveSubject = save(subjectsCollection);
45✔
102
  storage.removeSubject = remove(subjectsCollection);
45✔
103
  storage.listSubjects = list(subjectsCollection);
45✔
104

105
  storage.createRole = create(rolesCollection);
45✔
106
  storage.saveRole = save(rolesCollection);
45✔
107
  storage.removeRole = remove(rolesCollection);
45✔
108
  storage.listRoles = list(rolesCollection);
45✔
109

110
  storage.defaultRoles = [
45✔
111
    { name: 'admin', permissions: ['*'] }
112
    , { name: 'denied', permissions: [ ] }
113
    , { name: 'status-only', permissions: [ 'api:status:read' ] }
114
    , { name: 'readable', permissions: [ '*:*:read' ] }
115
    , { name: 'careportal', permissions: [ 'api:treatments:create' ] }
116
    , { name: 'devicestatus-upload', permissions: [ 'api:devicestatus:create' ] }
117
  ];
118

119
  storage.reload = function reload (callback) {
45✔
120

121
    storage.listRoles({sort: {name: 1}}, function listResults (err, results) {
42✔
122
      if (err) {
42✔
123
        return callback && callback(err);
×
124
      }
125

126
      storage.roles = results || [ ];
42✔
127

128
      _.forEach(storage.defaultRoles, function eachRole (role) {
42✔
129
        if (_.isEmpty(_.find(storage.roles, {name: role.name}))) {
252✔
130
          storage.roles.push(role);
252✔
131
        }
132
      });
133

134
      storage.roles = _.sortBy(storage.roles, 'name');
42✔
135

136
      storage.listSubjects({sort: {name: 1}}, function listResults (err, results) {
42✔
137
        if (err) {
42✔
138
          return callback && callback(err);
×
139
        }
140

141
        storage.subjects = _.map(results, function eachSubject (subject) {
42✔
142
          if (env.api_secret) {
×
143
            var shasum = crypto.createHash('sha1');
×
144
            shasum.update(env.api_secret);
×
145
            shasum.update(subject._id.toString());
×
146
            var abbrev = subject.name.toLowerCase().replace(/[\W]/g, '').substring(0, 10);
×
147
            subject.digest = shasum.digest('hex');
×
148
            subject.accessToken = abbrev + '-' + subject.digest.substring(0, 16);
×
149
          }
150

151
          return subject;
×
152
        });
153

154
        if (callback) {
42✔
155
          callback( );
42✔
156
        }
157
      });
158
    });
159

160
  };
161

162
  storage.findRole = function findRole (roleName) {
45✔
163
    return _.find(storage.roles, {name: roleName});
123✔
164
  };
165

166
  storage.roleToShiro = function roleToShiro (roleName) {
45✔
167
    var shiro = null;
123✔
168

169
    var role = storage.findRole(roleName);
123✔
170
    if (role) {
123✔
171
      shiro = shiroTrie.new();
123✔
172
      shiro.add(role.permissions);
123✔
173
    }
174

175
    return shiro;
123✔
176
  };
177

178
  storage.rolesToShiros = function roleToShiro (roleNames) {
45✔
179
    return _.chain(roleNames)
123✔
180
      .map(storage.roleToShiro)
181
      .reject(_.isEmpty)
182
      .value();
183
  };
184

185
  storage.roleToPermissions = function roleToPermissions (roleName) {
45✔
186
    var permissions = [ ];
×
187

188
    var role = storage.findRole(roleName);
×
189
    if (role) {
×
190
      permissions = role.permissions;
×
191
    }
192

193
    return permissions;
×
194
  };
195

196
  storage.findSubject = function findSubject (accessToken) {
45✔
197
    var prefix = _.last(accessToken.split('-'));
27✔
198

199
    if (prefix.length < 16) {
27✔
200
      return null;
27✔
201
    }
202

203
    return _.find(storage.subjects, function matches (subject) {
×
204
      return subject.digest.indexOf(prefix) === 0;
×
205
    });
206
  };
207

208
  storage.resolveSubjectAndPermissions = function resolveSubjectAndPermissions (accessToken) {
45✔
209
    var shiros = [];
×
210

211
    var subject = storage.findSubject(accessToken);
×
212
    if (subject) {
×
213
      shiros = storage.rolesToShiros(subject.roles);
×
214
    }
215

216
    return {
×
217
      subject: subject
218
      , shiros: shiros
219
    };
220
  };
221

222
  return storage;
45✔
223

224
}
225

226
module.exports = init;
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2024 Coveralls, Inc