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                    this.cryptoSessionID = (
062                                    sessionManager.getCryptoSessionIDPrefix()
063                                    + '.'
064                                    + Long.toString(sessionManager.nextCryptoSessionSerial(), 36)
065                                    + '.'
066                                    + IdentifierUtil.createRandomID(8)
067                    );
068    
069                    this.userName = userName;
070                    // Clone to prevent the password in the session from being nulled, when the outside password is nulled
071                    // or the outside password from being corrupted when this session is closed. Marco :-)
072                    this.password = password.clone();
073            }
074    
075            private String cryptoSessionID;
076            private String userName;
077            private char[] password;
078            private volatile Date lastUse;
079            private volatile Date expiry;
080            private volatile boolean released;
081    
082            /**
083             * Get the identifier of this session.
084             * @return the session's unique identifier.
085             */
086            public String getCryptoSessionID() {
087                    return cryptoSessionID;
088            }
089    
090            public String getUserName() {
091                    return userName;
092            }
093            public char[] getPassword() {
094                    return password;
095            }
096    
097            public Date getLastUse() {
098                    return lastUse;
099            }
100    
101            protected void updateLastUse(long expiryAgeMSec) {
102                    lastUse = new Date();
103                    expiry = new Date(lastUse.getTime() + expiryAgeMSec);
104            }
105    
106            public Date getExpiry() {
107                    return expiry;
108            }
109    
110            public void destroy()
111            {
112                    SessionManager sm = sessionManager;
113                    if (sm == null)
114                            return;
115    
116                    sm.onDestroySession(this);
117    
118                    sessionManager = null;
119    
120                    logger.debug("destroy: Destroying session for userName='{}' cryptoSessionID='{}'.", userName, cryptoSessionID);
121    
122                    char[] pw = password;
123                    if (pw != null) {
124                            Arrays.fill(pw, (char)0);
125                            password = null;
126                    }
127    
128                    cryptoSessionID = null;
129                    userName = null;
130            }
131    
132            /**
133             * <p>
134             * Set the 'released' status.
135             * </p>
136             * <p>
137             * The application server can only request keys from a session that is currently acquired. That means, a session
138             * should first be acquired, then the app-server should be made to work (on behalf of the client) and finally,
139             * it should be released again.
140             * </p>
141             *
142             * @param released the new 'released' status.
143             */
144            protected void setReleased(boolean released) {
145                    this.released = released;
146            }
147    
148            public void release() {
149                    SessionManager sm = sessionManager;
150                    if (sm == null)
151                            return;
152    
153                    sm.onReleaseSession(this);
154            }
155    
156            public boolean isReleased() {
157                    return released;
158            }
159    
160            public void reacquire() {
161                    SessionManager sm = sessionManager;
162                    if (sm == null)
163                            return;
164    
165                    sm.onReacquireSession(this);
166            }
167    }