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

                             



                                              

                                           

                                                    
                                           
                                                        
                                          
                                                             
                                                            
                                                          
                                              
                                              

                                        






                                                             
                                                                                        













                                                                                       



                                                   


                                                                                      



                                                                                         
                                                            
                                 
                                            








                                                                                                            

           





                                                                                   
                                       
           
                                                                                                             
                                                 






                                                                                       




                                                                                   
                                       
           
                                                                                                                   





                                                                                   
           
                                    


                                                  

                                                  
                                                                            



                                    
                                                                                                                
                                                  

                                                                                         
                                                
                 




                                                            
                 

                              










                                                  




                                                   

                                                                             
                                                                     
           
                                                                                                              


                                                                                     
                                                      
                                                                            

                                                                                           
                                       

                                                                                                                         
                                                         
                 
                                                                                                               



                                                                      
                                                                                                  
                                                                 
                                                        




                                                                         
                                                                                                           
                                                                 
                         


                                                                                                                    
                 


                                                       






























                                                                                                                                
 
 
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.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.openslx.bwlp.sat.database.mappers.DbUser;
import org.openslx.bwlp.sat.permissions.User;
import org.openslx.bwlp.sat.util.Formatter;
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.TInvalidTokenException;
import org.openslx.bwlp.thrift.iface.TInvocationException;
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 = LogManager.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
	 * @throws TInvocationException
	 */
	public static UserInfo getOrFail(String token) throws TAuthorizationException, TInvocationException {
		UserInfo ui = getInternal(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 TInvocationException
	 */
	public static void ensureAuthenticated(String token) throws TAuthorizationException, TInvocationException {
		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 | TInvocationException e) {
			return null;
		}
	}

	private static UserInfo getInternal(String token) throws TAuthorizationException, TInvocationException {
		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 TInvocationException if something unexpected fails
	 */
	private static UserInfo getRemote(String token) throws TAuthorizationException, TInvocationException {
		UserInfo ui = null;
		try {
			ui = ThriftManager.getMasterClient().getUserFromToken(token);
		} catch (TInvalidTokenException ite) {
			LOGGER.warn("Master says: Invalid token: " + token);
			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 TInvocationException();
		}
		LOGGER.info("Got '" + Formatter.userFullName(ui) + "' (" + ui.userId + ") for token " + token);
		if (ui.role == null) {
			// Fail-safe: No role supplied, assume student
			ui.role = Role.STUDENT;
		}
		// Valid reply, check if user is allowed to communicate with this satellite server
		AuthorizationError authError = User.canLogin(ui);
		handleAuthorizationError(ui, authError);
		// 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 TInvocationException();
			}
			// Check again, as it might be a fresh entry to the DB, and we don't allow logins by default
			authError = User.canLogin(ui);
			handleAuthorizationError(ui, authError);
		}
		tokenManager.put(token, new Entry(ui));
		return ui;
	}
	
	private static void handleAuthorizationError(UserInfo ui, AuthorizationError authError) throws TAuthorizationException {
		if (authError == null)
			return;
		
		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");
		}
	}

}