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 */
019package org.apache.shiro.realm;
020
021import org.apache.shiro.authc.*;
022import org.apache.shiro.authz.AuthorizationInfo;
023import org.apache.shiro.authz.SimpleRole;
024import org.apache.shiro.subject.PrincipalCollection;
025import org.apache.shiro.util.CollectionUtils;
026
027import java.util.HashSet;
028import java.util.LinkedHashMap;
029import java.util.Map;
030import java.util.Set;
031import java.util.concurrent.locks.Lock;
032import java.util.concurrent.locks.ReadWriteLock;
033import 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 */
046public 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}