summaryrefslogblamecommitdiffstats
path: root/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/thrift/SessionManager.java
blob: c8384459b2b0bbbeba411bbfe0b7afc1cacd8690 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                    
 

                             




                                              

                                                    
                                                        
                                          
                                                             
                                                          
                                                            
                                              
                                              

                                        





















                                                                                       



                                                   


                                                                                      



                                                                                         
                                                            
                                 
                                            








                                                                                                            

           















                                                                                       














                                                                                            
           
                                    


                                                  







                                                                                                                
                                                  

                                                                                         
                                                
                 




                                                            
                 

                              










                                                  




                                                   


                                                                             
           
                                                                                                              


                                                                                     
                                                      


                                                                                           
                                       


                                                                                                                         
                 



                                                                              
                                                                                                  



























                                                                                                                              




                                                                         

                                                                                                           
                         
                 



                                                       
 
package org.openslx.bwlp.sat.thrift;

import java.sql.SQLException;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.openslx.bwlp.sat.database.mappers.DbUser;
import org.openslx.bwlp.sat.permissions.User;
import org.openslx.bwlp.thrift.iface.AuthorizationError;
import org.openslx.bwlp.thrift.iface.Role;
import org.openslx.bwlp.thrift.iface.TAuthorizationException;
import org.openslx.bwlp.thrift.iface.TInternalServerError;
import org.openslx.bwlp.thrift.iface.TInvalidTokenException;
import org.openslx.bwlp.thrift.iface.UserInfo;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.QuickTimer;
import org.openslx.util.QuickTimer.Task;

/**
 * Manages user sessions. Mainly used to map tokens to users.
 * 
 */
public class SessionManager {

	private static final Logger LOGGER = Logger.getLogger(SessionManager.class);

	private static class Entry {
		private static final long SESSION_TIMEOUT = TimeUnit.DAYS.toMillis(1);
		private final UserInfo user;
		private long validUntil;

		private Entry(UserInfo user) {
			this.user = user;
			this.validUntil = System.currentTimeMillis() + SESSION_TIMEOUT;
		}

		public void touch(long now) {
			this.validUntil = now + SESSION_TIMEOUT;
		}

		public boolean isTooOld(long now) {
			return validUntil < now;
		}
	}

	// saves the current tokens and the mapped userdata, returning from the server
	private static final Map<String, Entry> tokenManager = new ConcurrentHashMap<>();

	static {
		// Clean cached session periodically
		QuickTimer.scheduleAtFixedDelay(new Task() {
			@Override
			public void fire() {
				final long now = System.currentTimeMillis();
				for (Iterator<Entry> it = tokenManager.values().iterator(); it.hasNext();) {
					Entry e = it.next();
					if (e == null || e.isTooOld(now))
						it.remove();
				}
			}
		}, 60000, 1200600);
	}

	/**
	 * Get the user corresponding to the given token.
	 * 
	 * @param token user's token
	 * @return UserInfo for the matching user
	 * @throws TAuthorizationException if the token is not known or the session
	 *             expired
	 */
	public static UserInfo getOrFail(String token) throws TAuthorizationException {
		UserInfo ui = get(token);
		if (ui != null)
			return ui;
		throw new TAuthorizationException(AuthorizationError.NOT_AUTHENTICATED,
				"Your session token is not known to the server");
	}

	/**
	 * Do nothing if a user belongs to the given token and is authorized to use
	 * this satellite, throw an exception otherwise.
	 * 
	 * @param token Token in question
	 * @throws TAuthorizationException
	 * @throws TInternalServerError
	 */
	public static void ensureAuthenticated(String token) throws TAuthorizationException,
			TInternalServerError {
		getInternal(token);
	}

	/**
	 * Get the user corresponding to the given token. Returns <code>null</code>
	 * if the token is not known, or the session already timed out.
	 * 
	 * @param token user's token
	 * @return UserInfo for the matching user
	 */
	public static UserInfo get(String token) {
		try {
			return getInternal(token);
		} catch (TAuthorizationException | TInternalServerError e) {
			return null;
		}
	}

	private static UserInfo getInternal(String token) throws TAuthorizationException, TInternalServerError {
		Entry e = tokenManager.get(token);
		if (e == null) {
			LOGGER.info("Cache miss for token " + token + ", asking master");
			return getRemote(token);
		}
		// User session already cached
		final long now = System.currentTimeMillis();
		if (e.isTooOld(now)) {
			tokenManager.remove(token);
			return getRemote(token);
		}
		e.touch(now);
		return e.user;
	}

	/**
	 * Remove session matching the given token
	 * 
	 * @param token
	 */
	public static void remove(String token) {
		tokenManager.remove(token);
	}

	/**
	 * Get {@link UserInfo} from master server.
	 * 
	 * @param token token of user to get
	 * @return
	 * @throws TAuthorizationException if user is not allowed to use this
	 *             satellite, this exception contains the reason
	 * @throws TInternalServerError if something unexpected fails
	 */
	private static UserInfo getRemote(String token) throws TAuthorizationException, TInternalServerError {
		UserInfo ui = null;
		try {
			ui = ThriftManager.getMasterClient().getUserFromToken(token);
		} catch (TInvalidTokenException ite) {
			LOGGER.warn("Invalid token: " + token, ite);
			throw new TAuthorizationException(AuthorizationError.INVALID_TOKEN,
					"Your token is not known to the master server");
		} catch (Exception e) {
			LOGGER.warn("Could not reach master server to query for user token (" + token + ") of a client!",
					e);
			throw new TInternalServerError();
		}
		LOGGER.info("Got user " + ui.userId + " for token " + token);
		// TODO XXX HACK: Remove this once master server supplies role
		if (ui.role == null)
			ui.role = Role.TUTOR;
		// Valid reply, check if user is allowed to communicate with this satellite server
		AuthorizationError authError = User.canLogin(ui);
		if (authError != null) {
			LOGGER.info("User " + ui.userId + " cannot login: " + authError.toString());
			switch (authError) {
			case ACCOUNT_SUSPENDED:
				throw new TAuthorizationException(authError,
						"Your account is not allowed to log in to this satellite");
			case BANNED_NETWORK:
				throw new TAuthorizationException(authError, "Your IP address is banned from this satellite");
			case INVALID_CREDENTIALS:
			case INVALID_KEY:
			case CHALLENGE_FAILED:
				throw new TAuthorizationException(authError, "Authentication error");
			case INVALID_ORGANIZATION:
				throw new TAuthorizationException(authError,
						"Your organization is not known to this satellite");
			case ORGANIZATION_SUSPENDED:
				throw new TAuthorizationException(authError,
						"Your organization is not allowed to log in to this satellite");
			case NOT_AUTHENTICATED:
			case NO_PERMISSION:
				throw new TAuthorizationException(authError, "No permission");
			case GENERIC_ERROR:
			case INVALID_TOKEN:
			default:
				throw new TAuthorizationException(authError, "Internal server error");
			}
		}
		// Is valid, insert/update db record, but ignore students
		if (ui.role != Role.STUDENT) {
			try {
				DbUser.writeUserOnLogin(ui);
			} catch (SQLException e) {
				LOGGER.info("User " + ui.userId + " cannot be written to DB - rejecting.");
				throw new TInternalServerError();
			}
		}
		tokenManager.put(token, new Entry(ui));
		return ui;
	}

}