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        List<String> allowedUserDirectoryIds = null;
067        try
068        {
069            allowedUserDirectoryIds = _getAllowedUserDirectoryIds(List.of());
070            
071        }
072        catch (MessagingConnectorException e)
073        {
074            getLogger().error("An error occured during computation of allowed user directories", e);
075            return;
076        }
077        
078        _ewsUserDirectoryIds = new ArrayList<>();
079        _graphUserDirectoryIds = new ArrayList<>();
080        
081        Boolean ews = Config.getInstance().getValue("org.ametys.plugins.exchange.ews");
082        Boolean graph = Config.getInstance().getValue("org.ametys.plugins.exchange.graph");
083
084
085        if (ews)
086        {
087            String userDirectoryIdsAsString = Config.getInstance().getValue("org.ametys.plugins.exchange.ewsuserdirectory");
088            _ewsUserDirectoryIds = _getUserDirectoryIds(userDirectoryIdsAsString, allowedUserDirectoryIds);
089        }
090
091        if (graph)
092        {
093            String userDirectoryIdsAsString = Config.getInstance().getValue("org.ametys.plugins.exchange.graphuserdirectory");
094            _graphUserDirectoryIds = _getUserDirectoryIds(userDirectoryIdsAsString, allowedUserDirectoryIds);
095        }
096        
097        if (graph && ews)
098        {
099            List<String> allowedExchangeUserDirectoryIds = _getAllowedUserDirectoryIds(_ewsUserDirectoryIds);
100            List<String> allowedGraphUserDirectoryIds = _getAllowedUserDirectoryIds(_graphUserDirectoryIds);
101            
102            if (CollectionUtils.containsAny(allowedExchangeUserDirectoryIds, allowedGraphUserDirectoryIds))
103            {
104                throw new IllegalStateException("The directory ids defined in Azure application and Exchange Server share same user directory");
105            }
106        }
107    }
108    
109    private List<String> _getUserDirectoryIds(String userDirectoryIdsAsString, List<String> allowedUserDirectoryIds)
110    {
111        List<String> ids = new ArrayList<>();
112        
113        if (StringUtils.isNotBlank(userDirectoryIdsAsString))
114        {
115            String[] userDirectoryIds = StringUtils.split(userDirectoryIdsAsString, ",");
116            
117            List<String> wrongUserDirectoryIds = new ArrayList<>();
118            for (String userDirectoryId : userDirectoryIds)
119            {
120                String userDirectoryIdTrimed = StringUtils.trim(userDirectoryId);
121                if (!allowedUserDirectoryIds.contains(userDirectoryIdTrimed))
122                {
123                    wrongUserDirectoryIds.add(userDirectoryIdTrimed);
124                }
125                else
126                {
127                    ids.add(userDirectoryIdTrimed);
128                }
129            }
130            
131            if (!wrongUserDirectoryIds.isEmpty())
132            {
133                throw new IllegalStateException("The following user directory ids defined in the Exchange configuration do not exist : " + wrongUserDirectoryIds);
134            }
135        }
136        
137        return ids;
138    }
139    
140    private List<String> _getAllowedUserDirectoryIds(List<String> userDirectoryIds)
141    {
142        if (userDirectoryIds.isEmpty())
143        {
144            List<String> allowedUserDirectoryIds = getAllowedPopulationIds()
145                    .stream()
146                    .map(populationId -> _userPopulationDAO.getUserPopulation(populationId))
147                    .map(UserPopulation::getUserDirectories)
148                    .flatMap(List::stream)
149                    .map(UserDirectory::getId)
150                    .collect(Collectors.toList());
151            return allowedUserDirectoryIds;
152        }
153        else
154        {
155            return userDirectoryIds;
156        }
157    }
158
159    private ConnectorType _getConnectorTypeByUserIdentity(UserIdentity userIdentity)
160    {
161        User user = _userManager.getUser(userIdentity);
162        UserDirectory userDirectory = user.getUserDirectory();
163        String userDirectoryId = userDirectory.getId();
164        return _getConnectorTypeByUserDirectory(userDirectoryId);
165    }
166    
167    private ConnectorType _getConnectorTypeByUserDirectory(String userDirectoryId)
168    {
169        Boolean exchange = Config.getInstance().getValue("org.ametys.plugins.exchange.ews");
170        Boolean graph = Config.getInstance().getValue("org.ametys.plugins.exchange.graph");
171        
172        if (!exchange && !graph)
173        {
174            return ConnectorType.NO_CONNECTOR_CONFIGURED;
175        }
176        else if (exchange && !graph)
177        {
178            return ConnectorType.EXCHANGE_CONNECTOR;
179        }
180        else if (!exchange && graph)
181        {
182            return ConnectorType.GRAPH_CONNECTOR;
183        }
184        else if (exchange && graph)
185        {
186            boolean idIsInExchangeList = _getAllowedUserDirectoryIds(_ewsUserDirectoryIds).contains(userDirectoryId);
187            boolean idIsInGraphList = _getAllowedUserDirectoryIds(_graphUserDirectoryIds).contains(userDirectoryId);
188            
189            if (idIsInExchangeList && !idIsInGraphList)
190            {
191                return ConnectorType.EXCHANGE_CONNECTOR;
192            }
193            else if (!idIsInExchangeList && idIsInGraphList)
194            {
195                return ConnectorType.GRAPH_CONNECTOR;
196            }
197        }
198
199        return ConnectorType.NO_CONNECTOR_ASSIGNED;
200    }
201    
202    /**
203     * Rights profiles
204     */
205    private enum ConnectorType
206    {
207        /** Graph connector */
208        GRAPH_CONNECTOR,
209        /** Exchange connector */
210        EXCHANGE_CONNECTOR,
211        /** No connector configured */
212        NO_CONNECTOR_CONFIGURED,
213        /** No connector assigned */
214        NO_CONNECTOR_ASSIGNED;
215        
216        @Override
217        public String toString()
218        {
219            return name().toLowerCase();
220        }
221    }
222    
223    @Override
224    protected List<CalendarEvent> internalGetEvents(UserIdentity userIdentity, int maxDays, int maxEvents) throws MessagingConnectorException
225    {
226        switch (_getConnectorTypeByUserIdentity(userIdentity))
227        {
228            case GRAPH_CONNECTOR:
229                return _graphConnector.internalGetEvents(userIdentity, maxDays, maxEvents);
230            case EXCHANGE_CONNECTOR:
231                return _ewsConnector.internalGetEvents(userIdentity, maxDays, maxEvents);
232            default:
233                throw new MessagingConnectorException("Cannot get events for user " + userIdentity + ": user directory is not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION);
234        }
235    }
236    
237    @Override
238    protected int internalGetEventsCount(UserIdentity userIdentity, int maxDays) throws MessagingConnectorException
239    {
240        switch (_getConnectorTypeByUserIdentity(userIdentity))
241        {
242            case GRAPH_CONNECTOR:
243                return _graphConnector.internalGetEventsCount(userIdentity, maxDays);
244            case EXCHANGE_CONNECTOR:
245                return _ewsConnector.internalGetEventsCount(userIdentity, maxDays);
246            default:
247                throw new MessagingConnectorException("Cannot get events count for user " + userIdentity + ": user directory is not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION);
248        }
249    }
250    
251    @Override
252    protected List<EmailMessage> internalGetEmails(UserIdentity userIdentity, int maxEmails) throws MessagingConnectorException
253    {
254        switch (_getConnectorTypeByUserIdentity(userIdentity))
255        {
256            case GRAPH_CONNECTOR:
257                return _graphConnector.internalGetEmails(userIdentity, maxEmails);
258            case EXCHANGE_CONNECTOR:
259                return _ewsConnector.internalGetEmails(userIdentity, maxEmails);
260            default:
261                throw new MessagingConnectorException("Cannot get emails for user " + userIdentity + ": user directory is not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION);
262        }
263    }
264    
265    @Override
266    protected int internalGetEmailsCount(UserIdentity userIdentity) throws MessagingConnectorException
267    {
268        switch (_getConnectorTypeByUserIdentity(userIdentity))
269        {
270            case GRAPH_CONNECTOR:
271                return _graphConnector.internalGetEmailsCount(userIdentity);
272            case EXCHANGE_CONNECTOR:
273                return _ewsConnector.internalGetEmailsCount(userIdentity);
274            default:
275                throw new MessagingConnectorException("Cannot get emails count for user " + userIdentity + ": user directory is not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION);
276        }
277    }
278    
279    @Override
280    public boolean supportUserCredential(UserIdentity userIdentity)
281    {
282        switch (_getConnectorTypeByUserIdentity(userIdentity))
283        {
284            case GRAPH_CONNECTOR:
285                return _graphConnector.supportUserCredential(userIdentity);
286            case EXCHANGE_CONNECTOR:
287                return _ewsConnector.supportUserCredential(userIdentity);
288            default:
289                throw new MessagingConnectorException("Cannot get support for credential for user " + userIdentity + ": user directory is not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION);
290        }
291    }
292}