1 /** 2 * Copyright © DiamondMVC 2018 3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 */ 6 module diamond.authentication.roles; 7 8 import diamond.core.apptype; 9 10 static if (isWeb) 11 { 12 import diamond.errors.checks; 13 import diamond.authentication.permissions; 14 import diamond.http; 15 16 /// The storage key for the authentication roles. 17 private static const __gshared roleStorageKey = "__D_AUTH_ROLE"; 18 19 /// The roles. 20 private static __gshared Role[string] _roles; 21 22 /// The default role. 23 package(diamond) static __gshared Role defaultRole; 24 25 /// Gets a boolean determining whether there are roles or not. 26 @property bool hasRoles() { return _roles.length > 0 && defaultRole !is null; } 27 28 /// Wrapper around a role. 29 final class Role 30 { 31 private: 32 /// The name. 33 string _name; 34 35 /// The permissions. 36 Permission[string] _permissions; 37 38 /// The parent role. 39 Role _parent; 40 41 /** 42 * Creates a new role. 43 * Params: 44 * name = The name of the role. 45 */ 46 this(string name) 47 { 48 _name = name; 49 } 50 51 /** 52 * Creates a new role. 53 * Params: 54 * name = The name of the role. 55 * parent = The parent role. 56 */ 57 this(string name, Role parent) 58 { 59 _name = name; 60 _parent = parent; 61 } 62 63 public: 64 final: 65 @property 66 { 67 /// Gets the name. 68 string name() { return _name; } 69 70 /// Gets the parent. 71 Role parent() { return _parent; } 72 } 73 74 /** 75 * Adds a permission to the role. 76 * Params: 77 * resource = The resource. 78 * readAccess = Boolean determining whether the role has read-access. 79 * writeAccess = Boolean determining whether the role has write-access. 80 * updateAccess = Boolean determining whether the role has update-access. 81 * deleteAccess = Boolean determining whether the role has delete-access. 82 * Returns: 83 * The role, allowing the function to be chained. 84 */ 85 Role addPermission 86 ( 87 string resource, 88 bool readAccess, bool writeAccess, 89 bool updateAccess, bool deleteAccess 90 ) 91 { 92 enforce(resource, "Found no resource to create permisions for."); 93 94 import std..string : strip; 95 import std.array : replace; 96 97 resource = resource.strip(); 98 99 if (!resource.replace("/", "").strip().length) 100 { 101 import diamond.core : webConfig, firstToLower; 102 103 resource = webConfig.homeRoute.firstToLower(); 104 } 105 106 if (resource[0] == '/') 107 { 108 resource = resource[1 .. $]; 109 } 110 111 if (resource[$-1] == '/') 112 { 113 resource = resource[0 .. $-1]; 114 } 115 116 _permissions[resource] = new Permission(resource, 117 readAccess, writeAccess, 118 updateAccess, deleteAccess 119 ); 120 121 return this; 122 } 123 124 /** 125 * Checks whether the role has permission to a specific resource. 126 * Params: 127 * resource = The resource. 128 * permission = The permission. 129 * Returns: 130 * True if the role has permission, false otherwise. 131 */ 132 bool hasPermission(string resource, PermissionType permission) 133 { 134 enforce(resource, "Found no resource to check permisions for."); 135 136 import std..string : strip; 137 import std.array : replace; 138 139 resource = resource.strip(); 140 141 if (!resource || !resource.replace("/", "").strip().length) 142 { 143 import diamond.core : webConfig, firstToLower; 144 145 resource = webConfig.homeRoute.firstToLower(); 146 } 147 148 if (resource[0] == '/') 149 { 150 resource = resource[1 .. $]; 151 } 152 153 if (resource[$-1] == '/') 154 { 155 resource = resource[0 .. $-1]; 156 } 157 158 auto permissionResource = _permissions.get(resource, null); 159 160 if (!permissionResource) 161 { 162 if (_parent) 163 { 164 return _parent.hasPermission(resource, permission); 165 } 166 167 return defaultPermission; 168 } 169 170 final switch (permission) 171 { 172 case PermissionType.readAccess: return permissionResource.readAccess; 173 case PermissionType.writeAccess: return permissionResource.writeAccess; 174 case PermissionType.updateAccess: return permissionResource.updateAccess; 175 case PermissionType.deleteAccess: return permissionResource.deleteAccess; 176 } 177 } 178 } 179 180 /** 181 * Gets a role by its name. 182 * Params: 183 * name = The name of the role. 184 * Returns: 185 * The role if found, defaultRole otherwise. 186 */ 187 Role getRole(string name) 188 { 189 return _roles.get(name, defaultRole); 190 } 191 192 /** 193 * Gets a role by its request. 194 * Params: 195 * client = The client. 196 * Returns: 197 * The role if existing, defaultRole otherwise. 198 */ 199 Role getRole(HttpClient client) 200 { 201 enforce(client, "No client specified."); 202 203 return client.getContext!Role(roleStorageKey, defaultRole); 204 } 205 206 /** 207 * Sets the role. 208 * Params: 209 * client = The client. 210 * role = The role. 211 */ 212 package(diamond.authentication) void setRole(HttpClient client, Role role) 213 { 214 enforce(client, "No client specified."); 215 enforce(role, "No role specified."); 216 217 client.addContext(roleStorageKey, role); 218 } 219 220 /** 221 * Sets the role from the session. 222 * Params: 223 * client = The client. 224 * defaultIsInvalid = Boolean determining whether the default role is an invalid role. 225 * Returns: 226 * Returns true if the role was set from the session. 227 */ 228 package(diamond.authentication) bool setRoleFromSession 229 ( 230 HttpClient client, 231 bool defaultIsInvalid 232 ) 233 { 234 enforce(client, "No client specified."); 235 236 auto sessionRole = client.session.getValue!string(roleStorageKey, null); 237 238 if (sessionRole !is null) 239 { 240 auto role = getRole(sessionRole); 241 242 if (defaultIsInvalid && role == defaultRole) 243 { 244 return false; 245 } 246 247 setRole(client, role); 248 return true; 249 } 250 251 return false; 252 } 253 254 /** 255 * Sets the session role. 256 * Params: 257 * client = The client. 258 * role = The role. 259 */ 260 package(diamond.authentication) void setSessionRole 261 ( 262 HttpClient client, Role role 263 ) 264 { 265 client.session.setValue(roleStorageKey, role.name); 266 } 267 268 /** 269 * Sets the default role. 270 * Params: 271 * role = The role. 272 */ 273 void setDefaultRole(Role role) 274 { 275 enforce(role, "Cannot set the default role to null."); 276 277 defaultRole = role; 278 } 279 280 /** 281 * Adds a new role. 282 * Params: 283 * name = The name of the role. 284 * Returns: 285 * The role. 286 */ 287 Role addRole(string name) 288 { 289 auto role = new Role(name); 290 291 _roles[role.name] = role; 292 293 return role; 294 } 295 296 /** 297 * Adds a new role. 298 * Params: 299 * name = The name of the role. 300 * parent = The parent role. 301 * Returns: 302 * The role. 303 */ 304 Role addRole(string name, Role parent) 305 { 306 auto role = new Role(name, parent); 307 308 _roles[role.name] = role; 309 310 return role; 311 } 312 }