001    /*
002     * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org
003     * Copyright (C) 2011 NightLabs Consulting GmbH
004     *
005     * This program is free software: you can redistribute it and/or modify
006     * it under the terms of the GNU Affero General Public License as
007     * published by the Free Software Foundation, either version 3 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU Affero General Public License for more details.
014     *
015     * You should have received a copy of the GNU Affero General Public License
016     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017     */
018    package org.cumulus4j.keymanager;
019    
020    import java.util.Arrays;
021    import java.util.Date;
022    
023    import org.cumulus4j.keymanager.back.shared.IdentifierUtil;
024    import org.slf4j.Logger;
025    import org.slf4j.LoggerFactory;
026    
027    /**
028     * <p>
029     * Session to control and restrict the key exchange with the application server.
030     * </p>
031     * <p>
032     * In order to protect the keys as well as possible, keys can only be requested from an application
033     * server within the scope of a so-called crypto-session. The client controls when to open/close a crypto-session
034     * and when to allow keys to be transferred. Key transfer is only possible while a session is {@link #setReleased(boolean) unlocked}.
035     * </p>
036     * <p>
037     * This is not API! Use the classes and interfaces provided by <code>org.cumulus4j.keymanager.api</code> instead.
038     * </p>
039     *
040     * @author Marco หงุ่ยตระกูล-Schulze - marco at nightlabs dot de
041     */
042    public class Session
043    {
044            private static final Logger logger = LoggerFactory.getLogger(Session.class);
045    
046            private SessionManager sessionManager;
047    
048            protected Session(SessionManager sessionManager, String userName, char[] password)
049            {
050                    if (sessionManager == null)
051                            throw new IllegalArgumentException("sessionManager == null");
052    
053                    if (userName == null)
054                            throw new IllegalArgumentException("userName == null");
055    
056                    if (password == null)
057                            throw new IllegalArgumentException("password == null");
058    
059                    this.sessionManager = sessionManager;
060    
061                    // see org.cumulus4j.keymanager.back.shared.Request#getCryptoSessionIDPrefix()
062                    // see org.cumulus4j.store.crypto.AbstractCryptoSession#getKeyStoreID()
063                    this.cryptoSessionID = (
064                                    sessionManager.getCryptoSessionIDPrefix()
065                                    + '*'
066                                    + Long.toString(sessionManager.nextCryptoSessionSerial(), 36)
067                                    + '*'
068                                    + IdentifierUtil.createRandomID(6)
069                    );
070    
071                    this.userName = userName;
072                    // Clone to prevent the password in the session from being nulled, when the outside password is nulled
073                    // or the outside password from being corrupted when this session is closed. Marco :-)
074                    this.password = password.clone();
075            }
076    
077            private String cryptoSessionID;
078            private String userName;
079            private char[] password;
080            private volatile Date lastUse;
081            private volatile Date expiry;
082            private volatile boolean released;
083    
084            /**
085             * Get the identifier of this session.
086             * @return the session's unique identifier.
087             */
088            public String getCryptoSessionID() {
089                    return cryptoSessionID;
090            }
091    
092            public String getUserName() {
093                    return userName;
094            }
095            public char[] getPassword() {
096                    return password;
097            }
098    
099            public Date getLastUse() {
100                    return lastUse;
101            }
102    
103            protected void updateLastUse(long expiryAgeMSec) {
104                    lastUse = new Date();
105                    expiry = new Date(lastUse.getTime() + expiryAgeMSec);
106            }
107    
108            public Date getExpiry() {
109                    return expiry;
110            }
111    
112            public void destroy()
113            {
114                    SessionManager sm = sessionManager;
115                    if (sm == null)
116                            return;
117    
118                    sm.onDestroySession(this);
119    
120                    sessionManager = null;
121    
122                    logger.debug("destroy: Destroying session for userName='{}' cryptoSessionID='{}'.", userName, cryptoSessionID);
123    
124                    char[] pw = password;
125                    if (pw != null) {
126                            Arrays.fill(pw, (char)0);
127                            password = null;
128                    }
129    
130                    cryptoSessionID = null;
131                    userName = null;
132            }
133    
134            /**
135             * <p>
136             * Set the 'released' status.
137             * </p>
138             * <p>
139             * The application server can only request keys from a session that is currently acquired. That means, a session
140             * should first be acquired, then the app-server should be made to work (on behalf of the client) and finally,
141             * it should be released again.
142             * </p>
143             *
144             * @param released the new 'released' status.
145             */
146            protected void setReleased(boolean released) {
147                    this.released = released;
148            }
149    
150            public void release() {
151                    SessionManager sm = sessionManager;
152                    if (sm == null)
153                            return;
154    
155                    sm.onReleaseSession(this);
156            }
157    
158            public boolean isReleased() {
159                    return released;
160            }
161    
162            public void reacquire() {
163                    SessionManager sm = sessionManager;
164                    if (sm == null)
165                            return;
166    
167                    sm.onReacquireSession(this);
168            }
169    }