All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.jstuff.integration.auth.DefaultAuthService Maven / Gradle / Ivy

/*
 * Copyright 2010-2022 by Sebastian Thomschke and contributors.
 * SPDX-License-Identifier: EPL-2.0
 */
package net.sf.jstuff.integration.auth;

import static net.sf.jstuff.core.validation.NullAnalysisHelper.*;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.inject.Inject;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.springframework.util.PatternMatchUtils;

import net.sf.jstuff.core.Strings;
import net.sf.jstuff.core.collection.MapWithSets;
import net.sf.jstuff.core.logging.Logger;
import net.sf.jstuff.integration.userregistry.GroupDetailsService;
import net.sf.jstuff.integration.userregistry.UserDetailsService;

/**
 * @author Sebastian Thomschke
 */
public class DefaultAuthService implements AuthService {
   private static final Logger LOG = Logger.create();

   protected Set applicationRoles = lazyNonNull();
   protected Authenticator authenticator = lazyNonNull();
   protected GroupDetailsService groupDetailsService = lazyNonNull();
   protected MapWithSets groupIdToApplicationRoleMappings = lazyNonNull();
   protected @Nullable AuthListener listener;
   protected MapWithSets uriPatternsToApplicationRoleMappings = lazyNonNull();
   protected UserDetailsService userDetailsService = lazyNonNull();

   public DefaultAuthService() {
      LOG.infoNew(this);
   }

   @Override
   public void assertAuthenticated() throws PermissionDeniedException {
      if (!isAuthenticated())
         throw new PermissionDeniedException("You are not authorized to perform that operation. You need to authenticate first.");
   }

   @Override
   public void assertIdentity(final String userId) throws PermissionDeniedException {
      if (!isIdentity(userId))
         throw new PermissionDeniedException("You are not authorized to perform that operation. Identity mismatch.");
   }

   @Override
   public void assertRole(final String applicationRole) throws PermissionDeniedException {
      if (!hasRole(applicationRole))
         throw new PermissionDeniedException("You are not authorized to perform that operation.");
   }

   @Override
   public void assertURIAccess(final String uri) throws PermissionDeniedException {
      for (final Entry> entry : uriPatternsToApplicationRoleMappings.entrySet()) {
         final String uriPattern = entry.getKey();

         if (PatternMatchUtils.simpleMatch(uriPattern, uri)) {
            LOG.trace("%s matches %s", uri, uriPattern);

            final Collection roles = entry.getValue();
            if (roles.isEmpty()) {
               continue;
            }

            boolean hasAnyRequiredRole = false;
            for (final String requiredRole : roles)
               if (hasRole(requiredRole)) {
                  hasAnyRequiredRole = true;
                  break;
               }
            if (!hasAnyRequiredRole)
               throw new PermissionDeniedException("You are not authorized to perform that operation.");
         } else {
            LOG.trace("%s does NOT match %s", uri, uriPattern);
         }
      }
   }

   @Override
   public Authentication getAuthentication() {
      return AuthenticationHolder.getAuthentication();
   }

   @Override
   public Set getGrantedRoles() {
      final Authentication auth = AuthenticationHolder.getAuthentication();
      if (!auth.isAuthenticated()) {
         LOG.trace("User is not authenticated.");
         return Collections.emptySet();
      }
      return getGrantedRoles(asNonNull(auth.getUserDetails()).getDistinguishedName());
   }

   protected Set getGrantedRoles(final @Nullable String userDN) {
      if (userDN == null || Strings.isBlank(userDN))
         return Collections.emptySet();

      final Set groupIds = groupDetailsService.getGroupIdsByUserDN(userDN);
      final var roles = new HashSet();

      for (final String groupId : groupIds) {
         final Collection coll = groupIdToApplicationRoleMappings.get(groupId);
         if (coll != null) {
            roles.addAll(coll);
         }
      }
      return roles;
   }

   @Override
   public Set getGroupIds() {
      final Authentication auth = AuthenticationHolder.getAuthentication();
      if (!auth.isAuthenticated()) {
         LOG.trace("User is not authenticated.");
         return Collections.emptySet();
      }
      return groupDetailsService.getGroupIdsByUserDN(asNonNull(asNonNull(auth.getUserDetails()).getDistinguishedName()));
   }

   @Override
   public boolean hasRole(final String applicationRole) {
      return getGrantedRoles().contains(applicationRole);
   }

   @Override
   public boolean isAuthenticated() {
      return AuthenticationHolder.getAuthentication().isAuthenticated();
   }

   @Override
   public boolean isIdentity(final String userId) throws PermissionDeniedException {
      final Authentication auth = AuthenticationHolder.getAuthentication();

      return auth.isAuthenticated() && asNonNull(auth.getUserDetails()).getUserId().equals(userId);
   }

