Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1"""Permission library 

2 

3* Computing permissions 

4* Support for the authorization system 

5""" 

6 

7from config import Config as C, Names as N 

8from control.utils import pick as G 

9 

10CT = C.tables 

11CP = C.perm 

12 

13DEFAULT_PERM = CP.default 

14GROUP_RANK = CP.groupRank 

15TABLE_PERM = CT.perm 

16 

17NOBODY = N.nobody 

18UNAUTH = N.public 

19AUTH = N.auth 

20EDIT = N.edit 

21OWN = N.own 

22COORD = N.coord 

23OFFICE = N.office 

24SYSTEM = N.system 

25ROOT = N.root 

26 

27ALLOW_OUR = set(CT.userTables) | set(CT.userEntryTables) 

28 

29 

30def checkTable( 

31 auth, table 

32): 

33 """Verify whether user's credentials match the requirements for a table. 

34 

35 Every table can only be listed on the interface if the user has permissions 

36 for it. 

37 The table permissions are configured in `tables.yaml`, under the key `perm`. 

38 For each table the minimal role is stated that can access the table. 

39 

40 If a table is not permitted, then its records are also not permitted, 

41 neither are fields in those records. 

42 

43 However, this holds for the generic interface. Individual tables and records 

44 and fields may be regulated in addition by 

45 `control.workflow.apply.WorkflowItem` conditions, 

46 which can open up and close off material. 

47 

48 Parameters 

49 ---------- 

50 auth: object 

51 The `control.auth.Auth` singleton, from which the group of the current 

52 user can be obtained. 

53 table: string 

54 The table for which permission is required. 

55 

56 Returns 

57 ------- 

58 boolean 

59 """ 

60 

61 require = G(TABLE_PERM, table) 

62 if require is None: 62 ↛ 63line 62 didn't jump to line 63, because the condition on line 62 was never true

63 return False 

64 

65 if require == UNAUTH: 

66 return True 

67 

68 group = auth.groupRep() 

69 

70 if require == NOBODY: 70 ↛ 71line 70 didn't jump to line 71, because the condition on line 70 was never true

71 return False 

72 

73 if require == AUTH: 73 ↛ 74line 73 didn't jump to line 74, because the condition on line 73 was never true

74 return group != UNAUTH 

75 

76 isSuper = group in {OFFICE, SYSTEM, ROOT} 

77 

78 if require == OFFICE: 

79 return isSuper 

80 

81 if require == SYSTEM: 81 ↛ 84line 81 didn't jump to line 84, because the condition on line 81 was never false

82 return group in {SYSTEM, ROOT} 

83 

84 if require == ROOT: 

85 return group == ROOT 

86 

87 if require == COORD: 

88 return group == COORD or isSuper 

89 

90 

91def checkPerm(require, perm): 

92 """Verify whether user's credentials match the requirements. 

93 

94 Parameters 

95 ---------- 

96 require: string 

97 The required permission level (see the perm.yaml configuration file under 

98 the key `roles`). 

99 perm: dict 

100 User attributes, in particular `group` which is the role a user can play on the 

101 basis of his/her identity. 

102 But it also contains attributes that links a user to ceertain records, e.g. the 

103 records of which (s)he is creator/editor, or National Coordinator. 

104 

105 Returns 

106 ------- 

107 boolean 

108 """ 

109 

110 if require == UNAUTH: 

111 return True 

112 

113 group = G(perm, N.group) 

114 

115 if require == NOBODY: 

116 return False 

117 

118 if require == AUTH: 

119 return group != UNAUTH 

120 

121 isSuper = group in {OFFICE, SYSTEM, ROOT} 

122 

123 if require == OFFICE: 

124 return isSuper 

125 

126 if require == SYSTEM: 126 ↛ 127line 126 didn't jump to line 127, because the condition on line 126 was never true

127 return group in {SYSTEM, ROOT} 

128 

129 if require == ROOT: 129 ↛ 130line 129 didn't jump to line 130, because the condition on line 129 was never true

130 return group == ROOT 

131 

132 if require == EDIT: 

133 return group != UNAUTH and (G(perm, N.isEdit) or isSuper) 

134 

135 if require == OWN: 

136 return group != UNAUTH and (G(perm, N.isOwn) or isSuper) 

137 

138 if require == COORD: 138 ↛ exitline 138 didn't return from function 'checkPerm', because the condition on line 138 was never false

139 return group == COORD and G(perm, N.sameCountry) or isSuper 

140 

141 

142def getPermField(table, permRecord, require, actual=None, minimum=None): 

