/** * ============LICENSE_START==================================================== * org.onap.aaf * =========================================================================== * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. * =========================================================================== * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END==================================================== * */ package org.onap.aaf.cadi.shiro; import java.io.IOException; import java.security.Principal; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.onap.aaf.cadi.Access.Level; import org.onap.aaf.cadi.CadiException; import org.onap.aaf.cadi.LocatorException; import org.onap.aaf.cadi.Permission; import org.onap.aaf.cadi.PropAccess; import org.onap.aaf.cadi.Symm; import org.onap.aaf.cadi.aaf.AAFPermission; import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn; import org.onap.aaf.cadi.aaf.v2_0.AAFCon; import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm; import org.onap.aaf.cadi.config.Config; import org.onap.aaf.cadi.filter.MapBathConverter; import org.onap.aaf.cadi.util.CSV; import org.onap.aaf.cadi.util.Split; import org.onap.aaf.misc.env.APIException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /* * Note: Shiro Realm document * https://shiro.apache.org/realm.html */ public class AAFRealm extends AuthorizingRealm { public static final String AAF_REALM = "AAFRealm"; private static final Logger logger = LoggerFactory.getLogger(AAFRealm.class); // Package on purpose static Singleton singleton = Singleton.singleton(); public static class Singleton { public AAFCon acon; public AAFAuthn authn; public AAFLurPerm authz; // private Set> supports; private MapBathConverter mbc; private Map idMap; private Singleton() { logger.info("Creating AAFRealm.Singleton"); mbc = null; idMap = null; String cadi_prop_files = access.getProperty(Config.CADI_PROP_FILES); if(cadi_prop_files==null) { String msg = Config.CADI_PROP_FILES + " in VM Args is required to initialize AAFRealm."; access.log(Level.INFO,msg); throw new RuntimeException(msg); } else { try { acon = AAFCon.newInstance(access); authn = acon.newAuthn(); authz = acon.newLur(authn); final String csv = access.getProperty(Config.CADI_BATH_CONVERT); if(csv!=null) { try { mbc = new MapBathConverter(access, new CSV(access,csv)); access.log(Level.INFO, "MapBathConversion enabled with file ",csv); idMap = Collections.synchronizedMap(new TreeMap()); // Load for(Entry es : mbc.map().entrySet()) { String oldID = es.getKey(); if(oldID.startsWith("Basic ")) { oldID = Symm.base64noSplit.decode(oldID.substring(6)); int idx = oldID.indexOf(':'); if(idx>=0) { oldID = oldID.substring(0, idx); } } String newID = es.getValue(); if(newID.startsWith("Basic ")) { newID = Symm.base64noSplit.decode(newID.substring(6)); int idx = newID.indexOf(':'); if(idx>=0) { newID = newID.substring(0, idx); } } idMap.put(oldID,newID); } } catch (IOException e) { access.log(e); } } } catch (APIException | CadiException | LocatorException e) { String msg = "Cannot initiate AAFRealm"; access.log(Level.ERROR,e,msg); throw new RuntimeException(msg,e); } } // There is only one of these. If there are more, put back // supports = Collections.synchronizedSet(new HashSet<>()); // supports.add(UsernamePasswordToken.class); } public static synchronized Singleton singleton() { if(singleton==null) { singleton = new Singleton(); } return singleton; } // pick up cadi_prop_files from VM_Args private final PropAccess access = new PropAccess() { @Override public void log(Exception e, Object... elements) { logger.error(buildMsg(Level.ERROR, elements).toString(),e); } @Override public void log(Level level, Object... elements) { if(willLog(level)) { String str = buildMsg(level, elements).toString(); switch(level) { case WARN: case AUDIT: logger.warn(str); break; case DEBUG: logger.debug(str); break; case ERROR: logger.error(str); break; case INFO: case INIT: logger.info(str); break; case NONE: break; case TRACE: logger.trace(str); break; } } } @Override public void printf(Level level, String fmt, Object... elements) { if(willLog(level)) { String str = String.format(fmt, elements); switch(level) { case WARN: case AUDIT: logger.warn(str); break; case DEBUG: logger.debug(str); break; case ERROR: logger.error(str); break; case INFO: case INIT: logger.info(str); break; case NONE: break; case TRACE: logger.trace(str); break; } } } @Override public boolean willLog(Level level) { if(super.willLog(level)) { switch(level) { case WARN: case AUDIT: return logger.isWarnEnabled(); case DEBUG: return logger.isDebugEnabled(); case ERROR: return logger.isErrorEnabled(); case INFO: case INIT: return logger.isInfoEnabled(); case NONE: return false; case TRACE: return logger.isTraceEnabled(); } } return false; } }; } /** * * There appears to be no configuration objects or references available for CADI to start with. * */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { logger.debug("AAFRealm.doGetAuthenticationInfo"); final UsernamePasswordToken upt = (UsernamePasswordToken)token; final String user = upt.getUsername(); String authUser = user; final String password=new String(upt.getPassword()); String authPassword = password; if(singleton.mbc!=null) { try { final String oldBath = "Basic " + Symm.base64noSplit.encode(user+':'+password); String bath = singleton.mbc.convert(singleton.access, oldBath); if(bath!=oldBath) { bath = Symm.base64noSplit.decode(bath.substring(6)); int colon = bath.indexOf(':'); if(colon>=0) { authUser = bath.substring(0, colon); authPassword = bath.substring(colon+1); } } } catch (IOException e) { singleton.access.log(e); } } String err; try { err = singleton.authn.validate(authUser,authPassword); if(err != null) { singleton.access.log(Level.INFO, err); throw new AuthenticationException(err); } } catch (IOException e) { singleton.access.log(e,"Credential cannot be validated"); } return new AAFAuthenticationInfo( singleton.access, user, password ); } @Override protected void assertCredentialsMatch(AuthenticationToken atoken, AuthenticationInfo ai)throws AuthenticationException { logger.debug("AAFRealm.assertCredentialsMatch"); if(ai instanceof AAFAuthenticationInfo) { if(!((AAFAuthenticationInfo)ai).matches(atoken)) { throw new AuthenticationException("Credentials do not match"); } } else { throw new AuthenticationException("AuthenticationInfo is not an AAFAuthenticationInfo"); } } @Override protected AAFAuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { logger.debug("AAFRealm.doGetAuthorizationInfo"); Principal bait = (Principal)principals.getPrimaryPrincipal(); Principal newBait = bait; if(singleton.idMap!=null) { final String newID = singleton.idMap.get(bait.getName()); if(newID!=null) { singleton.access.printf(Level.INFO,"Successful authentication Translation %s to %s",bait.getName(), newID); newBait = new Principal() { @Override public String getName() { return newID; } }; } } List pond = new ArrayList<>(); singleton.authz.fishAll(newBait,pond); return new AAFAuthorizationInfo(singleton.access,bait,pond); } @Override public boolean supports(AuthenticationToken token) { // Only one was being loaded. If more are needed uncomment the multi-class mode return token instanceof UsernamePasswordToken; // return singleton.supports.contains(token.getClass()); } @Override public String getName() { return AAF_REALM; } private AAFPermission aafPerm(String permission) { String[] pa = Split.splitTrim('|', permission); switch(pa.length) { case 3: return new AAFPermission(null,pa[0],pa[1],pa[2]); case 4: return new AAFPermission(pa[0],pa[1],pa[2],pa[3]); default: return null; } } /* @Override public boolean isPermitted(PrincipalCollection principals, String permission) { logger.debug("AAFRealm.isPermitted(principals,permission)"); AAFPermission ap = aafPerm(permission); if(ap!=null) { return singleton.authz.fish((Principal)principals.getPrimaryPrincipal(), ap); } return false; } @Override protected boolean isPermitted(org.apache.shiro.authz.Permission permission, AuthorizationInfo info) { logger.debug("AAFRealm.isPermitted(shiro.Permission,AuthorizationInfo)"); if(info instanceof AAFAuthorizationInfo) { AAFPermission ap = aafPerm(permission.toString()); if(ap!=null) { return singleton.authz.fish(((AAFAuthorizationInfo)info).principal(), ap); } return false; } return super.isPermitted(permission, info); } @Override protected boolean[] isPermitted(List permissions, AuthorizationInfo info) { logger.debug("AAFRealm.isPermitted(List,AuthorizationInfo)"); if(info instanceof AAFAuthorizationInfo) { boolean rv[] = new boolean[permissions.size()]; int i=0; for(org.apache.shiro.authz.Permission sp : permissions) { AAFPermission ap = aafPerm(sp.toString()); if(ap!=null) { rv[i++]=singleton.authz.fish(((AAFAuthorizationInfo)info).principal(), ap); } else { rv[i++]=false; } } return rv; } return super.isPermitted(permissions, info); } */ }