   @Override
   public void login(final String logonName, final String password) throws AuthenticationFailedException, AlreadyAuthenticatedException {
      if (isAuthenticated())
         throw new AlreadyAuthenticatedException("An authentication for the active session already exists.");

      if (isAuthenticated()) {
         logout();
      }

      if (!authenticator.authenticate(logonName, password))
         throw new AuthenticationFailedException("Incorrect username or password.");

      final var auth = new DefaultAuthentication(userDetailsService.getUserDetailsByLogonName(logonName), password);
      AuthenticationHolder.setAuthentication(auth);

      if (listener != null) {
         listener.afterLogin(auth);
      }
   }

   @Override
   public void logout() {
      final Authentication auth = AuthenticationHolder.getAuthentication();
      auth.invalidate();

      final var listener = this.listener;
      if (listener != null) {
         final var userDetails = auth.getUserDetails();
         if (userDetails != null) {
            listener.afterLogout(userDetails);
         }
      }
   }

   /**
    * @param applicationRoles the applicationRoles to set
    */
   @Inject
   public synchronized void setApplicationRoles(final @NonNull String... applicationRoles) {
      this.applicationRoles = new HashSet<>();
      for (final String element : applicationRoles) {
         LOG.trace("Registering application role: %s", element);
         this.applicationRoles.add(element);
      }
   }

   /**
    * @param authenticator the authenticator to set
    */
   @Inject
   public void setAuthenticator(final Authenticator authenticator) {
      this.authenticator = authenticator;
   }

   /**
    * @param groupDetailsService the groupDetailsService to set
    */
   @Inject
   public void setGroupDetailsService(final GroupDetailsService groupDetailsService) {
      this.groupDetailsService = groupDetailsService;
   }

   /**
    * @param mappings format ? groupIdXXX -> roleXXX
    */
   @Inject
   public synchronized void setGroupIdToApplicationRoleMappings(final Map mappings) throws UnknownApplicationRoleException {
      groupIdToApplicationRoleMappings = new MapWithSets<>();
      for (final Entry mapping : mappings.entrySet()) {
         final String group = mapping.getKey().trim();
         if (group.length() > 0) {
            final var role = mapping.getValue().trim();
            if (role.length() > 0) {
               LOG.trace("Registering groupId -> application role mapping: %s => %s", group, role);

               if (!applicationRoles.contains(role))
                  throw new UnknownApplicationRoleException("Application role is unknown: " + role);

               groupIdToApplicationRoleMappings.add(group, role);
            }
         }
      }
   }

   /**
    * @param mappings format = "groupIdXXX=roleXXX"
    */
   @Inject
   public synchronized void setGroupIdToApplicationRoleMappingsViaStringArray(final @NonNull String[] mappings)
      throws UnknownApplicationRoleException {
      groupIdToApplicationRoleMappings = new MapWithSets<>();
      for (final var element : mappings) {
         final var pair = Strings.split(element, '=');
         pair[0] = pair[0].trim();
         pair[1] = pair[1].trim();

         if (pair[0].length() == 0 || pair[1].length() == 0) {
            continue;
         }

         LOG.trace("Registering groupId -> application role mapping: %s => %s", pair[0], pair[1]);
         if (!applicationRoles.contains(pair[1]))
            throw new UnknownApplicationRoleException("Application role is unknown: " + pair[1]);
         groupIdToApplicationRoleMappings.add(pair[0], pair[1]);
      }
   }

   @Override
   public void setListener(final AuthListener listener) {
      this.listener = listener;
   }

   /**
    * @param mappings format = "/myuri*=roleXXX"
    */
   @Inject
   public synchronized void setUriPatternsToApplicationRoleMappings(final @NonNull String[] mappings)
      throws UnknownApplicationRoleException {
      uriPatternsToApplicationRoleMappings = new MapWithSets<>();
      for (final var element : mappings) {
         final var pair = Strings.split(element, '=');
         pair[0] = pair[0].trim();
         pair[1] = pair[1].trim();

         if (pair[0].length() == 0 || pair[1].length() == 0) {
            continue;
         }

         LOG.trace("Registering URI pattern -> application role mapping: %s => %s", pair[0], pair[1]);
         if (!applicationRoles.contains(pair[1]))
            throw new UnknownApplicationRoleException("Application role is unknown: " + pair[1]);
         uriPatternsToApplicationRoleMappings.add(pair[0], pair[1]);
      }
   }

   /**
    * @param userDetailsService the userDetailsService to set
    */
   @Inject
   public void setUserDetailsService(final UserDetailsService userDetailsService) {
      this.userDetailsService = userDetailsService;
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy