package de.hsadmin.mods.user; import java.util.LinkedList; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.Query; import de.hsadmin.core.model.AuthorisationException; import de.hsadmin.core.model.Entity; import de.hsadmin.core.model.AbstractModuleImpl; import de.hsadmin.core.model.HSAdminException; import de.hsadmin.mods.pac.Pac; public class UnixUserModuleImpl extends AbstractModuleImpl { private static final long serialVersionUID = 7480353553685400790L; public UnixUserModuleImpl() { } @Override public Entity initialize(Entity newEntity) throws AuthorisationException { UnixUser newUnixUser = (UnixUser) super.initialize(newEntity); newUnixUser.setName(getLoginUser().getPac().getName() + '-'); return newUnixUser; } @Override public Entity find(Class entityClass, Object key) throws HSAdminException { // do query UnixUser res = (UnixUser) super.find(entityClass, key); // check access rights needsPartialAccessOnPacOf(res, "find"); // return clean result return res; } @Override public Entity findByString(Class entityClass, String key) throws HSAdminException { // do query UnixUser res = (UnixUser) super.findByString(entityClass, key); // return directly (checking rights already done in search within // findByString) return res; } @Override public List search(Class entityClass, String condition, String orderBy) throws HSAdminException { // do query if (orderBy == null || orderBy.length() == 0) { orderBy = "ORDER BY obj.name ASC"; } List res = super.search(entityClass, condition, orderBy); List ret = new LinkedList(); // remove entities where login user has no access rights if (res != null) { for (Entity entity : res) { try { UnixUser returnedUnixUser = (UnixUser) entity; needsPartialAccessOnPacOf(returnedUnixUser, "search"); ret.add(returnedUnixUser); } catch (AuthorisationException exc) { } // ignore } } // return clean result return ret; } @Override public Entity add(Entity newEntity) throws HSAdminException { EntityManager em = getEntityManager(); // only allow pac which matches the username (TODO: hard coded // Hostsharing convention) UnixUser newUnixUser = (UnixUser) newEntity; // validation of username and password String name = newUnixUser.getName(); if (name == null) { throw new HSAdminException("username is required"); } String userName = name.toLowerCase().trim(); for (char c : userName.toCharArray()) { if (!(Character.isLetterOrDigit(c) || c == '.' || c == '-' || c == '_')) { throw new AuthorisationException(getLoginUser(), "add", newUnixUser, "userId"); } } if (userName.length() < 7 || userName.charAt(5) != '-' || userName.lastIndexOf('-') > 5) { throw new AuthorisationException(getLoginUser(), "add", newUnixUser, "userId"); } String passWord = newUnixUser.getPassword(); if (passWord == null || passWord.length() == 0) { throw new HSAdminException("password is required"); } if (passWord.indexOf(':') >= 0) { throw new AuthorisationException(getLoginUser(), "add", newUnixUser, "userId"); } Query qPac = em.createQuery("SELECT obj FROM Pacs obj WHERE obj.name = :pacName"); qPac.setParameter("pacName", userName.substring(0, 5)); Pac pac = (Pac) qPac.getSingleResult(); newUnixUser.setName(userName); newUnixUser.setHomedir("/home/pacs/" + userName.substring(0, 5) + "/users/" + userName.substring(6)); // no appropriate uid? if (newUnixUser.getUserId() < 1000) { // determine next free uid long nUID = 20000; Query qUID = em.createQuery("SELECT MAX(u.userId) FROM UnixUsers u"); Long maxUid = (Long) qUID.getSingleResult(); if (maxUid >= nUID) nUID = maxUid + 1; newUnixUser.setUserId(nUID); } else { // given uid belongs to same pac? Query q = em.createQuery("SELECT u.userId FROM UnixUsers u WHERE u.userId = :userId AND u.pac = :pac"); q.setParameter("userId", newUnixUser.getUserId()); q.setParameter("pac", pac); List idOfSamePac = q.getResultList(); if (idOfSamePac.size() == 0) { throw new AuthorisationException(getLoginUser(), "add", newUnixUser, "userId"); } } // don't move this up, it will update the new entity still with wrong // userid! newUnixUser.setPac(pac); newUnixUser.init(); // authorisation check needsFullAccessOnPacOf(newUnixUser, "add"); // add new entity return super.add(newEntity); } private EntityManager getEntityManager() { return getTransaction().getEntityManager(); } @Override public Entity update(Entity existingEntity) throws HSAdminException { // get the entity from the database UnixUser detachedUnixUser = (UnixUser) existingEntity; UnixUser attachedUnixUser = getEntityManager().find(detachedUnixUser.getClass(), detachedUnixUser.getId()); // authorisation check needsFullAccessOnUser(attachedUnixUser, "update"); // update fields where the login user has write access if (attachedUnixUser.getUserId() != detachedUnixUser.getUserId()) throw new AuthorisationException(getLoginUser(), "update", detachedUnixUser, "id"); attachedUnixUser.setName(detachedUnixUser.getName()); attachedUnixUser.setPassword(detachedUnixUser.getPassword()); if (hasFullAccessOnPacOf(attachedUnixUser)) attachedUnixUser.setComment(detachedUnixUser.getComment()); else if (!attachedUnixUser.getComment().equals( detachedUnixUser.getComment())) throw new AuthorisationException(getLoginUser(), "update", detachedUnixUser, "comment"); if (hasFullAccessOnPacOf(attachedUnixUser)) attachedUnixUser.setHomedir(detachedUnixUser.getHomedir()); else if (!attachedUnixUser.getHomedir().equals( detachedUnixUser.getHomedir())) throw new AuthorisationException(getLoginUser(), "update", detachedUnixUser, "homedir"); if (!attachedUnixUser.getShell().equals(detachedUnixUser.getShell())) if (hasFullAccessOnPacOf(attachedUnixUser) || isLoginShell(attachedUnixUser.getShell()) || !isLoginShell(detachedUnixUser.getShell())) attachedUnixUser.setShell(detachedUnixUser.getShell()); else throw new AuthorisationException(getLoginUser(), "update", detachedUnixUser, "shell"); if (attachedUnixUser.isLocked() != detachedUnixUser.isLocked()) throw new AuthorisationException(getLoginUser(), "update", detachedUnixUser, "locked"); if (detachedUnixUser.getQuotaSoftlimit() != null) { if (hasFullAccessOnPacOf(attachedUnixUser)) { attachedUnixUser.setQuotaSoftlimit(detachedUnixUser.getQuotaSoftlimit()); if (detachedUnixUser.getQuotaHardlimit() != null) { attachedUnixUser.setQuotaHardlimit(detachedUnixUser.getQuotaHardlimit()); } } else { Integer oldQuota = attachedUnixUser.getQuotaSoftlimit(); Integer newQuota = detachedUnixUser.getQuotaSoftlimit(); if (oldQuota != newQuota && !oldQuota.equals(newQuota)) throw new AuthorisationException(getLoginUser(), "update", detachedUnixUser, "quota"); } } // update entity return super.update(attachedUnixUser); } @Override public void delete(Entity existingEntity) throws HSAdminException { // get the entity from the database UnixUser detachedUnixUser = (UnixUser) existingEntity; UnixUser attachedUnixUser = getEntityManager().find(detachedUnixUser.getClass(), detachedUnixUser.getId()); // authorisation check if (attachedUnixUser.getName().length() < 7) { throw new AuthorisationException(attachedUnixUser, "delete"); } needsFullAccessOnPacOf(attachedUnixUser, "delete"); // delete entity super.delete(attachedUnixUser); } // throws an AuthorisationException if the login user has no write acess // on the pac of the given UnixUser private boolean hasFullAccessOnPacOf(UnixUser user) { // only pac admins (same name as pac) and the owner (customer) have // write access to the pac boolean isPacAdmin = getLoginUser().getName().equals( user.getPac().getName()); boolean isCustomer = getLoginUser().getName().equals( user.getPac().getCustomer().getName()); boolean isHostmaster = getLoginUser().hasHostmasterRole(); return isPacAdmin || isCustomer || isHostmaster; } // throws an AuthorisationException if the login user has no write acess // on the pac of the given UnixUser private void needsFullAccessOnPacOf(UnixUser user, String method) throws AuthorisationException { if (!hasFullAccessOnPacOf(user)) throw new AuthorisationException(getLoginUser(), method, user); } // throws an AuthorisationException if the login user has no read acess on // the pac of the given UnixUser private void needsPartialAccessOnPacOf(UnixUser user, String method) throws AuthorisationException { if (!hasFullAccessOnPacOf(user) && getLoginUser().getPac().id() != user.getPac().id()) throw new AuthorisationException(getLoginUser(), method, user); } // throws an AuthorisationException if the login user has not even partial // write access on the given UnixUser private void needsFullAccessOnUser(UnixUser user, String method) throws AuthorisationException { // neither pac admin (same name as pac), pac owner (customer) nor the // user itself? if (!hasFullAccessOnPacOf(user) && !getLoginUser().sameIdAs(user)) throw new AuthorisationException(getLoginUser(), method, user); } // returns true if the given shell is a login shell private static boolean isLoginShell(String shell) { // TODO: list of login shells should not be hardcoded if (shell.equals("/bin/sh")) return true; if (shell.equals("/bin/bash")) return true; if (shell.equals("/bin/csh")) return true; if (shell.equals("/bin/tcsh")) return true; if (shell.equals("/bin/zsh")) return true; if (shell.equals("/bin/ksh")) return true; return false; } }