001/* 
002 * JKNIV, whinstone one contract to access your database.
003 * 
004 * Copyright (C) 2017, the original author or authors.
005 *
006 * This library is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 2.1 of the License.
010 * 
011 * This library is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 * 
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with this library; if not, write to the Free Software Foundation, Inc., 
018 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
019 */
020package net.sf.jkniv.whinstone.couchdb;
021
022import java.io.IOException;
023import java.io.UnsupportedEncodingException;
024import java.net.URI;
025import java.net.URISyntaxException;
026import java.nio.charset.Charset;
027import java.util.Date;
028import java.util.concurrent.TimeUnit;
029
030import org.apache.http.Consts;
031import org.apache.http.Header;
032import org.apache.http.HeaderIterator;
033import org.apache.http.client.ClientProtocolException;
034import org.apache.http.client.methods.CloseableHttpResponse;
035import org.apache.http.client.methods.HttpPost;
036import org.apache.http.client.utils.URIBuilder;
037import org.apache.http.entity.StringEntity;
038import org.apache.http.impl.client.CloseableHttpClient;
039import org.apache.http.impl.client.HttpClients;
040import org.slf4j.Logger;
041import org.slf4j.LoggerFactory;
042
043import net.sf.jkniv.exception.HandlerException;
044import net.sf.jkniv.sqlegance.RepositoryException;
045
046public class CouchDbAuthenticate
047{
048    private static final Logger    LOG         = LoggerFactory.getLogger(CouchDbAuthenticate.class);
049    private static final String    AUTH_COOKIE = "Set-Cookie";
050    private final HandlerException handlerException;
051    private Date startSession;
052    private long sessionTimeout;
053    private String url;
054    private String username;
055    private String password;
056    private String cookieSession;
057
058    /**
059     * Build a new Authentication with 10 minutes to expire after authenticate.
060     * @param url Uniform Resource Locator pointing to CouchDb instance
061     * @param username couchDb user name
062     * @param password couchDb password
063     */
064    public CouchDbAuthenticate(String url, String username, String password)
065    {
066        super();
067        this.url = url;
068        this.username = username;
069        this.password = password;
070        this.sessionTimeout = TimeUnit.MINUTES.toMillis(10L);
071        this.handlerException = new HandlerException(RepositoryException.class, "Cannot connect to couchdb [%s]");
072        configHanlerException();
073    }
074    
075    private void configHanlerException()
076    {
077        // ClientProtocolException | URISyntaxException | UnsupportedEncodingException | IOException
078        handlerException.config(ClientProtocolException.class, "Error at HTTP protocol [%s]");
079        handlerException.config(URISyntaxException.class, "Error to parser URI [%s]");
080        handlerException.config(UnsupportedEncodingException.class, "Character encoding unsupported");
081        handlerException.config(IOException.class, "Error from I/O json content [%s]");
082    }
083    
084    /**
085     * Retrieve the cookie session for user authenticated
086     * @return cookie session number for this user authenticated, default session time it's for 10 minutes
087     */
088    public String authenticate()
089    {
090        String token = "";
091        CloseableHttpResponse response = null;
092        try
093        {
094            URIBuilder uri = new URIBuilder(new URI(url)).setCharset(Charset.defaultCharset()).setPath("_session");
095            CloseableHttpClient httpclient = HttpClients.createDefault();
096            
097            HttpPost httpPost = new HttpPost(uri.build());
098            httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
099            StringEntity xmlEntity = new StringEntity("name=" + username + "&password=" + password, Consts.UTF_8); // TODO config charset for HTTP body
100            httpPost.setEntity(xmlEntity);
101            
102            response = httpclient.execute(httpPost);
103            LOG.debug(response.getStatusLine().toString());
104            int statusCode = response.getStatusLine().getStatusCode();
105            if (statusCode == 200)
106            {
107                this.startSession = new Date();
108                HeaderIterator it = response.headerIterator();
109                while (it.hasNext())
110                {
111                    Header header = it.nextHeader();
112                    if (AUTH_COOKIE.equals(header.getName()))
113                    {
114                        token = header.getValue().split(";")[0];
115                        this.cookieSession = token;
116                    }
117                }
118            }
119            else if (statusCode == 401)
120            {
121                throw new RepositoryException("Access denied, unauthorized for user ["+username+"] and url ["+url+"]");
122            }
123            else
124                throw new RepositoryException(
125                        "Http code[" + statusCode + "] " + response.getStatusLine().getReasonPhrase());
126        }
127        // ClientProtocolException | URISyntaxException | UnsupportedEncodingException | IOException 
128        catch (Exception ex)
129        {
130            this.handlerException.handle(ex, url);
131        }
132        finally
133        {
134            if (response != null)
135            {
136                try
137                {
138                    response.close();
139                }
140                catch (IOException e)
141                {
142                    this.handlerException.handle(e, url);
143                }
144            }
145        }
146        return token;
147    }
148    
149    public String getCookieSession()
150    {
151        return cookieSession;
152    }
153    
154    public boolean isExpired() 
155    {
156        if (this.startSession == null)
157            return true;
158        
159        return (new Date().getTime() > (this.startSession.getTime()+sessionTimeout));
160    }
161    
162    public void keepAlive()
163    {
164        this.startSession = new Date();
165    }
166}