eb8bc60b5cb3e9db1c848fceb6f340171b51d88b
[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.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.Permission;
42 import org.onap.aaf.cadi.PropAccess;
43 import org.onap.aaf.cadi.Symm;
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.misc.env.APIException;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 public class AAFRealm extends AuthorizingRealm {
55         public static final String AAF_REALM = "AAFRealm";
56         private static final Logger logger =  LoggerFactory.getLogger(AAFRealm.class);
57         private static Singleton singleton = Singleton.singleton();
58         
59         private static class Singleton {
60                 private AAFCon<?> acon;
61                 private AAFAuthn<?> authn;
62 //              private Set<Class<? extends AuthenticationToken>> supports;
63                 private AAFLurPerm authz;
64                 private MapBathConverter mbc;
65                 private Map<String,String> idMap;
66                 private Singleton() {
67                         mbc = null;
68                         idMap = null;
69                         String cadi_prop_files = access.getProperty(Config.CADI_PROP_FILES);
70                         if(cadi_prop_files==null) {
71                                 String msg = Config.CADI_PROP_FILES + " in VM Args is required to initialize AAFRealm.";
72                                 access.log(Level.INFO,msg);
73                                 throw new RuntimeException(msg);
74                         } else {
75                                 try {
76                                         acon = AAFCon.newInstance(access);
77                                         authn = acon.newAuthn();
78                                         authz = acon.newLur(authn);
79                                         
80                                         final String csv = access.getProperty(Config.CADI_BATH_CONVERT);
81                                         if(csv!=null) {
82                                                 try {
83                                                         mbc = new MapBathConverter(access, new CSV(access,csv));
84                                                         access.log(Level.INFO, "MapBathConversion enabled with file ",csv);
85                                                         idMap = Collections.synchronizedMap(new TreeMap<String,String>());
86                                                         // Load 
87                                                         for(Entry<String, String> es : mbc.map().entrySet()) {
88                                                                 String oldID = es.getKey();
89                                                                 if(oldID.startsWith("Basic ")) {
90                                                                         oldID = Symm.base64noSplit.decode(oldID.substring(6));
91                                                                         int idx = oldID.indexOf(':');
92                                                                         if(idx>=0) {
93                                                                                 oldID = oldID.substring(0, idx);
94                                                                         }
95                                                                 }
96                                                                 String newID = es.getValue();
97                                                                 if(newID.startsWith("Basic ")) {
98                                                                         newID = Symm.base64noSplit.decode(newID.substring(6));
99                                                                         int idx = newID.indexOf(':');
100                                                                         if(idx>=0) {
101                                                                                 newID = newID.substring(0, idx);
102                                                                         }
103                                                                 }
104                                                                 idMap.put(oldID,newID);
105                                                         }
106                                                 } catch (IOException e) {
107                                                         access.log(e);
108                                                 }
109                                         }
110                                 } catch (APIException | CadiException | LocatorException e) {
111                                         String msg = "Cannot initiate AAFRealm";
112                                         access.log(Level.ERROR,e,msg);
113                                         throw new RuntimeException(msg,e);
114                                 }
115                         }
116                         
117                         // There is only one of these.  If there are more, put back 
118 //                      supports = Collections.synchronizedSet(new HashSet<>());
119 //                      supports.add(UsernamePasswordToken.class);
120                 }
121                 
122                 public static synchronized Singleton singleton() {
123                         if(singleton==null) {
124                                 singleton = new Singleton();
125                         }
126                         return singleton;
127                 }
128
129                 // pick up cadi_prop_files from VM_Args
130                 private final PropAccess access = new PropAccess() {
131                         @Override
132                         public void log(Exception e, Object... elements) {
133                                 logger.error(buildMsg(Level.ERROR, elements).toString(),e);
134                         }
135                 
136                         @Override
137                         public void log(Level level, Object... elements) {
138                                 if(willLog(level)) {
139                                         String str = buildMsg(level, elements).toString();
140                                         switch(level) {
141                                                 case WARN:
142                                                 case AUDIT:
143                                                         logger.warn(str);
144                                                         break;
145                                                 case DEBUG:
146                                                         logger.debug(str);
147                                                         break;
148                                                 case ERROR:
149                                                         logger.error(str);
150                                                         break;
151                                                 case INFO:
152                                                 case INIT:
153                                                         logger.info(str);
154                                                         break;
155                                                 case NONE:
156                                                         break;
157                                                 case TRACE:
158                                                         logger.trace(str);
159                                                         break;
160                                         }
161                                 }
162                         }
163                 
164                         @Override
165                         public void printf(Level level, String fmt, Object... elements) {
166                                 if(willLog(level)) {
167                                         String str = String.format(fmt, elements);
168                                         switch(level) {
169                                                 case WARN:
170                                                 case AUDIT:
171                                                         logger.warn(str);
172                                                         break;
173                                                 case DEBUG:
174                                                         logger.debug(str);
175                                                         break;
176                                                 case ERROR:
177                                                         logger.error(str);
178                                                         break;
179                                                 case INFO:
180                                                 case INIT:
181                                                         logger.info(str);
182                                                         break;
183                                                 case NONE:
184                                                         break;
185                                                 case TRACE:
186                                                         logger.trace(str);
187                                                         break;
188                                         }
189                                 }
190                         }
191                 
192                         @Override
193                         public boolean willLog(Level level) {
194                                 if(super.willLog(level)) {
195                                         switch(level) {
196                                                 case WARN:
197                                                 case AUDIT:
198                                                         return logger.isWarnEnabled();
199                                                 case DEBUG:
200                                                         return logger.isDebugEnabled();
201                                                 case ERROR:
202                                                         return logger.isErrorEnabled();
203                                                 case INFO:
204                                                 case INIT:
205                                                         return logger.isInfoEnabled();
206                                                 case NONE:
207                                                         return false;
208                                                 case TRACE:
209                                                         return logger.isTraceEnabled();
210                                         }
211                                 }
212                                 return false;
213                         }
214                 };
215         }               
216         
217         /**
218          * 
219          * There appears to be no configuration objects or references available for CADI to start with.
220          *  
221          */
222
223         @Override
224         protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
225                 final UsernamePasswordToken upt = (UsernamePasswordToken)token;
226                 final String user = upt.getUsername();
227                 String authUser = user; 
228                 final String password=new String(upt.getPassword());
229                 String authPassword = password;
230                 if(singleton.mbc!=null) {
231                         try {
232                                 final String oldBath = "Basic " + Symm.base64noSplit.encode(user+':'+password);
233                                 String bath = singleton.mbc.convert(singleton.access, oldBath);
234                                 if(bath!=oldBath) {
235                                         bath = Symm.base64noSplit.decode(bath.substring(6));
236                                         int colon = bath.indexOf(':');
237                                         if(colon>=0) {
238                                                 authUser = bath.substring(0, colon);
239                                                 authPassword = bath.substring(colon+1); 
240                                         }
241                                 }
242                         } catch (IOException e) {
243                                 singleton.access.log(e);
244                         } 
245                 }
246                 String err;
247                 try {
248                         err = singleton.authn.validate(authUser,authPassword);
249                         if(err != null) {
250                                 singleton.access.log(Level.INFO, err);
251                                 throw new AuthenticationException(err);
252                         }
253
254                 } catch (IOException e) {
255                         singleton.access.log(e,"Credential cannot be validated");
256                 }
257                 
258             return new AAFAuthenticationInfo(
259                         singleton.access,
260                         user,
261                         password
262             );
263         }
264
265         @Override
266         protected void assertCredentialsMatch(AuthenticationToken atoken, AuthenticationInfo ai)throws AuthenticationException {
267                 if(ai instanceof AAFAuthenticationInfo) {
268                         if(!((AAFAuthenticationInfo)ai).matches(atoken)) {
269                                 throw new AuthenticationException("Credentials do not match");
270                         }
271                 } else {
272                         throw new AuthenticationException("AuthenticationInfo is not an AAFAuthenticationInfo");
273                 }
274         }
275
276         @Override
277         protected AAFAuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
278                 Principal bait = (Principal)principals.getPrimaryPrincipal();
279                 Principal newBait = bait;
280                 if(singleton.idMap!=null) {
281                         final String newID = singleton.idMap.get(bait.getName());
282                         if(newID!=null) {
283                                 singleton.access.printf(Level.INFO,"Successful authentication Translation %s to %s",bait.getName(), newID); 
284                                 newBait = new Principal() {
285                                         @Override
286                                         public String getName() {
287                                                 return newID;
288                                         }
289                                 };
290                         }
291                 }
292                 List<Permission> pond = new ArrayList<>();
293                 singleton.authz.fishAll(newBait,pond);
294                 return new AAFAuthorizationInfo(singleton.access,bait,pond);
295         }
296
297         @Override
298         public boolean supports(AuthenticationToken token) {
299                 // Only one was being loaded.  If more are needed uncomment the multi-class mode
300                 return token instanceof UsernamePasswordToken;
301 //              return singleton.supports.contains(token.getClass());
302         }
303
304         @Override
305         public String getName() {
306                 return AAF_REALM;
307         }
308
309 }