NetworkAssociationBean.java 10.9 KB
/*
 * Copyright Codecrew Ry
 * 
 * All rights reserved.
 * 
 * This license applies to any software containing a notice placed by the 
 * copyright holder. Such software is herein referred to as the Software. 
 * This license covers modification, distribution and use of the Software. 
 * 
 * Any distribution and use in source and binary forms, with or without 
 * modification is not permitted without explicit written permission from the 
 * copyright owner. 
 * 
 * A non-exclusive royalty-free right is granted to the copyright owner of the 
 * Software to use, modify and distribute all modifications to the Software in 
 * future versions of the Software. 
 * 
 */
package fi.codecrew.moya.beans;

import java.util.Calendar;
import java.util.HashSet;
import java.util.List;

import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;

import fi.codecrew.moya.enums.BortalApplication;
import fi.codecrew.moya.enums.NetworkAssociationStatus;
import fi.codecrew.moya.enums.apps.IAppPermission;
import fi.codecrew.moya.enums.apps.NetworkAssociationPermission;
import fi.codecrew.moya.facade.NetworkAssociationFacade;
import fi.codecrew.moya.model.ApplicationPermission;
import fi.codecrew.moya.model.EventUser;
import fi.codecrew.moya.model.GroupMembership;
import fi.codecrew.moya.model.NetworkAssociation;
import fi.codecrew.moya.model.Place;
import fi.codecrew.moya.model.PrintedCard;
import fi.codecrew.moya.model.Role;

/**
 * Session Bean implementation class NetworkAssociationBean
 */
@Stateless
@LocalBean
public class NetworkAssociationBean implements NetworkAssociationBeanLocal {

	@EJB
    private UserBean userBean;
    
    @EJB
    private BarcodeBean barcodeBean;
    
    @EJB
    private RoleBean roleBean;
    
    @EJB
    private NetworkAssociationFacade networkAssociationFacade;
    
    @EJB
    private EventBean eventBean;
    
    @EJB
    private CardTemplateBean cardBean; 
    
    @EJB
    private PermissionBean permissionBean;

    public NetworkAssociationBean() {}
    
    @Override
    @RolesAllowed(NetworkAssociationPermission.S_CAN_ADMINISTER_ASSOCIATIONS)
	public List<NetworkAssociation> getStatusByIPAndMAC(String ip, String mac) {
		return networkAssociationFacade.findByIPAndMAC(eventBean.getCurrentEvent(), ip, mac);
	}
    
    @Override
    @RolesAllowed(NetworkAssociationPermission.S_CAN_ADMINISTER_ASSOCIATIONS)
	public List<NetworkAssociation> getActiveAssociations(boolean activatePending) {
		if(activatePending)
			activatePendingAssociations();
		
		return networkAssociationFacade.findByStatus(eventBean.getCurrentEvent(), NetworkAssociationStatus.ACTIVE);
	}

    @Override
    @RolesAllowed(NetworkAssociationPermission.S_CAN_ADMINISTER_ASSOCIATIONS)
    public NetworkAssociation tryAssociate(String ip, String mac) {
    	EventUser eu = permissionBean.getCurrentUser();
    	HashSet<IAppPermission> userPerms = buildPermsFor(eu);
    	NetworkAssociation nasc = associate(eu, userPerms, null, ip, mac);
    	
    	return nasc;
    }
    
	@Override
	@RolesAllowed(NetworkAssociationPermission.S_CAN_ADMINISTER_ASSOCIATIONS)
    public NetworkAssociation tryAssociate(String username, String password,
			String ip, String mac, String code, boolean codeRequired)
			throws Exception {
    	
    	EventUser authUser = userBean.validateUser(username, password);
    	if(authUser == null) 
    		throw new Exception("INVALID_USER_OR_PASSWORD");
    	
    	return tryAssociateInternal(authUser, ip, mac, code, codeRequired);
	}
	

	@Override
	@RolesAllowed(NetworkAssociationPermission.S_CAN_ADMINISTER_ASSOCIATIONS)
	public NetworkAssociation tryAssociate(String usercode, String ip, String mac, String code, Boolean codeRequired) throws Exception {
		EventUser authUser = userBean.getUser(usercode);
		if(authUser == null)
			throw new Exception("INVALID_USERCODE");

		return tryAssociateInternal(authUser, ip, mac, code, codeRequired);
	}
	
