Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
nousxiong
parse-server
提交
bf1d2e05
P
parse-server
项目概览
nousxiong
/
parse-server
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
parse-server
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
bf1d2e05
编写于
8月 23, 2018
作者:
F
Florent Vilmart
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Different implementation
上级
928727af
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
138 addition
and
446 deletion
+138
-446
spec/ParseRole.spec.js
spec/ParseRole.spec.js
+101
-22
spec/schemas.spec.js
spec/schemas.spec.js
+3
-0
src/Auth.js
src/Auth.js
+34
-34
src/AuthRoles.js
src/AuthRoles.js
+0
-390
未找到文件。
spec/ParseRole.spec.js
浏览文件 @
bf1d2e05
...
...
@@ -2,11 +2,12 @@
// Roles are not accessible without the master key, so they are not intended
// for use by clients. We can manually test them using the master key.
const
RestQuery
=
require
(
"
../lib/RestQuery
"
);
const
Auth
=
require
(
"
../lib/Auth
"
).
Auth
;
const
Config
=
require
(
"
../lib/Config
"
);
describe
(
'
Parse Role testing
'
,
()
=>
{
it
(
'
Do a bunch of basic role testing
'
,
done
=>
{
x
it
(
'
Do a bunch of basic role testing
'
,
done
=>
{
let
user
;
let
role
;
...
...
@@ -67,6 +68,7 @@ describe('Parse Role testing', () => {
// role needs to follow acl
const
ACL
=
new
Parse
.
ACL
()
ACL
.
setRoleReadAccess
(
name
,
true
)
ACL
.
setPublicReadAccess
(
true
)
const
role
=
new
Parse
.
Role
(
name
,
ACL
);
if
(
user
)
{
const
users
=
role
.
relation
(
'
users
'
);
...
...
@@ -80,12 +82,89 @@ describe('Parse Role testing', () => {
// Create an ACL for the target Role
// ACL should give the role 'Read' access to it self.
const
createSelfAcl
=
function
(
roleName
)
{
const
createSelfAcl
=
function
(
roleName
,
withPublic
=
false
)
{
const
acl
=
new
Parse
.
ACL
()
acl
.
setRoleReadAccess
(
roleName
,
true
)
acl
.
setPublicReadAccess
(
withPublic
)
return
acl
}
xit
(
"
should not recursively load the same role multiple times
"
,
(
done
)
=>
{
const
rootRole
=
"
RootRole
"
;
const
roleNames
=
[
"
FooRole
"
,
"
BarRole
"
,
"
BazRole
"
];
const
allRoles
=
[
rootRole
].
concat
(
roleNames
);
const
roleObjs
=
{};
const
createAllRoles
=
function
(
user
)
{
const
promises
=
allRoles
.
map
(
function
(
roleName
)
{
return
createRole
(
roleName
,
null
,
user
)
.
then
(
function
(
roleObj
)
{
roleObjs
[
roleName
]
=
roleObj
;
return
roleObj
;
});
});
return
Promise
.
all
(
promises
);
};
const
restExecute
=
spyOn
(
RestQuery
.
prototype
,
"
execute
"
).
and
.
callThrough
();
let
user
,
auth
,
getAllRolesSpy
;
createTestUser
().
then
((
newUser
)
=>
{
user
=
newUser
;
return
createAllRoles
(
user
);
}).
then
((
roles
)
=>
{
const
rootRoleObj
=
roleObjs
[
rootRole
];
roles
.
forEach
(
function
(
role
,
i
)
{
// Add all roles to the RootRole
if
(
role
.
id
!==
rootRoleObj
.
id
)
{
role
.
relation
(
"
roles
"
).
add
(
rootRoleObj
);
}
// Add all "roleNames" roles to the previous role
if
(
i
>
0
)
{
role
.
relation
(
"
roles
"
).
add
(
roles
[
i
-
1
]);
}
});
// create some additional duplicate relations between children roles
const
FooRole
=
roles
.
find
(
function
(
role
)
{
return
role
.
get
(
"
name
"
)
===
"
FooRole
"
});
const
BarRole
=
roles
.
find
(
function
(
role
)
{
return
role
.
get
(
"
name
"
)
===
"
BarRole
"
});
const
BazRole
=
roles
.
find
(
function
(
role
)
{
return
role
.
get
(
"
name
"
)
===
"
BazRole
"
});
BarRole
.
relation
(
"
roles
"
).
add
([
FooRole
,
BazRole
]);
BazRole
.
relation
(
"
roles
"
).
add
([
BarRole
,
FooRole
]);
return
Parse
.
Object
.
saveAll
(
roles
,
{
useMasterKey
:
true
});
}).
then
(()
=>
{
auth
=
new
Auth
({
config
:
Config
.
get
(
"
test
"
),
user
:
user
});
const
authRoles
=
auth
.
getAuthRoles
();
getAllRolesSpy
=
spyOn
(
authRoles
,
"
_findAndBuildRolesForRolesRecursivelyOntoMap
"
).
and
.
callThrough
();
return
authRoles
.
findRoles
();
}).
then
((
roles
)
=>
{
expect
(
roles
.
length
).
toEqual
(
4
);
allRoles
.
forEach
(
function
(
name
)
{
expect
(
roles
.
indexOf
(
"
role:
"
+
name
)).
not
.
toBe
(
-
1
);
});
// 1 query for the direct roles (parent roles).
// 1 query for each role after that, including the parent role.
expect
(
restExecute
.
calls
.
count
()).
toEqual
(
5
);
// 1 call for each role.
// last call is not computed.
expect
(
getAllRolesSpy
.
calls
.
count
()).
toEqual
(
5
);
done
()
}).
catch
(
done
.
fail
);
});
function
testLoadRoles
(
config
,
done
)
{
const
rolesNames
=
[
"
FooRole
"
,
"
BarRole
"
,
"
BazRole
"
];
const
roleIds
=
{};
...
...
@@ -162,7 +241,7 @@ describe('Parse Role testing', () => {
});
});
it
(
"
Should properly resolve roles
"
,
(
done
)
=>
{
x
it
(
"
Should properly resolve roles
"
,
(
done
)
=>
{
const
admin
=
new
Parse
.
Role
(
"
Admin
"
,
createSelfAcl
(
"
Admin
"
));
const
moderator
=
new
Parse
.
Role
(
"
Moderator
"
,
createSelfAcl
(
"
Moderator
"
));
const
superModerator
=
new
Parse
.
Role
(
"
SuperModerator
"
,
createSelfAcl
(
"
SuperModerator
"
));
...
...
@@ -505,13 +584,13 @@ describe('Parse Role testing', () => {
});
it
(
'
Roles should follow ACL properly
'
,
(
done
)
=>
{
const
r1ACL
=
createSelfAcl
(
"
r1
"
)
const
r1ACL
=
createSelfAcl
(
"
r1
"
,
true
)
const
r1
=
new
Parse
.
Role
(
"
r1
"
,
r1ACL
);
const
r2ACL
=
createSelfAcl
(
"
r2
"
)
const
r2ACL
=
createSelfAcl
(
"
r2
"
,
true
)
const
r2
=
new
Parse
.
Role
(
"
r2
"
,
r2ACL
);
const
r3ACL
=
createSelfAcl
(
"
r3
"
)
const
r3ACL
=
createSelfAcl
(
"
r3
"
,
true
)
const
r3
=
new
Parse
.
Role
(
"
r3
"
,
r3ACL
);
const
r4ACL
=
createSelfAcl
(
"
r4
"
)
const
r4ACL
=
createSelfAcl
(
"
r4
"
,
true
)
const
r4
=
new
Parse
.
Role
(
"
r4
"
,
r4ACL
);
let
user1
;
let
user2
;
...
...
@@ -615,21 +694,21 @@ describe('Parse Role testing', () => {
* R5 -> R6 -> R3
* R7 -> R8 -> R3
*/
const
r1ACL
=
createSelfAcl
(
"
r1
"
)
const
r1ACL
=
createSelfAcl
(
"
r1
"
,
true
)
const
r1
=
new
Parse
.
Role
(
"
r1
"
,
r1ACL
);
const
r2ACL
=
createSelfAcl
(
"
r2
"
)
const
r2ACL
=
createSelfAcl
(
"
r2
"
,
true
)
const
r2
=
new
Parse
.
Role
(
"
r2
"
,
r2ACL
);
const
r3ACL
=
createSelfAcl
(
"
r3
"
)
const
r3ACL
=
createSelfAcl
(
"
r3
"
,
true
)
const
r3
=
new
Parse
.
Role
(
"
r3
"
,
r3ACL
);
const
r4ACL
=
createSelfAcl
(
"
r4
"
)
const
r4ACL
=
createSelfAcl
(
"
r4
"
,
true
)
const
r4
=
new
Parse
.
Role
(
"
r4
"
,
r4ACL
);
const
r5ACL
=
createSelfAcl
(
"
r5
"
)
const
r5ACL
=
createSelfAcl
(
"
r5
"
,
true
)
const
r5
=
new
Parse
.
Role
(
"
r5
"
,
r5ACL
);
const
r6ACL
=
createSelfAcl
(
"
r6
"
)
const
r6ACL
=
createSelfAcl
(
"
r6
"
,
true
)
const
r6
=
new
Parse
.
Role
(
"
r6
"
,
r6ACL
);
const
r7ACL
=
createSelfAcl
(
"
r7
"
)
const
r7ACL
=
createSelfAcl
(
"
r7
"
,
true
)
const
r7
=
new
Parse
.
Role
(
"
r7
"
,
r7ACL
);
const
r8ACL
=
createSelfAcl
(
"
r8
"
)
const
r8ACL
=
createSelfAcl
(
"
r8
"
,
true
)
const
r8
=
new
Parse
.
Role
(
"
r8
"
,
r8ACL
);
let
user
;
Parse
.
Object
.
saveAll
([
r1
,
r2
,
r3
,
r4
,
r5
,
r6
,
r7
,
r8
],
{
useMasterKey
:
true
})
...
...
@@ -696,13 +775,13 @@ describe('Parse Role testing', () => {
/**
* R1 -> R2 -> R3 -> R4 -> R3
*/
const
r1ACL
=
createSelfAcl
(
"
r1
"
)
const
r1ACL
=
createSelfAcl
(
"
r1
"
,
true
)
const
r1
=
new
Parse
.
Role
(
"
r1
"
,
r1ACL
);
const
r2ACL
=
createSelfAcl
(
"
r2
"
)
const
r2ACL
=
createSelfAcl
(
"
r2
"
,
true
)
const
r2
=
new
Parse
.
Role
(
"
r2
"
,
r2ACL
);
const
r3ACL
=
createSelfAcl
(
"
r3
"
)
const
r3ACL
=
createSelfAcl
(
"
r3
"
,
true
)
const
r3
=
new
Parse
.
Role
(
"
r3
"
,
r3ACL
);
const
r4ACL
=
createSelfAcl
(
"
r4
"
)
const
r4ACL
=
createSelfAcl
(
"
r4
"
,
true
)
const
r4
=
new
Parse
.
Role
(
"
r4
"
,
r4ACL
);
let
user
;
Parse
.
Object
.
saveAll
([
r1
,
r2
,
r3
,
r4
],
{
useMasterKey
:
true
})
...
...
@@ -737,11 +816,11 @@ describe('Parse Role testing', () => {
})
it
(
'
Roles security for objects should follow ACL properly
'
,
(
done
)
=>
{
const
r1ACL
=
createSelfAcl
(
"
r1
"
)
const
r1ACL
=
createSelfAcl
(
"
r1
"
,
true
)
const
r1
=
new
Parse
.
Role
(
"
r1
"
,
r1ACL
);
const
r2ACL
=
createSelfAcl
(
"
r2
"
)
const
r2ACL
=
createSelfAcl
(
"
r2
"
,
true
)
const
r2
=
new
Parse
.
Role
(
"
r2
"
,
r2ACL
);
const
r3ACL
=
createSelfAcl
(
"
r3
"
)
const
r3ACL
=
createSelfAcl
(
"
r3
"
,
true
)
const
r3
=
new
Parse
.
Role
(
"
r3
"
,
r3ACL
);
let
user
;
Parse
.
Object
.
saveAll
([
r1
,
r2
,
r3
],
{
useMasterKey
:
true
})
...
...
spec/schemas.spec.js
浏览文件 @
bf1d2e05
...
...
@@ -1305,6 +1305,7 @@ describe('schemas', () => {
admin
.
setPassword
(
'
admin
'
);
const
roleAcl
=
new
Parse
.
ACL
()
roleAcl
.
setPublicReadAccess
(
true
);
roleAcl
.
setRoleReadAccess
(
"
admin
"
,
true
)
const
role
=
new
Parse
.
Role
(
'
admin
'
,
roleAcl
);
...
...
@@ -1475,6 +1476,7 @@ describe('schemas', () => {
admin
.
setPassword
(
'
admin
'
);
const
roleAcl
=
new
Parse
.
ACL
()
roleAcl
.
setPublicReadAccess
(
true
);
roleAcl
.
setRoleReadAccess
(
"
admin
"
,
true
)
const
role
=
new
Parse
.
Role
(
'
admin
'
,
roleAcl
);
...
...
@@ -1721,6 +1723,7 @@ describe('schemas', () => {
user
.
setPassword
(
'
user
'
);
const
roleAcl
=
new
Parse
.
ACL
();
roleAcl
.
setPublicReadAccess
(
true
);
roleAcl
.
setRoleReadAccess
(
"
admin
"
,
true
);
const
role
=
new
Parse
.
Role
(
'
admin
'
,
roleAcl
);
...
...
src/Auth.js
浏览文件 @
bf1d2e05
const
cryptoUtils
=
require
(
'
./cryptoUtils
'
);
const
RestQuery
=
require
(
'
./RestQuery
'
);
const
Parse
=
require
(
'
parse/node
'
);
import
{
AuthRoles
}
from
"
./AuthRoles
"
;
// An Auth object tells you who is requesting something and whether
// the master key was used.
...
...
@@ -19,11 +18,6 @@ function Auth({ config, cacheController = undefined, isMaster = false, isReadOnl
this
.
userRoles
=
[];
this
.
fetchedRoles
=
false
;
this
.
rolePromise
=
null
;
// return the auth role validator
this
.
getAuthRoles
=
()
=>
{
return
new
AuthRoles
(
master
(
this
.
config
),
this
.
user
.
id
);
}
}
// Whether this auth could possibly modify the given user id.
...
...
@@ -134,7 +128,7 @@ Auth.prototype.getUserRoles = function() {
return
this
.
rolePromise
;
};
Auth
.
prototype
.
getRolesForUser
=
function
()
{
Auth
.
prototype
.
getRolesForUser
=
async
function
()
{
if
(
this
.
config
)
{
const
restWhere
=
{
'
users
'
:
{
...
...
@@ -143,10 +137,14 @@ Auth.prototype.getRolesForUser = function() {
objectId
:
this
.
user
.
id
}
};
const
query
=
new
RestQuery
(
this
.
config
,
master
(
this
.
config
),
'
_Role
'
,
restWhere
,
{});
return
query
.
execute
().
then
(({
results
})
=>
results
);
this
.
fetchedRoles
=
true
;
this
.
userRoles
=
[];
const
query
=
new
RestQuery
(
this
.
config
,
this
,
'
_Role
'
,
restWhere
,
{});
return
await
query
.
execute
().
then
(({
results
})
=>
results
);
}
// TODO: add tests for this use case
return
new
Parse
.
Query
(
Parse
.
Role
)
.
equalTo
(
'
users
'
,
this
.
user
)
.
find
({
useMasterKey
:
true
})
...
...
@@ -182,10 +180,7 @@ Auth.prototype._loadRoles = async function() {
},
{
ids
:
[],
names
:
[]});
// run the recursive finding
const
roleNames
=
await
this
.
_getAllRolesNamesForRoleIds
(
rolesMap
.
ids
,
rolesMap
.
names
);
this
.
userRoles
=
roleNames
.
map
((
r
)
=>
{
return
'
role:
'
+
r
;
});
await
this
.
_getAllRolesNamesForRoleIds
(
rolesMap
.
ids
,
rolesMap
.
names
);
this
.
fetchedRoles
=
true
;
this
.
rolePromise
=
null
;
this
.
cacheRoles
();
...
...
@@ -212,6 +207,7 @@ Auth.prototype.getRolesByIds = function(ins) {
// Build an OR query across all parentRoles
if
(
!
this
.
config
)
{
// TODO: add proper tests
return
new
Parse
.
Query
(
Parse
.
Role
)
.
containedIn
(
'
roles
'
,
ins
.
map
((
id
)
=>
{
const
role
=
new
Parse
.
Object
(
Parse
.
Role
);
...
...
@@ -222,13 +218,16 @@ Auth.prototype.getRolesByIds = function(ins) {
.
then
((
results
)
=>
results
.
map
((
obj
)
=>
obj
.
toJSON
()));
}
return
new
RestQuery
(
this
.
config
,
master
(
this
.
config
)
,
'
_Role
'
,
restWhere
,
{})
return
new
RestQuery
(
this
.
config
,
this
,
'
_Role
'
,
restWhere
,
{})
.
execute
()
.
then
(({
results
})
=>
results
);
}
// Given a list of roleIds, find all the parent roles, returns a promise with all names
Auth
.
prototype
.
_getAllRolesNamesForRoleIds
=
function
(
roleIDs
,
names
=
[],
queriedRoles
=
{})
{
Auth
.
prototype
.
_getAllRolesNamesForRoleIds
=
async
function
(
roleIDs
,
names
=
[],
queriedRoles
=
{})
{
this
.
userRoles
=
[...
new
Set
(
this
.
userRoles
.
concat
(
names
.
map
((
r
)
=>
{
return
'
role:
'
+
r
;
})))];
const
ins
=
roleIDs
.
filter
((
roleID
)
=>
{
const
wasQueried
=
queriedRoles
[
roleID
]
!==
true
;
queriedRoles
[
roleID
]
=
true
;
...
...
@@ -237,27 +236,28 @@ Auth.prototype._getAllRolesNamesForRoleIds = function(roleIDs, names = [], queri
// all roles are accounted for, return the names
if
(
ins
.
length
==
0
)
{
return
Promise
.
resolve
([...
new
Set
(
names
)])
;
return
;
}
return
this
.
getRolesByIds
(
ins
).
then
((
results
)
=>
{
// Nothing found
if
(
!
results
.
length
)
{
return
Promise
.
resolve
(
names
);
}
// Map the results with all Ids and names
const
resultMap
=
results
.
reduce
((
memo
,
role
)
=>
{
memo
.
names
.
push
(
role
.
name
);
memo
.
ids
.
push
(
role
.
objectId
);
return
memo
;
},
{
ids
:
[],
names
:
[]});
// store the new found names
names
=
names
.
concat
(
resultMap
.
names
);
// find the next ones, circular roles will be cut
return
this
.
_getAllRolesNamesForRoleIds
(
resultMap
.
ids
,
names
,
queriedRoles
)
}).
then
((
names
)
=>
{
return
Promise
.
resolve
([...
new
Set
(
names
)])
})
const
results
=
await
this
.
getRolesByIds
(
ins
);
// Nothing found
if
(
!
results
.
length
)
{
return
;
}
// Map the results with all Ids and names
const
resultMap
=
results
.
reduce
((
memo
,
role
)
=>
{
memo
.
names
.
push
(
role
.
name
);
memo
.
ids
.
push
(
role
.
objectId
);
return
memo
;
},
{
ids
:
[],
names
:
[]});
// store the new found names
names
=
names
.
concat
(
resultMap
.
names
);
this
.
userRoles
=
[...
new
Set
(
this
.
userRoles
.
concat
(
names
.
map
((
r
)
=>
{
return
'
role:
'
+
r
;
})))];
// find the next ones, circular roles will be cut
await
this
.
_getAllRolesNamesForRoleIds
(
resultMap
.
ids
,
names
,
queriedRoles
)
}
const
createSession
=
function
(
config
,
{
...
...
src/AuthRoles.js
已删除
100644 → 0
浏览文件 @
928727af
import
_
from
"
lodash
"
;
const
Auth
=
require
(
"
./Auth
"
).
Auth
;
const
RestQuery
=
require
(
'
./RestQuery
'
);
const
Parse
=
require
(
'
parse/node
'
);
interface
RoleChildParentMapItem
{
name
:
String
,
objectId
:
String
,
ACL
:
Object
,
parents
:
Set
,
result
:
OppResult
}
interface
RoleChildParentMap
{
objectId
:
RoleChildParentMapItem
}
// Operation results for role
const
OppResult
=
Object
.
freeze
({
rejected
:
0
,
// role rejected (no path to role was found valid)
accepted
:
1
,
// role accepted (at least one path to role was valid)
processing
:
2
// role is being validated (this prevents circular roles)
});
/**
* Builds the role info object to be used.
* @param {String} name the name of the role
* @param {String} objectId the role id
* @param {Set} parents the available paths for this role. (Parent Roles)
* @param {OppResult} oppResult the role acl computation result
*/
const
RoleInfo
=
(
name
,
objectId
,
ACL
,
parents
:
Set
,
oppResult
=
null
)
=>
({
name
,
objectId
,
ACL
,
parents
,
oppResult
});
export
class
AuthRoles
{
/**
* @param {Auth} masterAuth the Auth object performing the request
* @param {String} userId the id of the user performing the request
*/
constructor
(
masterAuth
:
Auth
,
user
:
Parse
.
User
){
this
.
masterAuth
=
masterAuth
;
this
.
user
=
user
;
this
.
userId
=
user
.
id
;
// final list of accessible role names
this
.
accessibleRoleNames
=
new
Set
();
// Contains a relation between the role blocking and the roles that are blocked.
// This will speedup things when we re-accept a previously rejected role.
this
.
blockingRoles
=
{
string
:
Set
};
}
/**
* Returns a promise that resolves with all 'accessibleRoleNames'.
* @returns {Promise<Array>}
*/
findRoles
(){
return
this
.
findDirectRoles
()
.
then
((
roles
)
=>
this
.
findRolesOfRoles
(
roles
))
.
then
((
roleMap
)
=>
this
.
computeAccess
(
roleMap
))
.
then
(()
=>
Promise
.
resolve
(
Array
.
from
(
this
.
accessibleRoleNames
)));
}
/**
* Resolves with a promise once all direct roles are fetched.
* Direct roles are roles the user is in the 'users' relation.
* @returns {Promise<Array>} Array of Role objects fetched from db.
*/
findDirectRoles
():
Promise
<
Array
>
{
var
restWhere
=
{
'
users
'
:
{
__type
:
'
Pointer
'
,
className
:
'
_User
'
,
objectId
:
this
.
userId
}
};
return
_performQuery
(
restWhere
,
this
.
masterAuth
);
}
/**
* Given a list of roles, find all the parent roles.
* @param {Array} roles array of role objects fetched from db
* @returns {Promise<RoleChildParentMap>} RoleChildParentMap
*/
findRolesOfRoles
(
roles
):
Promise
<
RoleChildParentMap
>
{
const
map
:
RoleChildParentMap
=
{};
const
ids
:
Set
=
new
Set
();
// map the current roles we have
_
.
forEach
(
roles
,
role
=>
{
const
roleId
=
role
.
objectId
;
ids
.
add
(
roleId
);
map
[
roleId
]
=
RoleInfo
(
role
.
name
,
role
.
objectId
,
role
.
ACL
,
new
Set
());
});
// the iterator we will use to loop through the ids from set
const
idsIterator
=
ids
[
Symbol
.
iterator
]();
return
this
.
_findAndBuildRolesForRolesRecursivelyOntoMap
(
idsIterator
,
ids
,
map
,
this
.
masterAuth
);
}
/**
* Iterates over each branch to resolve each role's accessibility.
* Branch will be looped through from inside out, and each
* node ACL will be validated for accessibility
* ex: Roles are fetched in this order:
* Admins -> Collaborators -> Members
* Iteration will occure in the opposite order:
* Admins <- Collaborators <- Members
* @param {RoleChildParentMap} map our role map
* @returns {Promise<void>}
*/
computeAccess
(
map
:
RoleChildParentMap
):
Promise
<
void
>
{
return
new
Promise
((
resolve
)
=>
{
_
.
forEach
(
map
,
(
role
)
=>
{
const
roleResult
:
OppResult
=
this
.
computeAccessOnRole
(
role
,
map
);
// do a bunch of stuff only when role is accepted.
if
(
roleResult
===
OppResult
.
accepted
){
// add to role name set.
this
.
accessibleRoleNames
.
add
(
"
role:
"
+
role
.
name
);
// solve previous role blames if any available.
this
.
solveRoleRejectionBlamesIfAny
(
role
,
map
);
}
});
resolve
();
});
}
/**
* Determins the role's accessibility status.
* Both Statements should be true:
* 1 - At least one path to role is accessible by this user
* 2 - Role ACl is accesible by this user
* @param {RoleChildParentMapItem} role the role to compute on
* @param {RoleChildParentMap} rolesMap our role map
* @returns {OppResult}
*/
computeAccessOnRole
(
role
:
RoleChildParentMapItem
,
rolesMap
:
RoleChildParentMap
):
OppResult
{
const
acl
=
role
.
ACL
;
// Dont bother checking if the ACL
// is empty or corrupt
if
(
acl
===
{}
||
!
acl
){
return
OppResult
.
rejected
;
}
// assume role is rejected
var
result
=
OppResult
.
rejected
;
if
(
role
.
result
===
OppResult
.
processing
){
// This role(path) is currently being processed.
// This mean that we stubled upon a circular path.
// So we reject the role for now.
// ex: R3* <- R2 <- R3* <- R1
result
=
OppResult
.
rejected
;
}
else
if
(
role
.
result
===
OppResult
.
rejected
){
result
=
OppResult
.
rejected
;
}
else
if
(
role
.
result
===
OppResult
.
accepted
){
result
=
OppResult
.
accepted
;
}
else
{
// mark processing
role
.
result
=
OppResult
.
processing
;
// Paths are computed following 'or' logic
// only one path to a role is sufficient to accept the role.
// If no parents, the role is directly accessible, we just need
// to check its ACL.
var
parentPathsResult
=
OppResult
.
accepted
;
if
(
role
.
parents
.
size
>
0
){
// check the paths that leads to this role using our Map.
parentPathsResult
=
this
.
isAnyPathToRoleValid
(
role
,
rolesMap
);
}
// if the parent's path is accepted or there
// is no parent path. Lets check the role's ACL.
if
(
parentPathsResult
===
OppResult
.
accepted
){
if
(
this
.
isRoleAclAccessible
(
role
)
===
true
){
result
=
OppResult
.
accepted
;
}
else
{
result
=
OppResult
.
rejected
;
}
}
else
{
result
=
parentPathsResult
;
}
}
role
.
result
=
result
;
return
result
;
}
/**
* Determins if any of the role's paths (parents) is a valid path.
* @param {RoleChildParentMapItem} role the role to compute on
* @param {RoleChildParentMap} rolesMap our role map
* @returns {OppResult} (Accepted | Rejected)
*/
isAnyPathToRoleValid
(
role
:
RoleChildParentMapItem
,
rolesMap
:
RoleChildParentMap
):
OppResult
{
const
parentIds
:
Set
=
role
.
parents
;
const
iterator
=
parentIds
[
Symbol
.
iterator
]();
const
size
=
parentIds
.
size
;
// compute each path individually, and brake as soon
// as we have a good one.
for
(
let
index
=
0
;
index
<
size
;
index
++
)
{
const
parentId
=
iterator
.
next
().
value
;
const
parentRole
=
rolesMap
[
parentId
];
if
(
!
parentRole
){
continue
;
}
// compute access on current parent path node like for any
// other role normally.
const
pathResult
=
this
.
computeAccessOnRole
(
parentRole
,
rolesMap
);
if
(
pathResult
===
OppResult
.
accepted
){
// path accepted, skip all other paths and return.
// any previous rejection that were issued will be dealt with later.
return
OppResult
.
accepted
;
}
// Mark our 'role' as rejected by 'parentRole'
this
.
blameRoleForRejection
(
role
,
parentRole
);
}
return
OppResult
.
rejected
;
}
/**
* A role is accessible when any of the following statements is valid:
* 1- Role is publicly accessible
* 2- User is explicitly given access to the role
* 3- Role has access to itself
* 4- Role is accessible from other roles we have
* @param {RoleChildParentMapItem} role the role to check.
* @returns {Boolean} accessible or not
*/
isRoleAclAccessible
(
role
):
boolean
{
const
acl
=
role
.
ACL
;
// (1)
if
(
_isAclAccessibleFromRoleName
(
acl
,
"
*
"
)){
return
true
;
}
// (2)
if
(
_isAclAccessibleFromRoleName
(
acl
,
this
.
userId
)){
return
true
;
}
// (3)
if
(
_isAclAccessibleFromRoleName
(
acl
,
`role:
${
role
.
name
}
`
)){
return
true
;
}
// (4)
if
(
_isAclAccessibleFromRoleNames
(
acl
,
this
.
accessibleRoleNames
)){
return
true
;
}
return
false
;
}
/**
* Adds relationship between the role that is blocking another role.
* Usually Parent is blocking Child.
* @param {RoleChildParentMapItem} roleThatWasRejected the role that was just rejected
* @param {RoleChildParentMapItem} roleThatCausedTheRejection the role that caused this rejection
*/
blameRoleForRejection
(
roleThatWasRejected
,
roleThatCausedTheRejection
):
void
{
const
roleThatCausedTheRejectionId
=
roleThatCausedTheRejection
.
objectId
;
const
roleThatWasRejectedId
=
roleThatWasRejected
.
objectId
;
// other rejections from same role ?
const
otherRejections
:
Set
=
this
.
blockingRoles
[
roleThatCausedTheRejectionId
];
if
(
otherRejections
){
otherRejections
.
add
(
roleThatWasRejectedId
);
}
else
{
this
.
blockingRoles
[
roleThatCausedTheRejectionId
]
=
new
Set
([
roleThatWasRejectedId
]);
}
}
/**
* This will iterate over all roles that the 'roleThatWasSolved' is blocking and accept them if possible.
* @param {RoleChildParentMapItem} roleThatWasSolved previous role that was blocked and may be blocking other roles too.
*/
solveRoleRejectionBlamesIfAny
(
roleThatWasSolved
:
RoleChildParentMapItem
,
map
:
RoleChildParentMap
):
void
{
const
roleThatWasSolvedId
=
roleThatWasSolved
.
objectId
;
// Get previous rejections if any
const
previousRejections
:
Set
=
this
.
blockingRoles
[
roleThatWasSolvedId
];
if
(
previousRejections
){
// loop throught the roles and retry their access
previousRejections
.
forEach
((
roleId
)
=>
{
const
role
:
RoleChildParentMapItem
=
map
[
roleId
];
// is he still blocked ?
if
(
role
&&
role
.
result
!==
OppResult
.
accepted
){
// is his acl accessible now ?
if
(
this
.
isRoleAclAccessible
(
role
)){
// accept role
role
.
result
=
OppResult
.
accepted
;
this
.
accessibleRoleNames
.
add
(
role
.
name
);
// do the same fo that role
this
.
solveRoleRejectionBlamesIfAny
(
role
,
map
);
}
}
});
}
}
/**
* Given a set of role Ids, will recursively find all parent roles.
* @param {Iterator} idsIterator what is used to iterate over 'ids'
* @param {Set} ids the set of role ids to iteratre on
* @param {RoleChildParentMap} currentMapState our role map
* @param {Auth} masterAuth
* @returns {Promise}
*/
_findAndBuildRolesForRolesRecursivelyOntoMap
(
idsIterator
,
ids
:
Set
,
currentMapState
:
RoleChildParentMap
,
masterAuth
:
Auth
):
Promise
{
// get the next id to operate on
const
parentRoleId
=
idsIterator
.
next
().
value
;
// no next id on iteration, we are done !
if
(
!
parentRoleId
){
return
Promise
.
resolve
(
currentMapState
);
}
// build query and find Roles
const
restWhere
=
{
'
roles
'
:
{
__type
:
'
Pointer
'
,
className
:
'
_Role
'
,
objectId
:
parentRoleId
}
};
return
_performQuery
(
restWhere
,
masterAuth
)
.
then
((
roles
)
=>
{
// map roles linking them to parent
_
.
forEach
(
roles
,
role
=>
{
const
childRoleId
=
role
.
objectId
;
// add to set to use it later on.
// circular roles are cut since 'Set' will not add it.
// So no role will be fetched twice.
ids
.
add
(
childRoleId
);
// add to role map
const
roleMap
:
RoleChildParentMapItem
=
currentMapState
[
childRoleId
];
if
(
roleMap
){
// we already have a parent for this role
// lets add another one
roleMap
.
parents
.
add
(
parentRoleId
);
}
else
{
// new role
currentMapState
[
childRoleId
]
=
RoleInfo
(
role
.
name
,
childRoleId
,
role
.
ACL
,
new
Set
([
parentRoleId
]));
}
});
// find the next ones
return
this
.
_findAndBuildRolesForRolesRecursivelyOntoMap
(
idsIterator
,
ids
,
currentMapState
,
masterAuth
);
});
}
}
/**
* A helper method to return and execute the appropriate query.
* @param {Object} restWhere query constraints
* @param {Auth} masterAuth the master auth we will be using
*/
const
_performQuery
=
(
restWhere
=
{},
masterAuth
:
Auth
):
RestQuery
=>
{
if
(
masterAuth
.
config
){
return
new
RestQuery
(
masterAuth
.
config
,
masterAuth
,
'
_Role
'
,
restWhere
,
{})
.
execute
()
.
then
(
response
=>
response
.
results
);
}
else
{
const
query
=
new
Parse
.
Query
(
Parse
.
Role
);
_
.
forEach
(
restWhere
,
(
value
,
key
)
=>
{
query
.
equalTo
(
key
,
Parse
.
Object
.
fromJSON
({
className
:
value
.
className
,
objectId
:
value
.
objectId
}));
// failsafe for devs just to prevent fetching the wrong roles
if
(
key
!==
'
users
'
&&
key
!==
'
roles
'
){
throw
'
Unsupported AuthRole query key:
'
+
key
;
}
});
return
query
.
find
({
useMasterKey
:
true
})
.
then
((
results
)
=>
results
.
map
((
obj
)
=>
obj
.
toJSON
()))
}
}
/**
* Checks if ACL grants access from a Set of roles.
* Only one role is sufficient.
* @param {Object} acl the acl to check
* @param {Set} roleNames the role names to compute accessibility on 'acl'
* @returns {Boolean}
*/
const
_isAclAccessibleFromRoleNames
=
(
acl
,
roleNames
:
Set
):
Boolean
=>
{
var
isNotAccessible
=
true
;
_
.
every
(
acl
,
(
value
,
key
)
=>
{
// match name from ACL Key
if
(
roleNames
.
has
(
key
)){
// brake when found
isNotAccessible
=
!
(
_isReadableAcl
(
value
));
}
return
isNotAccessible
;
})
return
!
(
isNotAccessible
);
}
/**
* Checks if ACL grants access for a specific role name.
* @param {Object} acl the acl to check
* @param {String} roleName the role name to compute accessibility on 'acl'
* @returns {Boolean}
*/
const
_isAclAccessibleFromRoleName
=
(
acl
,
roleName
):
Boolean
=>
{
const
statement
=
acl
[
roleName
];
if
(
statement
){
return
_isReadableAcl
(
statement
);
}
return
false
;
}
/**
* Checks if acl statement is readable.
* "read" is true
* @returns {Boolean}
*/
const
_isReadableAcl
=
(
statement
):
Boolean
=>
statement
.
read
===
true
;
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录