001/*
002 *  Copyright 2023 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.zimbra;
017
018import java.io.UnsupportedEncodingException;
019import java.nio.charset.StandardCharsets;
020import java.security.InvalidKeyException;
021import java.security.NoSuchAlgorithmException;
022import java.util.ArrayList;
023import java.util.List;
024
025import javax.crypto.Mac;
026import javax.crypto.spec.SecretKeySpec;
027
028import org.apache.commons.codec.binary.Hex;
029import org.apache.commons.lang3.StringUtils;
030import org.apache.http.NameValuePair;
031import org.apache.http.client.methods.CloseableHttpResponse;
032import org.apache.http.client.methods.HttpGet;
033import org.apache.http.client.protocol.HttpClientContext;
034import org.apache.http.client.utils.URLEncodedUtils;
035import org.apache.http.cookie.Cookie;
036import org.apache.http.impl.client.CloseableHttpClient;
037import org.apache.http.impl.client.HttpClients;
038import org.apache.http.message.BasicNameValuePair;
039
040import org.ametys.runtime.model.checker.ItemChecker;
041import org.ametys.runtime.model.checker.ItemCheckerTestFailureException;
042
043/**
044 * Item Checker testing the connection to a zimbra server using a test user
045 */
046public class ZimbraConnectionChecker implements ItemChecker
047{
048    public void check(List<String> values) throws ItemCheckerTestFailureException
049    {
050        String url = values.get(0);
051        String preAuthToken = values.get(1);
052        String testUser = values.get(2);
053        
054        try (CloseableHttpClient httpClient = HttpClients.createDefault())
055        {
056            String qs = _computeQueryString(testUser, preAuthToken);
057            
058            HttpGet get = new HttpGet(url + "/service/preauth?" + qs);
059            HttpClientContext context = HttpClientContext.create();
060            try (CloseableHttpResponse response = httpClient.execute(get, context))
061            {
062                List<Cookie> cookies = context.getCookieStore().getCookies();
063    
064                for (Cookie cookie : cookies)
065                {
066                    if (StringUtils.equals(cookie.getName(), "ZM_AUTH_TOKEN"))
067                    {
068                        return;
069                    }
070                }
071                
072                throw new ItemCheckerTestFailureException("Failed to authenticate the test user. Server response: '" + response.getStatusLine().toString() + "'");
073            }
074        }
075        catch (Exception e)
076        {
077            throw new ItemCheckerTestFailureException(e);
078        }
079    }
080    
081    private String _computeQueryString(String zimbraUser, String secretKey) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException
082    {
083        String timestamp = String.valueOf(System.currentTimeMillis());
084        String computedPreauth = _getComputedPreauth(zimbraUser, timestamp, secretKey);
085
086        // Preauth request parameters
087        List<NameValuePair> params = new ArrayList<>();
088        params.add(new BasicNameValuePair("account", zimbraUser));
089        params.add(new BasicNameValuePair("timestamp", timestamp));
090        params.add(new BasicNameValuePair("expires", "0"));
091        params.add(new BasicNameValuePair("preauth", computedPreauth));
092        
093        // Computing query string
094        return URLEncodedUtils.format(params, StandardCharsets.UTF_8);
095    }
096    
097    private String _getComputedPreauth(String zimbraUser, String timestamp, String secretKey) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException
098    {
099        SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
100        Mac mac = Mac.getInstance("HmacSHA1");
101        mac.init(signingKey);
102
103        String data = StringUtils.join(new String[] {zimbraUser, "name", "0", timestamp}, '|');
104        byte[] rawHmac = mac.doFinal(data.getBytes());
105        byte[] hexBytes = new Hex().encode(rawHmac);
106        return new String(hexBytes, "UTF-8");
107    }
108}