1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 module hunt.shiro.realm.SimpleAccountRealm; 20 21 import hunt.shiro.realm.AuthorizingRealm; 22 23 import hunt.shiro.Exceptions; 24 import hunt.shiro.authc.AuthenticationInfo; 25 import hunt.shiro.authc.AuthenticationToken; 26 import hunt.shiro.authc.SimpleAccount; 27 import hunt.shiro.authc.UsernamePasswordToken; 28 import hunt.shiro.authz.AuthorizationInfo; 29 import hunt.shiro.authz.permission.Permission; 30 import hunt.shiro.authz.SimpleRole; 31 import hunt.shiro.subject.PrincipalCollection; 32 import hunt.shiro.util.CollectionUtils; 33 34 import hunt.collection.HashSet; 35 import hunt.collection.LinkedHashMap; 36 import hunt.collection.Map; 37 import hunt.collection.Set; 38 import hunt.Exceptions; 39 import hunt.String; 40 41 import core.sync.rwmutex; // ReentrantReadWriteLock 42 43 import std.array; 44 import std.string; 45 46 /** 47 * A simple implementation of the {@link Realm Realm} interface that 48 * uses a set of configured user accounts and roles to support authentication and authorization. Each account entry 49 * specifies the username, password, and roles for a user. Roles can also be mapped 50 * to permissions and associated with users. 51 * <p/> 52 * User accounts and roles are stored in two {@code Map}s in memory, so it is expected that the total number of either 53 * is not sufficiently large. 54 * 55 */ 56 class SimpleAccountRealm : AuthorizingRealm { 57 58 //TODO - complete JavaDoc 59 protected Map!(string, SimpleAccount) users; //username-to-SimpleAccount 60 protected Map!(string, SimpleRole) roles; //roleName-to-SimpleRole 61 protected ReadWriteMutex USERS_LOCK; 62 protected ReadWriteMutex ROLES_LOCK; 63 64 this() { 65 initialize(); 66 } 67 68 this(string name) { 69 initialize(); 70 setName(name); 71 } 72 73 private void initialize() { 74 this.users = new LinkedHashMap!(string, SimpleAccount)(); 75 this.roles = new LinkedHashMap!(string, SimpleRole)(); 76 USERS_LOCK = new ReadWriteMutex(); 77 ROLES_LOCK = new ReadWriteMutex(); 78 //SimpleAccountRealms are memory-only realms - no need for an additional cache mechanism since we're 79 //already as memory-efficient as we can be: 80 setCachingEnabled(false); 81 } 82 83 protected SimpleAccount getUser(string username) { 84 USERS_LOCK.reader().lock(); 85 try { 86 return this.users.get(username); 87 } finally { 88 USERS_LOCK.reader().unlock(); 89 } 90 } 91 92 bool accountExists(string username) { 93 return getUser(username) !is null; 94 } 95 96 void addAccount(string username, string password) { 97 addAccount(username, password, cast(string[]) null); 98 } 99 100 void addAccount(string username, string password, string[] roles...) { 101 Set!(string) roleNames = CollectionUtils.asSet(roles); 102 SimpleAccount account = new SimpleAccount(new String(username), new String(password), getName(), roleNames, cast(Set!(Permission))null); 103 add(account); 104 } 105 106 protected string getUsername(SimpleAccount account) { 107 return getUsername(account.getPrincipals()); 108 } 109 110 protected string getUsername(PrincipalCollection principals) { 111 return getAvailablePrincipal(principals).toString(); 112 } 113 114 protected void add(SimpleAccount account) { 115 string username = getUsername(account); 116 USERS_LOCK.writer().lock(); 117 try { 118 this.users.put(username, account); 119 } finally { 120 USERS_LOCK.writer().unlock(); 121 } 122 } 123 124 protected SimpleRole getRole(string rolename) { 125 ROLES_LOCK.reader().lock(); 126 try { 127 return roles.get(rolename); 128 } finally { 129 ROLES_LOCK.reader().unlock(); 130 } 131 } 132 133 bool roleExists(string name) { 134 return getRole(name) !is null; 135 } 136 137 void addRole(string name) { 138 add(new SimpleRole(name)); 139 } 140 141 protected void add(SimpleRole role) { 142 ROLES_LOCK.writer().lock(); 143 try { 144 roles.put(role.getName(), role); 145 } finally { 146 ROLES_LOCK.writer().unlock(); 147 } 148 } 149 150 protected static Set!(string) toSet(string delimited, string delimiter) { 151 if (delimited.empty) { 152 return null; 153 } 154 155 Set!(string) values = new HashSet!(string)(); 156 string[] rolenamesArray = delimited.split(delimiter); 157 foreach(string s ; rolenamesArray) { 158 string trimmed = s.strip(); 159 if (trimmed.length > 0) { 160 values.add(trimmed); 161 } 162 } 163 164 return values; 165 } 166 167 override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){ 168 UsernamePasswordToken upToken = cast(UsernamePasswordToken) token; 169 SimpleAccount account = getUser(upToken.getUsername()); 170 171 if (account !is null) { 172 173 if (account.isLocked()) { 174 throw new LockedAccountException("Account [" ~ account.toString() ~ "] is locked."); 175 } 176 if (account.isCredentialsExpired()) { 177 string msg = "The credentials for account [" ~ account.toString() ~ "] are expired"; 178 throw new ExpiredCredentialsException(msg); 179 } 180 181 } 182 183 return account; 184 } 185 186 override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 187 string username = getUsername(principals); 188 USERS_LOCK.reader().lock(); 189 try { 190 return this.users.get(username); 191 } finally { 192 USERS_LOCK.reader().unlock(); 193 } 194 } 195 }