Prepare for release 2.1.18
[aaf/cadi.git] / shiro / src / main / java / org / onap / aaf / cadi / shiro / AAFRealm.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21 package org.onap.aaf.cadi.shiro;
22
23 import java.io.IOException;
24 import java.security.Principal;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.TreeMap;
31
32 import org.apache.shiro.authc.AuthenticationException;
33 import org.apache.shiro.authc.AuthenticationInfo;
34 import org.apache.shiro.authc.AuthenticationToken;
35 import org.apache.shiro.authc.UsernamePasswordToken;
36 import org.apache.shiro.authz.AuthorizationInfo;
37 import org.apache.shiro.realm.AuthorizingRealm;
38 import org.apache.shiro.subject.PrincipalCollection;
39 import org.onap.aaf.cadi.Access.Level;
40 import org.onap.aaf.cadi.CadiException;
41 import org.onap.aaf.cadi.LocatorException;
42 import org.onap.aaf.cadi.Permission;
43 import org.onap.aaf.cadi.PropAccess;
44 import org.onap.aaf.cadi.Symm;
45 import org.onap.aaf.cadi.aaf.AAFPermission;
46 import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
47 import org.onap.aaf.cadi.aaf.v2_0.AAFCon;
48 import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
49 import org.onap.aaf.cadi.config.Config;
50 import org.onap.aaf.cadi.filter.MapBathConverter;
51 import org.onap.aaf.cadi.util.CSV;
52 import org.onap.aaf.cadi.util.Split;
53 import org.onap.aaf.misc.env.APIException;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 /*
58  * Note: Shiro Realm document
59  * https://shiro.apache.org/realm.html
60  */
61
62 public class AAFRealm extends AuthorizingRealm {
63         public static final String AAF_REALM = "AAFRealm";
64         private static final Logger logger =  LoggerFactory.getLogger(AAFRealm.class);
65         
66         // Package on purpose
67         static Singleton singleton = Singleton.singleton();
68         
69         public static class Singleton {
70                 public AAFCon<?> acon;
71                 public AAFAuthn<?> authn;
72                 public AAFLurPerm authz;
73 //              private Set<Class<? extends AuthenticationToken>> supports;
74                 
75                 private MapBathConverter mbc;
76                 private Map<String,String> idMap;
77                 private Singleton() {
78                         logger.info("Creating AAFRealm.Singleton");
79                         mbc = null;
80                         idMap = null;
81                         String cadi_prop_files = access.getProperty(Config.CADI_PROP_FILES);
82                         if(cadi_prop_files==null) {
83                                 String msg = Config.CADI_PROP_FILES + " in VM Args is required to initialize AAFRealm.";
84                                 access.log(Level.INFO,msg);
85                                 throw new RuntimeException(msg);
86                         } else {
87                                 try {
88                                         acon = AAFCon.newInstance(access);
89                                         authn = acon.newAuthn();
90                                         authz = acon.newLur(authn);
91                                         
92                                         final String csv = access.getProperty(Config.CADI_BATH_CONVERT);
93                                         if(csv!=null) {
94                                                 try {
95                                                         mbc = new MapBathConverter(access, new CSV(access,csv));
96                                                         access.log(Level.INFO, "MapBathConversion enabled with file ",csv);
97                                                         idMap = Collections.synchronizedMap(new TreeMap<String,String>());
98                                                         // Load 
99                                                         for(Entry<String, String> es : mbc.map().entrySet()) {
100                                                                 String oldID = es.getKey();
101                                                                 if(oldID.startsWith("Basic ")) {
102                                                                         oldID = Symm.base64noSplit.decode(oldID.substring(6));
103                                                                         int idx = oldID.indexOf(':');
104                                                                         if(idx>=0) {
105                                                                                 oldID = oldID.substring(0, idx);
106                                                                         }
107                                                                 }
108                                                                 String newID = es.getValue();
109                                                                 if(newID.startsWith("Basic ")) {
110                                                                         newID = Symm.base64noSplit.decode(newID.substring(6));
111                                                                         int idx = newID.indexOf(':');
112                                                                         if(idx>=0) {
113                                                                                 newID = newID.substring(0, idx);
114                                                                         }
115                                                                 }
116                                                                 idMap.put(oldID,newID);
117                                                         }
118                                                 } catch (IOException e) {
119                                                         access.log(e);
120                                                 }
121                                         }
122                                 } catch (APIException | CadiException | LocatorException e) {
123                                         String msg = "Cannot initiate AAFRealm";
124                                         access.log(Level.ERROR,e,msg);
125                                         throw new RuntimeException(msg,e);
126                                 }
127                         }
128                         
129                         // There is only one of these.  If there are more, put back 
130 //                      supports = Collections.synchronizedSet(new HashSet<>());
131 //                      supports.add(UsernamePasswordToken.class);
132                 }
133                 
134                 public static synchronized Singleton singleton() {
135                         if(singleton==null) {
136                                 singleton = new Singleton();
137                         }
138                         return singleton;
139                 }
140
141                 // pick up cadi_prop_files from VM_Args
142                 private final PropAccess access = new PropAccess() {
143                         @Override
144                         public void log(Exception e, Object... elements) {
145                                 logger.error(buildMsg(Level.ERROR, elements).toString(),e);
146                         }
147                 
148                         @Override
149                         public void log(Level level, Object... elements) {
150                                 if(willLog(level)) {
151                                         String str = buildMsg(level, elements).toString();
152                                         switch(level) {
153                                                 case WARN:
154                                                 case AUDIT:
155                                                         logger.warn(str);
156                                                         break;
157                                                 case DEBUG:
158                                                         logger.debug(str);
159                                                         break;
160                                                 case ERROR:
161                                                         logger.error(str);
162                                                         break;
163                                                 case INFO:
164                                                 case INIT:
165                                                         logger.info(str);
166                                                         break;
167                                                 case NONE:
168                                                         break;
169                                                 case TRACE:
170                                                         logger.trace(str);
171                                                         break;
172                                         }
173                                 }
174                         }
175                 
176                         @Override
177                         public void printf(Level level, String fmt, Object... elements) {
178                                 if(willLog(level)) {
179                                         String str = String.format(fmt, elements);
180                                         switch(level) {
181                                                 case WARN:
182                                                 case AUDIT:
183                                                         logger.warn(str);
184                                                         break;
185                                                 case DEBUG:
186                                                         logger.debug(str);
187                                                         break;
188                                                 case ERROR:
189                                                         logger.error(str);
190                                                         break;
191                                                 case INFO:
192                                                 case INIT:
193                                                         logger.info(str);
194                                                         break;
195                                                 case NONE:
196                                                         break;
197                                                 case TRACE:
198                                                         logger.trace(str);
199                                                         break;
200                                         }
201                                 }
202                         }
203                 
204                         @Override
205                         public boolean willLog(Level level) {
206                                 if(super.willLog(level)) {
207                                         switch(level) {
208                                                 case WARN:
209                                                 case AUDIT:
210                                                         return logger.isWarnEnabled();
211                                                 case DEBUG:
212                                                         return logger.isDebugEnabled();
213                                                 case ERROR:
214                                                         return logger.isErrorEnabled();
215                                                 case INFO:
216                                                 case INIT:
217                                                         return logger.isInfoEnabled();
218                                                 case NONE:
219                                                         return false;
220                                                 case TRACE:
221                                                         return logger.isTraceEnabled();
222                                         }
223                                 }
224                                 return false;
225                         }
226                 };
227         }               
228         
229         /**
230          * 
231          * There appears to be no configuration objects or references available for CADI to start with.
232          *  
233          */
234
235         @Override
236         protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
237                 logger.debug("AAFRealm.doGetAuthenticationInfo");
238                 final UsernamePasswordToken upt = (UsernamePasswordToken)token;
239                 final String user = upt.getUsername();
240                 String authUser = user; 
241                 final String password=new String(upt.getPassword());
242                 String authPassword = password;
243                 if(singleton.mbc!=null) {
244                         try {
245                                 final String oldBath = "Basic " + Symm.base64noSplit.encode(user+':'+password);
246                                 String bath = singleton.mbc.convert(singleton.access, oldBath);
247                                 if(bath!=oldBath) {
248                                         bath = Symm.base64noSplit.decode(bath.substring(6));
249                                         int colon = bath.indexOf(':');
250                                         if(colon>=0) {
251                                                 authUser = bath.substring(0, colon);
252                                                 authPassword = bath.substring(colon+1); 
253                                         }
254                                 }
255                         } catch (IOException e) {
256                                 singleton.access.log(e);
257                         }
258                 }
259                 String err;
260                 try {
261                         err = singleton.authn.validate(authUser,authPassword);
262                         if(err != null) {
263                                 singleton.access.log(Level.INFO, err);
264                                 throw new AuthenticationException(err);
265                         }
266
267                 } catch (IOException e) {
268                         singleton.access.log(e,"Credential cannot be validated");
269                 }
270                 
271             return new AAFAuthenticationInfo(
272                         singleton.access,
273                         user,
274                         password
275             );
276         }
277
278         @Override
279         protected void assertCredentialsMatch(AuthenticationToken atoken, AuthenticationInfo ai)throws AuthenticationException {
280                 logger.debug("AAFRealm.assertCredentialsMatch");
281                 if(ai instanceof AAFAuthenticationInfo) {
282                         if(!((AAFAuthenticationInfo)ai).matches(atoken)) {
283                                 throw new AuthenticationException("Credentials do not match");
284                         }
285                 } else {
286                         throw new AuthenticationException("AuthenticationInfo is not an AAFAuthenticationInfo");
287                 }
288         }
289
290         @Override
291         protected AAFAuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
292                 logger.debug("AAFRealm.doGetAuthorizationInfo");
293                 Principal bait = (Principal)principals.getPrimaryPrincipal();
294                 Principal newBait = bait;
295                 if(singleton.idMap!=null) {
296                         final String newID = singleton.idMap.get(bait.getName());
297                         if(newID!=null) {
298                                 singleton.access.printf(Level.INFO,"Successful authentication Translation %s to %s",bait.getName(), newID); 
299                                 newBait = new Principal() {
300                                         @Override
301                                         public String getName() {
302                                                 return newID;
303                                         }
304                                 };
305                         }
306                 }
307                 List<Permission> pond = new ArrayList<>();
308                 singleton.authz.fishAll(newBait,pond);
309                 return new AAFAuthorizationInfo(singleton.access,bait,pond);
310         }
311
312         @Override
313         public boolean supports(AuthenticationToken token) {
314                 // Only one was being loaded.  If more are needed uncomment the multi-class mode
315                 return token instanceof UsernamePasswordToken;
316 //              return singleton.supports.contains(token.getClass());
317         }
318
319         @Override
320         public String getName() {
321                 return AAF_REALM;
322         }
323
324         private AAFPermission aafPerm(String permission) {
325                 String[] pa = Split.splitTrim('|', permission);
326                 switch(pa.length) {
327                         case 3:
328                                 return new AAFPermission(null,pa[0],pa[1],pa[2]);
329                         case 4:
330                                 return new AAFPermission(pa[0],pa[1],pa[2],pa[3]);
331                         default:
332                                 return null;
333                 }
334         }
335 /*
336         @Override
337     public boolean isPermitted(PrincipalCollection principals, String permission) {
338                 logger.debug("AAFRealm.isPermitted(principals,permission<String>)");
339                 AAFPermission ap = aafPerm(permission);
340                 if(ap!=null) {
341                         return singleton.authz.fish((Principal)principals.getPrimaryPrincipal(), ap);
342                 }
343                 return false;
344     }
345         
346         @Override
347         protected boolean isPermitted(org.apache.shiro.authz.Permission permission, AuthorizationInfo info) {
348                 logger.debug("AAFRealm.isPermitted(shiro.Permission,AuthorizationInfo)");
349                 if(info instanceof AAFAuthorizationInfo) {
350                         AAFPermission ap = aafPerm(permission.toString());
351                         if(ap!=null) {
352                                 return singleton.authz.fish(((AAFAuthorizationInfo)info).principal(), ap);
353                         }
354                         return false;
355                 }
356                 return super.isPermitted(permission, info);
357         }
358
359         @Override
360         protected boolean[] isPermitted(List<org.apache.shiro.authz.Permission> permissions, AuthorizationInfo info) {
361                 logger.debug("AAFRealm.isPermitted(List<shiro.Permission>,AuthorizationInfo)");
362                 if(info instanceof AAFAuthorizationInfo) {
363                         boolean rv[] = new boolean[permissions.size()];
364                         int i=0;
365                         for(org.apache.shiro.authz.Permission sp : permissions) {
366                                 AAFPermission ap = aafPerm(sp.toString());
367                                 if(ap!=null) {
368                                         rv[i++]=singleton.authz.fish(((AAFAuthorizationInfo)info).principal(), ap);
369                                 } else {
370                                         rv[i++]=false;
371                                 }
372                         }
373                         return rv;
374                 } 
375                 return super.isPermitted(permissions, info);
376         }
377 */
378 }