001/*
002 *  Copyright 2016 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.core.impl.user.directory;
017
018import java.util.Collection;
019import java.util.Comparator;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023import java.util.stream.Collectors;
024
025import org.apache.avalon.framework.component.Component;
026import org.apache.commons.lang3.StringUtils;
027
028import org.ametys.core.user.User;
029import org.ametys.core.user.UserIdentity;
030import org.ametys.core.user.directory.NotUniqueUserException;
031import org.ametys.core.user.directory.UserDirectory;
032import org.ametys.runtime.plugin.component.AbstractLogEnabled;
033
034/**
035 * This implementation only uses predefined users 
036 */
037public class StaticUserDirectory extends AbstractLogEnabled implements UserDirectory, Component
038{
039    private static final String __PARAM_USERS = "runtime.users.static.users";
040    
041    private Map<String, User> _staticUsers;
042
043    private String _udModelId;
044
045    private Map<String, Object> _paramValues;
046
047    private String _populationId;
048
049    private String _label;
050
051    private String _id;
052
053    private boolean _grantAllCredentials = true;
054    
055    public String getId()
056    {
057        return _id;
058    }
059    
060    public String getLabel()
061    {
062        return _label;
063    }
064    
065    @Override
066    public void init(String id, String udModelId, Map<String, Object> paramValues, String label)
067    {
068        _id = id;
069        _udModelId = udModelId;
070        _staticUsers = new HashMap<>();
071        _label = label;
072        
073        _paramValues = paramValues;
074        
075        String usersAsText = (String) paramValues.get(__PARAM_USERS);
076        for (String userLine : usersAsText.split("\\n"))
077        {
078            User user = _createUser(userLine);
079            _staticUsers.put(user.getIdentity().getLogin(), user);
080        }
081    }
082    
083    @Override
084    public void setPopulationId(String populationId)
085    {
086        _populationId = populationId;
087    }
088    
089    @Override
090    public String getPopulationId()
091    {
092        return _populationId;
093    }
094    
095    @Override
096    public Map<String, Object> getParameterValues()
097    {
098        return _paramValues;
099    }
100    
101    /**
102     * Set to false to disallow any user to be authenticated by its credentials
103     * @param grantAllCredentials true if the directory should grant all call to checkCredentials
104     */
105    public void setGrantAllCredentials(boolean grantAllCredentials)
106    {
107        _grantAllCredentials  = grantAllCredentials;
108    }
109    
110    @Override
111    public String getUserDirectoryModelId()
112    {
113        return _udModelId;
114    }
115    
116    @Override
117    public Collection<User> getUsers()
118    {
119        return _staticUsers.values();
120    }
121
122    @Override
123    public List<User> getUsers(int count, int offset, Map<String, Object> parameters)
124    {
125        String pattern = StringUtils.defaultIfEmpty((String) parameters.get("pattern"), "");
126        boolean sort = (boolean) parameters.getOrDefault("sort", false);
127        int boundedCount = count >= 0 ? count : Integer.MAX_VALUE;
128        int boundedOffset = offset >= 0 ? offset : 0;
129        
130        List<User> result = _staticUsers.values().stream().filter(user-> _isLike(user, pattern)).collect(Collectors.toList());
131        if (sort)
132        {
133            result.sort(Comparator.comparing(User::getSortableName));
134        }
135        
136        int toIndex = boundedOffset + boundedCount; 
137        toIndex = toIndex > result.size() ? result.size() : toIndex;
138        return result.subList(boundedOffset, toIndex);
139    }
140
141    @Override
142    public User getUser(String login)
143    {
144        return _staticUsers.get(login);
145    }
146    
147    public User getUserByEmail(String email) throws NotUniqueUserException
148    {
149        if (StringUtils.isBlank(email))
150        {
151            return null;
152        }
153        
154        List<User> users = _staticUsers.values().stream().filter(user-> email.equalsIgnoreCase(user.getEmail())).collect(Collectors.toList());
155        
156        if (users.size() == 1)
157        {
158            return users.get(0);
159        }
160        else if (users.isEmpty())
161        {
162            return null;
163        }
164        else
165        {
166            throw new NotUniqueUserException("Find " + users.size() + " users matching the email " + email);
167        }
168    }
169
170    @Override
171    public boolean checkCredentials(String login, String password)
172    {
173        return _grantAllCredentials && _staticUsers.containsKey(login);
174    }
175    
176    private User _createUser(String userLine)
177    {
178        String[] userInfo = userLine.split(":");
179        
180        String login = null;
181        String lastName = null;
182        String firstName = null;
183        String email = null;
184        
185        switch (userInfo.length)
186        {
187            case 4:
188                email = userInfo[3];
189                //fall through
190            case 3:
191                firstName = userInfo[2];
192                //fall through
193            case 2:
194                lastName = userInfo[1];
195                login = userInfo[0];
196                break;
197            case 1:
198                login = userInfo[0];
199                lastName = login;
200                break;
201            default:
202                getLogger().error("Error while reading StaticUserDirectory users, cannot create a user with the data from line {}", userLine);
203                break;
204        }
205        
206        return new User(new UserIdentity(login, _populationId), lastName, firstName, email, this);
207    }
208    
209    private boolean _isLike(User user, String pattern)
210    {
211        String modifiedPattern = StringUtils.stripAccents(pattern);
212        return StringUtils.containsIgnoreCase(StringUtils.stripAccents(user.getIdentity().getLogin()), modifiedPattern) 
213                || StringUtils.containsIgnoreCase(StringUtils.stripAccents(user.getFirstName()), modifiedPattern)
214                || StringUtils.containsIgnoreCase(StringUtils.stripAccents(user.getLastName()), modifiedPattern);
215    }
216}