Add Multi-Realm class handling
[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.List;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.Set;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ConcurrentSkipListSet;
32
33 import org.apache.shiro.authc.AuthenticationException;
34 import org.apache.shiro.authc.AuthenticationInfo;
35 import org.apache.shiro.authc.AuthenticationToken;
36 import org.apache.shiro.authc.UsernamePasswordToken;
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.v2_0.AAFAuthn;
46 import org.onap.aaf.cadi.aaf.v2_0.AAFCon;
47 import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
48 import org.onap.aaf.cadi.config.Config;
49 import org.onap.aaf.cadi.filter.MapBathConverter;
50 import org.onap.aaf.cadi.util.CSV;
51 import org.onap.aaf.misc.env.APIException;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 public class AAFRealm extends AuthorizingRealm {
56         public static final String AAF_REALM = "AAFRealm";
57         private static final Logger logger =  LoggerFactory.getLogger(AAFRealm.class);
58         private static Singleton singleton = Singleton.singleton();
59         
60         private static class Singleton {
61                 private AAFCon<?> acon;
62                 private AAFAuthn<?> authn;
63                 private Set<Class<? extends AuthenticationToken>> supports;
64                 private AAFLurPerm authz;
65                 private MapBathConverter mbc;
66                 private Map<String,String> idMap;
67                 private Singleton() {
68                         mbc = null;
69                         idMap = null;
70                         String cadi_prop_files = access.getProperty(Config.CADI_PROP_FILES);
71                         if(cadi_prop_files==null) {
72                                 String msg = Config.CADI_PROP_FILES + " in VM Args is required to initialize AAFRealm.";
73                                 access.log(Level.INFO,msg);
74                                 throw new RuntimeException(msg);
75                         } else {
76                                 try {
77                                         acon = AAFCon.newInstance(access);
78                                         authn = acon.newAuthn();
79                                         authz = acon.newLur(authn);
80                                         
81                                         final String csv = access.getProperty(Config.CADI_BATH_CONVERT);
82                                         if(csv!=null) {
83                                                 try {
84                                                         mbc = new MapBathConverter(access, new CSV(csv));
85                                                         access.log(Level.INFO, "MapBathConversion enabled with file ",csv);
86                                                         idMap = new ConcurrentHashMap<String,String>();
87                                                         // Load 
88                                                         for(Entry<String, String> es : mbc.map().entrySet()) {
89                                                                 String oldID = es.getKey();
90                                                                 if(oldID.startsWith("Basic ")) {
91                                                                         oldID = Symm.base64noSplit.decode(oldID.substring(6));
92                                                                         int idx = oldID.indexOf(':');
93                                                                         if(idx>=0) {
94                                                                                 oldID = oldID.substring(0, idx);
95                                                                         }
96                                                                 }
97                                                                 String newID = es.getValue();
98                                                                 if(newID.startsWith("Basic ")) {
99                                                                         newID = Symm.base64noSplit.decode(newID.substring(6));
100                                                                         int idx = newID.indexOf(':');
101                                                                         if(idx>=0) {
102                                                                                 newID = newID.substring(0, idx);
103                                                                         }
104                                                                 }
105                                                                 idMap.put(oldID,newID);
106                                                                 
107                                                         }
108                                                 } catch (IOException e) {
109                                                         access.log(e);
110                                                 }
111                                         }
112                                 } catch (APIException | CadiException | LocatorException e) {
113                                         String msg = "Cannot initiate AAFRealm";
114                                         access.log(Level.ERROR,e,msg);
115                                         throw new RuntimeException(msg,e);
116                                 }
117                         }
118                         supports = new ConcurrentSkipListSet<>();
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.warn(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.warn(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 AUDIT:
197                                                         return logger.isWarnEnabled();
198                                                 case DEBUG:
199                                                         return logger.isDebugEnabled();
200                                                 case ERROR:
201                                                         return logger.isErrorEnabled();
202                                                 case INFO:
203                                                 case INIT:
204                                                         return logger.isInfoEnabled();
205                                                 case NONE:
206                                                         return false;
207                                                 case TRACE:
208                                                         return logger.isTraceEnabled();
209                                                 case WARN:
210                                                         return logger.isWarnEnabled();
211                                 
212                                         }
213                                 }
214                                 return false;
215                         }
216                 };
217         }               
218         
219         /**
220          * 
221          * There appears to be no configuration objects or references available for CADI to start with.
222          *  
223          */
224
225         @Override
226         protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
227                 final UsernamePasswordToken upt = (UsernamePasswordToken)token;
228                 final String user = upt.getUsername();
229                 String authUser = user; 
230                 final String password=new String(upt.getPassword());
231                 String authPassword = password;
232                 if(singleton.mbc!=null) {
233                         try {
234                                 final String oldBath = "Basic " + Symm.base64noSplit.encode(user+':'+password);
235                                 String bath = singleton.mbc.convert(singleton.access, oldBath);
236                                 if(bath!=oldBath) {
237                                         bath = Symm.base64noSplit.decode(bath.substring(6));
238                                         int colon = bath.indexOf(':');
239                                         if(colon>=0) {
240                                                 authUser = bath.substring(0, colon);
241                                                 authPassword = bath.substring(colon+1); 
242                                         }
243                                 }
244                         } catch (IOException e) {
245                                 singleton.access.log(e);
246                         } 
247                 }
248                 String err;
249                 try {
250                         err = singleton.authn.validate(authUser,authPassword);
251                         if(err != null) {
252                                 singleton.access.log(Level.INFO, err);
253                                 throw new AuthenticationException(err);
254                         }
255
256                 } catch (IOException e) {
257                         singleton.access.log(e,"Credential cannot be validated");
258                 }
259                 
260             return new AAFAuthenticationInfo(
261                         singleton.access,
262                         user,
263                         password
264             );
265         }
266
267         @Override
268         protected void assertCredentialsMatch(AuthenticationToken atoken, AuthenticationInfo ai)throws AuthenticationException {
269                 
270                 if(ai instanceof AAFAuthenticationInfo) {
271                         if(!((AAFAuthenticationInfo)ai).matches(atoken)) {
272                                 throw new AuthenticationException("Credentials do not match");
273                         }
274                         
275                 } else {
276                         throw new AuthenticationException("AuthenticationInfo is not an AAFAuthenticationInfo");
277                 
278                 }
279         }
280
281
282
283
284         @Override
285         protected AAFAuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
286                 Principal bait = (Principal)principals.getPrimaryPrincipal();
287                 Principal newBait = bait;
288                 if(singleton.idMap!=null) {
289                         final String newID = singleton.idMap.get(bait.getName());
290                         singleton.access.printf(Level.INFO,"Successful authentication attempt by %s",bait.getName()); 
291                         if(newID!=null) {
292                                 newBait = new Principal() {
293                                         @Override
294                                         public String getName() {
295                                                 return newID;
296                                         }
297                                 };
298                         }
299                 }
300                 List<Permission> pond = new ArrayList<>();
301                 singleton.authz.fishAll(newBait,pond);
302                 return new AAFAuthorizationInfo(singleton.access,bait,pond);
303         }
304
305         @Override
306         public boolean supports(AuthenticationToken token) {
307                 return singleton.supports.contains(token.getClass());
308         }
309
310         @Override
311         public String getName() {
312                 return AAF_REALM;
313         }
314
315 }