	private NetworkAssociation tryAssociateInternal(EventUser authUser, String ip, String mac, String code, boolean codeRequired) throws Exception {
    	NetworkAssociation association;
    	
    	HashSet<IAppPermission> userPerms = buildPermsFor(authUser);
		
		if(!userPerms.contains(NetworkAssociationPermission.CAN_ASSOCIATE)) 
			throw new Exception("USER_HAS_NO_ASSOCIATE_PERMISSION");
		
		if(codeRequired) {
			Place place = resolvePlaceFromCode(code);
			
			if(place == null) {
				// This is a valid case when user has permissions that allow them to
				// override any place requirements, effectively allowing for free
				// associations without the need of a real place.
				if(!userPerms.contains(NetworkAssociationPermission.OVERRIDE_PLACE_REQUIREMENT))
					throw new Exception("INVALID_PLACECODE");
					
				association = associate(authUser, userPerms, place, ip, mac);

			} else {
				if(place.getPlaceReserver().getUser().equals(authUser)) {
					// This is the simple case when user goes to their own place
					association = associate(authUser, userPerms, place, ip, mac);
				} else {
					// In this case, the user has probably shuffled their seats
					// within their group
					if(!userPerms.contains(NetworkAssociationPermission.CAN_SHUFFLE_IN_GROUP))
						throw new Exception("INVALID_PLACECODE");
					
					boolean userIsInGroup = false;
					for(GroupMembership gm : place.getGroup().getMembers()) {
						EventUser member = gm.getUser();
						if(member != null && gm.getUser().equals(authUser)) {
							userIsInGroup = true;
							break;
						}
					}
					
					if(!userIsInGroup)
						throw new Exception("INVALID_PLACECODE");
					
					association = associate(authUser, userPerms, place, ip, mac);
				}
			}
		} else {
			// Code was not required, this is often a case for WLAN
			association = associate(authUser, userPerms, null, ip, mac);
		}
    	
    	return association;
    }
	
	@Override
	@RolesAllowed(NetworkAssociationPermission.S_CAN_ADMINISTER_ASSOCIATIONS)
	public List<NetworkAssociation> getPendingAssociations() {
		return networkAssociationFacade.findByStatus(eventBean.getCurrentEvent(), NetworkAssociationStatus.PENDING);
	}
	
	@Override
	@RolesAllowed(NetworkAssociationPermission.S_CAN_ADMINISTER_ASSOCIATIONS)
	public void dropAssociationById(Integer associd) {
		NetworkAssociation na = networkAssociationFacade.find(associd);
		if(na.getEvent().equals(eventBean.getCurrentEvent())) {
			na.setModifyTime(Calendar.getInstance());
			na.setStatus(NetworkAssociationStatus.EXPIRED);
		}
	}
	
	private HashSet<IAppPermission> buildPermsFor(EventUser authUser) {
		HashSet<IAppPermission> userPerms = new HashSet<>();
		if (authUser.getUser().isSuperadmin()) {
			// Iterate through all permissions & add
			for (BortalApplication app : BortalApplication.values()) {
				for (IAppPermission perm : app.getPermissions()) {
					userPerms.add(perm);
				}
			}
		} else {
			for(Role r : userBean.localFindUsersRoles(authUser)) {
				for(ApplicationPermission appPerm : r.getPermissions()) {
					IAppPermission iap = appPerm.getPermission();
					if(!userPerms.contains(iap)) {
						userPerms.add(iap);
					}
				}
			}
		}
		
		return userPerms;
	}

    private void activatePendingAssociations() {
    	List<NetworkAssociation> netAssocs = networkAssociationFacade.findByStatus(
    			eventBean.getCurrentEvent(), NetworkAssociationStatus.PENDING);
    	
		for(NetworkAssociation na : netAssocs) {
			na.setStatus(NetworkAssociationStatus.ACTIVE);
			na.setModifyTime(Calendar.getInstance());
		}
	}
	
