Skip to content

Commit 94fd917

Browse files
committed
Clean acls, improve roles
1 parent c6f36b6 commit 94fd917

8 files changed

Lines changed: 233 additions & 28 deletions

File tree

OpenFlow/src/DatabaseConnection.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,36 @@ export class DatabaseConnection {
6868
await this.db.dropCollection(collectionname);
6969
}
7070

71+
async CleanACL<T extends Base>(item: T): Promise<T> {
72+
for (var i = item._acl.length - 1; i >= 0; i--) {
73+
{
74+
var ace = item._acl[i];
75+
var arr = await this.db.collection("users").find({ _id: ace._id }).project({ name: 1 }).limit(1).toArray();
76+
if (arr.length == 0) {
77+
item._acl.splice(i, 1);
78+
} else { ace.name = arr[0].name; }
79+
}
80+
}
81+
return item;
82+
}
83+
async Cleanmembers<T extends Role>(item: T): Promise<T> {
84+
for (var i = item.members.length - 1; i >= 0; i--) {
85+
{
86+
var ace = item.members[i];
87+
var exists = item.members.filter(x => x._id == ace._id);
88+
if (exists.length > 1) {
89+
item.members.splice(i, 1);
90+
} else {
91+
var arr = await this.db.collection("users").find({ _id: ace._id }).project({ name: 1 }).limit(1).toArray();
92+
if (arr.length == 0) {
93+
item.members.splice(i, 1);
94+
} else { ace.name = arr[0].name; }
95+
}
96+
}
97+
}
98+
return item;
99+
}
100+
71101
/**
72102
* Send a query to the database.
73103
* @param {any} query MongoDB Query
@@ -298,6 +328,11 @@ export class DatabaseConnection {
298328
}
299329

300330

331+
item = await this.CleanACL(item);
332+
if (item._type === "role" && collectionname === "users") {
333+
item = await this.Cleanmembers(item as any);
334+
}
335+
301336
// var options:CollectionInsertOneOptions = { writeConcern: { w: parseInt((w as any)), j: j } };
302337
var options: CollectionInsertOneOptions = { w: w, j: j };
303338
//var options: CollectionInsertOneOptions = { w: "majority" };
@@ -308,6 +343,10 @@ export class DatabaseConnection {
308343
users.AddMember(item);
309344
await users.Save(jwt)
310345
}
346+
if (collectionname === "users" && item._type === "role") {
347+
item.addRight(item._id, item.name, [Rights.read]);
348+
await this.db.collection(collectionname).replaceOne({ _id: item._id }, item);
349+
}
311350
this.traversejsondecode(item);
312351
return item;
313352
}
@@ -442,6 +481,10 @@ export class DatabaseConnection {
442481
q.opresult = null;
443482
try {
444483
if (itemReplace) {
484+
q.item = await this.CleanACL(q.item);
485+
if (q.item._type === "role" && q.collectionname === "users") {
486+
q.item = await this.Cleanmembers(q.item as any);
487+
}
445488
q.opresult = await this.db.collection(q.collectionname).replaceOne(_query, q.item, options);
446489
} else {
447490
if ((q.item["$set"]) === undefined) { (q.item["$set"]) = {} };

OpenFlow/src/Role.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Base } from "./base";
1+
import { Base, Rights, WellknownIds } from "./base";
22
import { DatabaseConnection } from "./DatabaseConnection";
33
import { TokenUser } from "./TokenUser";
44
import { Config } from "./Config";
@@ -44,9 +44,23 @@ export class Role extends Base {
4444
return result;
4545
}
4646
public static async FindByNameOrId(name: string, id: string): Promise<Role> {
47-
var items: Role[] = await Config.db.query<Role>({ $or: [{ name: name }, { _id: id }] }, null, 1, 0, null, "users", TokenUser.rootToken());
47+
var jwt = TokenUser.rootToken();
48+
var items: Role[] = await Config.db.query<Role>({ $or: [{ name: name }, { _id: id }] }, null, 1, 0, null, "users", jwt);
4849
if (items === null || items === undefined || items.length === 0) { return null; }
4950
var result: Role = Role.assign(items[0]);
51+
result = Role.assign(result);
52+
// Temp hack to update all existing users and roles
53+
if (result._type == "user") {
54+
result.addRight(result._id, result.name, [Rights.full_control]);
55+
result.removeRight(result._id, [Rights.delete]);
56+
} else {
57+
result.removeRight(result._id, [Rights.full_control]);
58+
if (result.name != "users") {
59+
result.addRight(result._id, result.name, [Rights.read]);
60+
}
61+
}
62+
result.addRight(WellknownIds.admins, "admins", [Rights.full_control]);
63+
await result.Save(jwt);
5064
return result;
5165
}
5266
public async Save(jwt: string): Promise<void> {

OpenFlow/src/base.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ export class Base implements IBase {
122122
var right: Ace = this.getRight(_id, deny);
123123
if (!right) { return; }
124124
rights.forEach(bit => {
125-
right.unsetBit(bit);
125+
if (bit == Rights.full_control) { this._acl = this._acl.filter(x => x._id != _id); } else { right.unsetBit(bit); }
126+
126127
});
127128
this.setRight(right);
128129
}

OpenFlow/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ async function initDatabase(): Promise<boolean> {
107107
filestore_users.removeRight(WellknownIds.admins, [Rights.delete]);
108108
await filestore_users.Save(jwt);
109109

110+
111+
// Temp hack to update all existing users and roles
112+
var _users = await Config.db.query<Role>({ $or: [{ _type: "user" }, { _type: "role" }] }, null, 1000, 0, null, "users", jwt);
113+
for (var i = 0; i < _users.length; i++) {
114+
var u = await Role.FindByNameOrId(null, _users[i]._id);
115+
}
116+
110117
return true;
111118
} catch (error) {
112119
logger.error(error);

OpenFlow/src/public/Controllers.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,7 @@ module openflow {
819819
export class RoleCtrl extends entityCtrl<openflow.Role> {
820820
public addthis: any = "";
821821
public users: TokenUser[] = null;
822+
public allusers: TokenUser[] = null;
822823
constructor(
823824
public $scope: ng.IScope,
824825
public $location: ng.ILocationService,
@@ -836,22 +837,18 @@ module openflow {
836837
await this.loadUsers();
837838
} else {
838839
this.model = new openflow.Role("");
840+
await this.loadUsers();
839841
}
840-
841842
});
842843
}
843844
async loadUsers(): Promise<void> {
844-
this.users = await this.api.Query(this.collection, { $or: [{ _type: "user" }, { _type: "role" }] }, null, null);
845-
var ids: string[] = [];
845+
this.allusers = await this.api.Query(this.collection, { $or: [{ _type: "user" }, { _type: "role" }] }, null, { _type: -1, name: 1 });
846846
if (this.model.members === undefined) { this.model.members = []; }
847+
var ids: string[] = [];
847848
for (var i: number = 0; i < this.model.members.length; i++) {
848849
ids.push(this.model.members[i]._id);
849850
}
850-
for (var i: number = this.users.length - 1; i >= 0; i--) {
851-
if (ids.indexOf(this.users[i]._id) > -1) {
852-
this.users.splice(i, 1);
853-
}
854-
}
851+
this.users = this.allusers.filter(x => ids.indexOf(x._id) == -1);
855852
this.addthis = this.users[0]._id;
856853
if (!this.$scope.$$phase) { this.$scope.$apply(); }
857854
}
@@ -871,6 +868,12 @@ module openflow {
871868
this.model.members.splice(i, 1);
872869
}
873870
}
871+
var ids: string[] = [];
872+
for (var i: number = 0; i < this.model.members.length; i++) {
873+
ids.push(this.model.members[i]._id);
874+
}
875+
this.users = this.allusers.filter(x => ids.indexOf(x._id) == -1);
876+
this.addthis = this.users[0]._id;
874877
}
875878
AddMember(model: any) {
876879
if (this.model.members === undefined) { this.model.members = []; }
@@ -879,6 +882,12 @@ module openflow {
879882
if (u._id === this.addthis) { user = u; }
880883
});
881884
this.model.members.push({ name: user.name, _id: user._id });
885+
var ids: string[] = [];
886+
for (var i: number = 0; i < this.model.members.length; i++) {
887+
ids.push(this.model.members[i]._id);
888+
}
889+
this.users = this.allusers.filter(x => ids.indexOf(x._id) == -1);
890+
this.addthis = this.users[0]._id;
882891
}
883892
}
884893

@@ -1569,12 +1578,11 @@ module openflow {
15691578
}
15701579
}
15711580
adduser() {
1572-
var ace = {
1573-
deny: false,
1574-
_id: this.newuser._id,
1575-
name: this.newuser.name,
1576-
rights: "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8="
1577-
}
1581+
var ace = new Ace();
1582+
ace.deny = false;
1583+
ace._id = this.newuser._id;
1584+
ace.name = this.newuser.name;
1585+
ace.rights = "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8=";
15781586
this.model._acl.push(ace);
15791587
}
15801588
isBitSet(base64: string, bit: number): boolean {

OpenFlow/src/public/Entities.ts

Lines changed: 133 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,144 @@ module openflow {
99
public _modifiedbyid: string;
1010
public _modifiedby: string;
1111
public _modified: Date;
12-
public _acl: ace[];
12+
public _acl: Ace[];
13+
14+
/**
15+
* Enumerate ACL for specefic ID
16+
* @param {string} _id Id to search for
17+
* @param {boolean=false} deny look for deny or allow permission
18+
* @returns Ace Ace if found, else null
19+
*/
20+
getRight(_id: string, deny: boolean = false): Ace {
21+
var result: Ace = null;
22+
if (!this._acl) { this._acl = []; }
23+
this._acl.forEach((a, index) => {
24+
if (a._id === _id && a.deny === deny) {
25+
this._acl[index] = Ace.assign(a);
26+
result = this._acl[index];
27+
}
28+
});
29+
if (result) {
30+
result = Ace.assign(result);
31+
}
32+
return result;
33+
}
34+
/**
35+
* Set right for specefic id, if exists
36+
* @param {Ace} x
37+
* @returns void
38+
*/
39+
setRight(x: Ace): void {
40+
if (!this._acl) { this._acl = []; }
41+
this._acl.forEach((a, index) => {
42+
if (a._id === x._id && a.deny === x.deny) {
43+
this._acl[index] = x;
44+
}
45+
});
46+
}
47+
/**
48+
* Add/update right for user/role
49+
* @param {string} _id user/role id
50+
* @param {string} name Displayname for user/role
51+
* @param {number[]} rights Right to set
52+
* @param {boolean=false} deny Deny the right
53+
* @returns void
54+
*/
55+
addRight(_id: string, name: string, rights: number[], deny: boolean = false): void {
56+
var right: Ace = this.getRight(_id, deny);
57+
if (!right) { right = new Ace(); this._acl.push(right); }
58+
right.deny = deny; right._id = _id; right.name = name;
59+
rights.forEach(bit => {
60+
right.setBit(bit);
61+
});
62+
this.setRight(right);
63+
}
64+
/**
65+
* Remove a right from user/role
66+
* @param {string} _id user/role id
67+
* @param {number[]=null} rights Right to revoke
68+
* @param {boolean=false} deny Deny right
69+
* @returns void
70+
*/
71+
removeRight(_id: string, rights: number[] = null, deny: boolean = false): void {
72+
if (!this._acl) { this._acl = []; }
73+
var right: Ace = this.getRight(_id, deny);
74+
if (!right) { return; }
75+
rights.forEach(bit => {
76+
right.unsetBit(bit);
77+
});
78+
this.setRight(right);
79+
}
1380
}
14-
export class ace {
81+
export class Ace {
1582
public deny: boolean;
1683
public _id: string;
1784
public name: string;
1885
public rights: string = "//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8=";
86+
static assign(o: any): Ace {
87+
return Object.assign(new Base(), o);
88+
}
89+
_base64ToArrayBuffer(string_base64): ArrayBuffer {
90+
var binary_string = window.atob(string_base64);
91+
var len = binary_string.length;
92+
var bytes = new Uint8Array(len);
93+
for (var i = 0; i < len; i++) {
94+
//var ascii = string_base64.charCodeAt(i);
95+
var ascii = binary_string.charCodeAt(i);
96+
bytes[i] = ascii;
97+
}
98+
return bytes.buffer;
99+
}
100+
_arrayBufferToBase64(array_buffer): string {
101+
var binary = '';
102+
var bytes = new Uint8Array(array_buffer);
103+
var len = bytes.byteLength;
104+
for (var i = 0; i < len; i++) {
105+
binary += String.fromCharCode(bytes[i])
106+
}
107+
return window.btoa(binary);
108+
}
109+
isBitSet(bit: number): boolean {
110+
bit--;
111+
var buf = this._base64ToArrayBuffer(this.rights);
112+
var view = new Uint8Array(buf);
113+
var octet = Math.floor(bit / 8);
114+
var currentValue = view[octet];
115+
var _bit = (bit % 8);
116+
var mask = Math.pow(2, _bit);
117+
return (currentValue & mask) != 0;
118+
}
119+
setBit(bit: number) {
120+
bit--;
121+
var buf = this._base64ToArrayBuffer(this.rights);
122+
var view = new Uint8Array(buf);
123+
var octet = Math.floor(bit / 8);
124+
var currentValue = view[octet];
125+
var _bit = (bit % 8);
126+
var mask = Math.pow(2, _bit);
127+
var newValue = currentValue | mask;
128+
view[octet] = newValue;
129+
return this._arrayBufferToBase64(view);
130+
}
131+
unsetBit(bit: number) {
132+
bit--;
133+
var buf = this._base64ToArrayBuffer(this.rights);
134+
var view = new Uint8Array(buf);
135+
var octet = Math.floor(bit / 8);
136+
var currentValue = view[octet];
137+
var _bit = (bit % 8);
138+
var mask = Math.pow(2, _bit);
139+
var newValue = currentValue &= ~mask;
140+
view[octet] = newValue;
141+
return this._arrayBufferToBase64(view);
142+
}
143+
toogleBit(bit: number) {
144+
if (this.isBitSet(bit)) {
145+
this.unsetBit(bit);
146+
} else {
147+
this.setBit(bit);
148+
}
149+
}
19150
}
20151

21152
export class Provider extends Base {

OpenFlow/src/public/Role.html

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,22 @@ <h1 class="pagetitle" translate lib="web">role</h1>
1313
<input ng-model="ctrl.model.name" class="form-control input-md" ng-disabled="ctrl.loading==true" />
1414
</div>
1515
</div>
16+
<div class="form-group ">
17+
<label for="add" class="col-sm-2 control-label" translate lib="web">add</label>
18+
<div class="col-sm-4 input-group">
19+
<select ng-model='ctrl.addthis' class="form-control"
20+
ng-options='item._id as (item.name + " " + item.username) for item in ctrl.users'></select>
21+
<button ng-click="ctrl.AddMember(ctrl.addthis)" type="button" ng-disabled="ctrl.loading==true"
22+
class="btn btn-success " translate lib="web">add</button>
23+
</div>
24+
</div>
1625
<div class="form-group" ng-repeat="m in ctrl.model.members">
1726
<label class="col-sm-2 control-label" translate lib="web">member</label>
1827
{{ m.name }} {{ m._id }}
1928
<button ng-click="ctrl.RemoveMember(m)" type="button" ng-disabled="ctrl.loading==true" class="btn btn-success"
2029
translate lib="web">remove</button>
2130

2231
</div>
23-
<div class="form-group">
24-
<label for="add" class="col-sm-2 control-label" translate lib="web">add</label>
25-
<div class="col-sm-4">
26-
<select ng-model='ctrl.addthis' ng-options='item._id as item.name for item in ctrl.users'></select>
27-
<button ng-click="ctrl.AddMember(ctrl.addthis)" type="button" ng-disabled="ctrl.loading==true"
28-
class="btn btn-success" translate lib="web">add</button>
29-
</div>
30-
</div>
3132

3233
<div class="form-group">
3334
<div class="col-sm-offset-2 col-sm-10">

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.0.325
1+
0.0.326

0 commit comments

Comments
 (0)