001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.shiro.realm;
020    
021    import org.apache.shiro.authc.*;
022    import org.apache.shiro.authz.AuthorizationInfo;
023    import org.apache.shiro.authz.SimpleRole;
024    import org.apache.shiro.subject.PrincipalCollection;
025    import org.apache.shiro.util.CollectionUtils;
026    
027    import java.util.HashSet;
028    import java.util.LinkedHashMap;
029    import java.util.Map;
030    import java.util.Set;
031    import java.util.concurrent.locks.Lock;
032    import java.util.concurrent.locks.ReadWriteLock;
033    import java.util.concurrent.locks.ReentrantReadWriteLock;
034    
035    /**
036     * A simple implementation of the {@link Realm Realm} interface that
037     * uses a set of configured user accounts and roles to support authentication and authorization.  Each account entry
038     * specifies the username, password, and roles for a user.  Roles can also be mapped
039     * to permissions and associated with users.
040     * <p/>
041     * User accounts and roles are stored in two {@code Map}s in memory, so it is expected that the total number of either
042     * is not sufficiently large.
043     *
044     * @since 0.1
045     */
046    public class SimpleAccountRealm extends AuthorizingRealm {
047    
048        //TODO - complete JavaDoc
049        protected final Map<String, SimpleAccount> users; //username-to-SimpleAccount
050        protected final Map<String, SimpleRole> roles; //roleName-to-SimpleRole
051        protected final ReadWriteLock USERS_LOCK;
052        protected final ReadWriteLock ROLES_LOCK;
053    
054        public SimpleAccountRealm() {
055            this.users = new LinkedHashMap<String, SimpleAccount>();
056            this.roles = new LinkedHashMap<String, SimpleRole>();
057            USERS_LOCK = new ReentrantReadWriteLock();
058            ROLES_LOCK = new ReentrantReadWriteLock();
059            //SimpleAccountRealms are memory-only realms - no need for an additional cache mechanism since we're
060            //already as memory-efficient as we can be:
061            setCachingEnabled(false);
062        }
063    
064        public SimpleAccountRealm(String name) {
065            this();
066            setName(name);
067        }
068    
069        protected SimpleAccount getUser(String username) {
070            USERS_LOCK.readLock().lock();
071            try {
072                return this.users.get(username);
073            } finally {
074                USERS_LOCK.readLock().unlock();
075            }
076        }
077    
078        public boolean accountExists(String username) {
079            return getUser(username) != null;
080        }
081    
082        public void addAccount(String username, String password) {
083            addAccount(username, password, (String[]) null);
084        }
085    
086        public void addAccount(String username, String password, String... roles) {
087            Set<String> roleNames = CollectionUtils.asSet(roles);
088            SimpleAccount account = new SimpleAccount(username, password, getName(), roleNames, null);
089            add(account);
090        }
091    
092        protected String getUsername(SimpleAccount account) {
093            return getUsername(account.getPrincipals());
094        }
095    
096        protected String getUsername(PrincipalCollection principals) {
097            return getAvailablePrincipal(principals).toString();
098        }
099    
100        protected void add(SimpleAccount account) {
101            String username = getUsername(account);
102            USERS_LOCK.writeLock().lock();
103            try {
104                this.users.put(username, account);
105            } finally {
106                USERS_LOCK.writeLock().unlock();
107            }
108        }
109    
110        protected SimpleRole getRole(String rolename) {
111            ROLES_LOCK.readLock().lock();
112            try {
113                return roles.get(rolename);
114            } finally {
115                ROLES_LOCK.readLock().unlock();
116            }
117        }
118    
119        public boolean roleExists(String name) {
120            return getRole(name) != null;
121        }
122    
123        public void addRole(String name) {
124            add(new SimpleRole(name));
125        }
126    
127        protected void add(SimpleRole role) {
128            ROLES_LOCK.writeLock().lock();
129            try {
130                roles.put(role.getName(), role);
131            } finally {
132                ROLES_LOCK.writeLock().unlock();
133            }
134        }
135    
136        protected static Set<String> toSet(String delimited, String delimiter) {
137            if (delimited == null || delimited.trim().equals("")) {
138                return null;
139            }
140    
141            Set<String> values = new HashSet<String>();
142            String[] rolenamesArray = delimited.split(delimiter);
143            for (String s : rolenamesArray) {
144                String trimmed = s.trim();
145                if (trimmed.length() > 0) {
146                    values.add(trimmed);
147                }
148            }
149    
150            return values;
151        }
152    
153        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
154            UsernamePasswordToken upToken = (UsernamePasswordToken) token;
155            SimpleAccount account = getUser(upToken.getUsername());
156    
157            if (account != null) {
158    
159                if (account.isLocked()) {
160                    throw new LockedAccountException("Account [" + account + "] is locked.");
161                }
162                if (account.isCredentialsExpired()) {
163                    String msg = "The credentials for account [" + account + "] are expired";
164                    throw new ExpiredCredentialsException(msg);
165                }
166    
167            }
168    
169            return account;
170        }
171    
172        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
173            String username = getUsername(principals);
174            USERS_LOCK.readLock().lock();
175            try {
176                return this.users.get(username);
177            } finally {
178                USERS_LOCK.readLock().unlock();
179            }
180        }
181    }