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