143 """Determine read/edit permissions for a field in a record in a table. 

144 

145 !!! hint 

146 When it comes to checking a field permission, first get the 

147 record permission, then the field requirements. 

148 Then apply this function to get the result. 

149 

150 Parameters 

151 ---------- 

152 table: string 

153 Where the record resides 

154 permRecord: 

155 Permissions based on the record as a whole. 

156 require: 

157 Permissions required for reading/editing a particular field, 

158 coming from the table specific .yaml files with field specifications. 

159 actual, minimum: string, optional None 

160 If the permission is dependent on the actual value of this field, 

161 pass the actual value and the minimum value. 

162 This applies to the edit permission. 

163 The typical use case is when the field type is permissionGroup, 

164 and an edit is only allowed if the permissionGroup of the user 

165 has a greater or equal rank than/as the current value of the field. 

166 We assume the ranks will be passed, not the values themselves. 

167 

168 Returns 

169 ------- 

170 mayRead: boolean 

171 mayEdit: boolean 

172 """ 

173 

174 mayRead = None 

175 if table in ALLOW_OUR: 

176 mayRead = G(permRecord, N.isOur) or None 

177 if mayRead is None: 

178 readRequire = ( 

179 G(DEFAULT_PERM, N.read) 

180 if require is None or N.read not in require 

181 else G(require, N.read) 

182 ) 

183 mayRead = checkPerm(readRequire, permRecord) 

184 

185 editRequire = ( 

186 G(DEFAULT_PERM, N.edit) 

187 if require is None or N.edit not in require 

188 else G(require, N.edit) 

189 ) 

190 mayEdit = checkPerm(editRequire, permRecord) 

191 if mayEdit and actual is not None and minimum is not None: 

192 if G(GROUP_RANK, actual, 0) > G(GROUP_RANK, minimum, 100): 192 ↛ 193line 192 didn't jump to line 193, because the condition on line 192 was never true

193 mayEdit = False 

194 return (mayRead, mayEdit) 

195 

196 

197def permRecord(context, table, record): 

198 """Determine record permissions. 

199 

200 Various possible relationships between this user and the record will be examined. 

201 

202 Parameters 

203 ---------- 

204 

205 Returns 

206 ------- 

207 group: string 

208 Permission group of current user 

209 country: ObjectId 

210 Country to which the record belongs. Follow the detail-master chain from the 

211 record to the contrib record and read the country from the contrib. 

212 isOwn: boolean 

213 Is the record created by the current user? 

214 isEdit: boolean 

215 Is the record created by the current user or is (s)he in the list of 

216 editors of this record? 

217 sameCountry: boolean 

218 Does the record belong to the same country as the currennt user? 

219 isCoordinated: boolean 

220 Is this record under the jurisdiction of the current user as National 

221 Coordinator? 

222 isOur: boolean 

223 Is this record in the workflow of the current user? 

224 As creator/editor/reviewer/coordinator? 

225 contribId: ObjectId 

226 The id of the contrib to which this record is linked. 

227 

228 These attributes are returned as a `dict`. 

229 """ 

230 auth = context.auth 

231 user = auth.user 

232 uid = G(user, N._id) 

233 group = auth.groupRep() 

234 uCountry = G(user, N.country) 

235 

236 cRecord = {} 

237 aRecord = {} 

238 

239 if table == N.contrib: 

240 cRecord = record 

241 elif table == N.assessment: 

242 aRecord = record 

243 contribId = G(record, N.contrib) 

244 cRecord = context.getItem(N.contrib, contribId) 

245 elif table in {N.review, N.criteriaEntry, N.reviewEntry}: 

246 assessmentId = G(record, N.assessment) 

247 aRecord = context.getItem(N.assessment, assessmentId) 

248 contribId = G(aRecord, N.contrib) 

249 cRecord = context.getItem(N.contrib, contribId) 

250 

251 refCountry = G(cRecord, N.country) 

252 reviewerE = G(aRecord, N.reviewerE) 

253 reviewerF = G(aRecord, N.reviewerF) 

254 reviewers = {reviewerE, reviewerF} - {None} 

255 

256 sameCountry = refCountry is not None and refCountry == uCountry 

257 isAuth = group != UNAUTH and uid is not None 

258 isCreator = uid is not None and uid == G(record, N.creator) 

259 isEditor = uid is not None and uid in (G(record, N.editors) or set()) 

260 isCoordinated = isAuth and sameCountry and group == COORD 

261 isACreator = uid is not None and uid == G(aRecord, N.creator) 

262 isAEditor = uid is not None and uid in (G(aRecord, N.editors) or set()) 

263 isReviewer = uid is not None and uid in reviewers 

264 

265 isOur = table in ALLOW_OUR and ( 

266 isCoordinated 

267 or isCreator 

268 or isEditor 

269 or isACreator 

270 or isAEditor 

271 or isReviewer 

272 ) 

273 

274 return { 

275 N.group: group, 

276 N.country: refCountry, 

277 N.isOwn: isAuth and isCreator, 

278 N.isEdit: isAuth and (isCreator or isEditor), 

279 N.sameCountry: sameCountry, 

280 N.isCoordinated: isCoordinated, 

281 N.isOur: isOur, 

282 N.isReviewer: isReviewer, 

283 N.contribId: G(cRecord, N._id), 

284 }