001/*
002 *  Copyright 2021 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.exchange;
017
018import java.util.ArrayList;
019import java.util.List;
020import java.util.stream.Collectors;
021
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024import org.apache.commons.collections.CollectionUtils;
025import org.apache.commons.lang3.StringUtils;
026
027import org.ametys.core.user.User;
028import org.ametys.core.user.UserIdentity;
029import org.ametys.core.user.UserManager;
030import org.ametys.core.user.directory.UserDirectory;
031import org.ametys.core.user.population.UserPopulation;
032import org.ametys.plugins.messagingconnector.AbstractMessagingConnector;
033import org.ametys.plugins.messagingconnector.CalendarEvent;
034import org.ametys.plugins.messagingconnector.EmailMessage;
035import org.ametys.plugins.messagingconnector.MessagingConnectorException;
036import org.ametys.runtime.config.Config;
037
038/**
039 * The connector used by the messaging connector plugin when the exchange mail server is used. 
040 */
041public class ExchangeConnector extends AbstractMessagingConnector
042{
043    private UserManager _userManager;
044    
045    private EWSConnector _ewsConnector;
046    private GraphConnector _graphConnector;
047
048    private List<String> _ewsUserDirectoryIds;
049    private List<String> _graphUserDirectoryIds;
050    
051    @Override
052    public void service(ServiceManager manager) throws ServiceException
053    {
054        super.service(manager);
055        
056        _userManager = (UserManager) manager.lookup(UserManager.ROLE);
057        _ewsConnector = (EWSConnector) manager.lookup(EWSConnector.INNER_ROLE);
058        _graphConnector = (GraphConnector) manager.lookup(GraphConnector.INNER_ROLE);
059    }   
060
061    @Override
062    public void initialize()
063    {
064        super.initialize();
065        
066        _ewsUserDirectoryIds = new ArrayList<>();
067        _graphUserDirectoryIds = new ArrayList<>();
068        
069        Boolean ews = Config.getInstance().getValue("org.ametys.plugins.exchange.ews");
070        Boolean graph = Config.getInstance().getValue("org.ametys.plugins.exchange.graph");
071        
072        List<String> allowedUserDirectoryIds = getAllowedPopulationIds()
073                .stream()
074                .map(populationId -> _userPopulationDAO.getUserPopulation(populationId))
075                .map(UserPopulation::getUserDirectories)
076                .flatMap(List::stream)
077                .map(UserDirectory::getId)
078                .collect(Collectors.toList());
079
080        if (ews)
081        {
082            String userDirectoryIdsAsString = Config.getInstance().getValue("org.ametys.plugins.exchange.ewsuserdirectory");
083            _ewsUserDirectoryIds = _getUserDirectoryIds(userDirectoryIdsAsString, allowedUserDirectoryIds);
084        }
085
086        if (graph)
087        {
088            String userDirectoryIdsAsString = Config.getInstance().getValue("org.ametys.plugins.exchange.graphuserdirectory");
089            _graphUserDirectoryIds = _getUserDirectoryIds(userDirectoryIdsAsString, allowedUserDirectoryIds);
090        }
091        
092        if (graph && ews)
093        {
094            List<String> allowedExchangeUserDirectoryIds = _getAllowedUserDirectoryIds(_ewsUserDirectoryIds);
095            List<String> allowedGraphUserDirectoryIds = _getAllowedUserDirectoryIds(_graphUserDirectoryIds);
096            
097            if (CollectionUtils.containsAny(allowedExchangeUserDirectoryIds, allowedGraphUserDirectoryIds))
098            {
099                throw new IllegalStateException("The directory ids defined in Azure application and Exchange Server share same user directory");
100            }
101        }
102    }
103    
104    private List<String> _getUserDirectoryIds(String userDirectoryIdsAsString, List<String> allowedUserDirectoryIds)
105    {
106        List<String> ids = new ArrayList<>();
107        
108        if (StringUtils.isNotBlank(userDirectoryIdsAsString))
109        {
110            String[] userDirectoryIds = StringUtils.split(userDirectoryIdsAsString, ",");
111            
112            List<String> wrongUserDirectoryIds = new ArrayList<>();
113            for (String userDirectoryId : userDirectoryIds)
114            {
115                String userDirectoryIdTrimed = StringUtils.trim(userDirectoryId);
116                if (!allowedUserDirectoryIds.contains(userDirectoryIdTrimed))
117                {
118                    wrongUserDirectoryIds.add(userDirectoryIdTrimed);
119                }
120                else
121                {
122                    ids.add(userDirectoryIdTrimed);
123                }
124            }
125            
126            if (!wrongUserDirectoryIds.isEmpty())
127            {
128                throw new IllegalStateException("The following user directory ids defined in the Exchange configuration do not exist : " + wrongUserDirectoryIds);
129            }
130        }
131        
132        return ids;
133    }
134    
135    private List<String> _getAllowedUserDirectoryIds(List<String> userDirectoryIds)
136    {
137        if (userDirectoryIds.isEmpty())
138        {
139            List<String> allowedUserDirectoryIds = getAllowedPopulationIds()
140                    .stream()
141                    .map(populationId -> _userPopulationDAO.getUserPopulation(populationId))
142                    .map(UserPopulation::getUserDirectories)
143                    .flatMap(List::stream)
144                    .map(UserDirectory::getId)
145                    .collect(Collectors.toList());
146            return allowedUserDirectoryIds;
147        }
148        else
149        {
150            return userDirectoryIds;
151        }
152    }
153
154    private ConnectorType _getConnectorTypeByUserIdentity(UserIdentity userIdentity)
155    {
156        User user = _userManager.getUser(userIdentity);
157        UserDirectory userDirectory = user.getUserDirectory();
158        String userDirectoryId = userDirectory.getId();
159        return _getConnectorTypeByUserDirectory(userDirectoryId);
160    }
161    
162    private ConnectorType _getConnectorTypeByUserDirectory(String userDirectoryId)
163    {
164        Boolean exchange = Config.getInstance().getValue("org.ametys.plugins.exchange.ews");
165        Boolean graph = Config.getInstance().getValue("org.ametys.plugins.exchange.graph");
166        
167        if (!exchange && !graph)
168        {
169            return null;
170        }
171        else if (exchange && !graph)
172        {
173            return ConnectorType.EXCHANGE_CONNECTOR;
174        }
175        else if (!exchange && graph)
176        {
177            return ConnectorType.GRAPH_CONNECTOR;
178        }
179        else if (exchange && graph)
180        {
181            boolean idIsInExchangeList = _getAllowedUserDirectoryIds(_ewsUserDirectoryIds).contains(userDirectoryId);
182            boolean idIsInGraphList = _getAllowedUserDirectoryIds(_graphUserDirectoryIds).contains(userDirectoryId);
183            
184            if (idIsInExchangeList && !idIsInGraphList)
185            {
186                return ConnectorType.EXCHANGE_CONNECTOR;
187            }
188            else if (!idIsInExchangeList && idIsInGraphList)
189            {
190                return ConnectorType.GRAPH_CONNECTOR;
191            }
192        }
193        
194        return null;
195    }
196    
197    /**
198     * Rights profiles
199     */
200    private enum ConnectorType
201    {
202        /** Graph connector */
203        GRAPH_CONNECTOR,
204        /** Exchange connector */
205        EXCHANGE_CONNECTOR;
206        
207        @Override
208        public String toString()
209        {
210            return name().toLowerCase();
211        }
212    }
213    
214    @Override
215    protected List<CalendarEvent> internalGetEvents(UserIdentity userIdentity, int maxDays, int maxEvents) throws MessagingConnectorException
216    {
217        switch (_getConnectorTypeByUserIdentity(userIdentity))
218        {
219            case GRAPH_CONNECTOR:
220                return _graphConnector.internalGetEvents(userIdentity, maxDays, maxEvents);
221            case EXCHANGE_CONNECTOR:
222                return _ewsConnector.internalGetEvents(userIdentity, maxDays, maxEvents);
223            default:
224                throw new MessagingConnectorException("Cannot get events for user " + userIdentity + ": user directory is not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION);
225        }
226    }
227    
228    @Override
229    protected int internalGetEventsCount(UserIdentity userIdentity, int maxDays) throws MessagingConnectorException
230    {
231        switch (_getConnectorTypeByUserIdentity(userIdentity))
232        {
233            case GRAPH_CONNECTOR:
234                return _graphConnector.internalGetEventsCount(userIdentity, maxDays);
235            case EXCHANGE_CONNECTOR:
236                return _ewsConnector.internalGetEventsCount(userIdentity, maxDays);
237            default:
238                throw new MessagingConnectorException("Cannot get events count for user " + userIdentity + ": user directory is not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION);
239        }
240    }
241    
242    @Override
243    protected List<EmailMessage> internalGetEmails(UserIdentity userIdentity, int maxEmails) throws MessagingConnectorException
244    {
245        switch (_getConnectorTypeByUserIdentity(userIdentity))
246        {
247            case GRAPH_CONNECTOR:
248                return _graphConnector.internalGetEmails(userIdentity, maxEmails);
249            case EXCHANGE_CONNECTOR:
250                return _ewsConnector.internalGetEmails(userIdentity, maxEmails);
251            default:
252                throw new MessagingConnectorException("Cannot get emails for user " + userIdentity + ": user directory is not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION);
253        }
254    }
255    
256    @Override
257    protected int internalGetEmailsCount(UserIdentity userIdentity) throws MessagingConnectorException
258    {
259        switch (_getConnectorTypeByUserIdentity(userIdentity))
260        {
261            case GRAPH_CONNECTOR:
262                return _graphConnector.internalGetEmailsCount(userIdentity);
263            case EXCHANGE_CONNECTOR:
264                return _ewsConnector.internalGetEmailsCount(userIdentity);
265            default:
266                throw new MessagingConnectorException("Cannot get emails count for user " + userIdentity + ": user directory is not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION);
267        }
268    }
269    
270    @Override
271    public boolean supportUserCredential(UserIdentity userIdentity)
272    {
273        switch (_getConnectorTypeByUserIdentity(userIdentity))
274        {
275            case GRAPH_CONNECTOR:
276                return _graphConnector.supportUserCredential(userIdentity);
277            case EXCHANGE_CONNECTOR:
278                return _ewsConnector.supportUserCredential(userIdentity);
279            default:
280                throw new MessagingConnectorException("Cannot get support for credential for user " + userIdentity + ": user directory is not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION);
281        }
282    }
283}