	private NetworkAssociation associate(EventUser authUser, HashSet<IAppPermission> userPerms, 
			Place place, String ip, String mac) {
		
		// This tests the case where association request is still pending, we will not
		// create new one but rather return an old one
		List<NetworkAssociation> nas = networkAssociationFacade.findByIPAndMACAndStatus(
				eventBean.getCurrentEvent(), ip, mac, NetworkAssociationStatus.PENDING);
		if(nas.size() > 0)
			return nas.get(0);
		
		nas = networkAssociationFacade.findByIPAndMACAndStatus(
				eventBean.getCurrentEvent(), ip, mac, NetworkAssociationStatus.ACTIVE);
		if(nas.size() > 0)
			return nas.get(0);		
		
		NetworkAssociation na = new NetworkAssociation();
		na.setIP(ip);
		na.setMAC(mac);
		na.setPlace(place);
		na.setEventUser(authUser);
		na.setEvent(authUser.getEvent());
		na.setStatus(NetworkAssociationStatus.PENDING);
		
		List<NetworkAssociation> activeAssocs;
		if(place == null) {
			// When in this branch, we are doing an association which will never
			// dis-associate things EXCEPT in the case where IP collision happens
			// which usually is a result of network ops change...
			activeAssocs = networkAssociationFacade.findActiveAssociationsByIP(
					eventBean.getCurrentEvent(), ip);
			
			for(NetworkAssociation activeAssoc : activeAssocs) {
				activeAssoc.setModifyTime(Calendar.getInstance());
				activeAssoc.setStatus(NetworkAssociationStatus.EXPIRED);
			}
		} else {
			// Disassociate any colliding IPs
			activeAssocs = networkAssociationFacade.findActiveAssociationsByIP(
					eventBean.getCurrentEvent(), ip);
			
			for(NetworkAssociation activeAssoc : activeAssocs) {
				activeAssoc.setModifyTime(Calendar.getInstance());
				activeAssoc.setStatus(NetworkAssociationStatus.EXPIRED);
			}
			
			// In here we disassociate old associations from this place unless the user has many
			// associations per place
			if(!userPerms.contains(NetworkAssociationPermission.CAN_ASSOCIATE_MANY_PER_PLACE)) {
				activeAssocs = networkAssociationFacade.findActiveAssociationsByPlace(place);
				
				for(NetworkAssociation activeAssoc : activeAssocs) {
					activeAssoc.setModifyTime(Calendar.getInstance());
					activeAssoc.setStatus(NetworkAssociationStatus.EXPIRED);
				}
			}
		}
		
		// Finally persist the association and return
		networkAssociationFacade.create(na);
		return na;
	}
	
	private Place resolvePlaceFromCode(String code) {
		if(code == null) return null;
		return barcodeBean.getPlaceFromTextCode(code);
	}

	@Override
	@RolesAllowed(NetworkAssociationPermission.S_CAN_ADMINISTER_ASSOCIATIONS)
	public NetworkAssociation getActiveAssociationByIP(String ipAddress) {
		List<NetworkAssociation> na = this.networkAssociationFacade.findActiveAssociationsByIP(
				eventBean.getCurrentEvent(), 
				ipAddress
		);
		
		if(na.size() > 0) return na.get(0);
		else return null;
	}

	@Override
	@RolesAllowed(NetworkAssociationPermission.S_CAN_ADMINISTER_ASSOCIATIONS)
	public List<NetworkAssociation> getActiveAssociationsByHorizon(Boolean activate, String horizon) {
		if(activate)
			activatePendingAssociationsByHorizon(horizon);
		
		return networkAssociationFacade.findByStatusAndHorizon(eventBean.getCurrentEvent(), NetworkAssociationStatus.ACTIVE, horizon);
	}

	private void activatePendingAssociationsByHorizon(String horizon) {
    	List<NetworkAssociation> netAssocs = networkAssociationFacade.findByStatusAndHorizon(
    			eventBean.getCurrentEvent(), NetworkAssociationStatus.PENDING, horizon);
    	
		for(NetworkAssociation na : netAssocs) {
			na.setStatus(NetworkAssociationStatus.ACTIVE);
			na.setModifyTime(Calendar.getInstance());
		}
	}
}