Batch, Remove unneeded Classes, refine, etc
[aaf/authz.git] / auth / auth-batch / src / main / java / org / onap / aaf / auth / batch / reports / Analyze.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  *
7  * Modifications Copyright (C) 2019 IBM.
8  * ===========================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END====================================================
21  *
22  */
23
24 package org.onap.aaf.auth.batch.reports;
25
26 import java.io.File;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 import java.security.cert.Certificate;
30 import java.security.cert.CertificateException;
31 import java.security.cert.X509Certificate;
32 import java.util.Date;
33 import java.util.GregorianCalendar;
34 import java.util.HashMap;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Map.Entry;
39 import java.util.Set;
40 import java.util.TreeMap;
41 import java.util.TreeSet;
42 import java.util.UUID;
43
44 import org.onap.aaf.auth.batch.Batch;
45 import org.onap.aaf.auth.batch.approvalsets.Pending;
46 import org.onap.aaf.auth.batch.approvalsets.Ticket;
47 import org.onap.aaf.auth.batch.helpers.Approval;
48 import org.onap.aaf.auth.batch.helpers.Cred;
49 import org.onap.aaf.auth.batch.helpers.Cred.Instance;
50 import org.onap.aaf.auth.batch.helpers.ExpireRange;
51 import org.onap.aaf.auth.batch.helpers.ExpireRange.Range;
52 import org.onap.aaf.auth.batch.helpers.Future;
53 import org.onap.aaf.auth.batch.helpers.LastNotified;
54 import org.onap.aaf.auth.batch.helpers.Role;
55 import org.onap.aaf.auth.batch.helpers.UserRole;
56 import org.onap.aaf.auth.batch.helpers.X509;
57 import org.onap.aaf.auth.dao.cass.CredDAO;
58 import org.onap.aaf.auth.dao.cass.UserRoleDAO;
59 import org.onap.aaf.auth.env.AuthzTrans;
60 import org.onap.aaf.auth.org.Organization.Identity;
61 import org.onap.aaf.auth.org.OrganizationException;
62 import org.onap.aaf.cadi.configure.Factory;
63 import org.onap.aaf.cadi.util.CSV;
64 import org.onap.aaf.misc.env.APIException;
65 import org.onap.aaf.misc.env.Env;
66 import org.onap.aaf.misc.env.TimeTaken;
67 import org.onap.aaf.misc.env.Trans;
68 import org.onap.aaf.misc.env.util.Chrono;
69
70
71 public class Analyze extends Batch {
72         private static final int unknown=0;
73     private static final int owner=1;
74     private static final int supervisor=2;
75     private static final int total=0;
76     private static final int pending=1;
77     private static final int approved=2;
78     
79     
80         public static final String NEED_APPROVALS = "NeedApprovals";
81         private static final String EXTEND = "Extend";
82         private static final String EXPIRED_OWNERS = "ExpiredOwners";
83         private static final String CSV = ".csv";
84         private static final String INFO = "info";
85         private int minOwners;
86         private Map<String, CSV.Writer> writerList;
87         private ExpireRange expireRange;
88         private Date deleteDate;
89         private CSV.Writer deleteCW;
90         private CSV.Writer needApproveCW;
91         private CSV.Writer extendCW;
92         private Range futureRange;
93         private final String sdate;
94         private LastNotified ln;
95         
96         public Analyze(AuthzTrans trans) throws APIException, IOException, OrganizationException {
97         super(trans.env());
98         trans.info().log("Starting Connection Process");
99         
100         TimeTaken tt0 = trans.start("Cassandra Initialization", Env.SUB);
101         try {
102             TimeTaken tt = trans.start("Connect to Cluster", Env.REMOTE);
103             try {
104                 session = cluster.connect();
105             } finally {
106                 tt.done();
107             }
108             
109             // Load Cred.  We don't follow Visitor, because we have to gather up everything into Identity Anyway
110             Cred.load(trans, session);
111
112             minOwners=1;
113
114             // Create Intermediate Output 
115             writerList = new HashMap<>();
116             
117             expireRange = new ExpireRange(trans.env().access());
118             sdate = Chrono.dateOnlyStamp(now);
119             for( List<Range> lr : expireRange.ranges.values()) {
120                 for(Range r : lr ) {
121                         if(writerList.get(r.name())==null) {
122                         File file = new File(logDir(),r.name() + sdate +CSV);
123                         CSV csv = new CSV(env.access(),file);
124                         CSV.Writer cw = csv.writer(false);
125                         cw.row(INFO,r.name(),sdate,r.reportingLevel());
126                         writerList.put(r.name(),cw);
127                         if("Delete".equals(r.name())) {
128                                 deleteDate = r.getEnd();
129                                 deleteCW = cw;
130                         }
131                         trans.init().log("Creating File:",file.getAbsolutePath());
132                         }
133                 }
134             }
135             
136             // Setup New Approvals file
137             futureRange = expireRange.newFutureRange();
138             File file = new File(logDir(),NEED_APPROVALS + sdate +CSV);
139             CSV approveCSV = new CSV(env.access(),file);
140             needApproveCW = approveCSV.writer();
141             needApproveCW.row(INFO,NEED_APPROVALS,sdate,1);
142             writerList.put(NEED_APPROVALS,needApproveCW);
143             
144             // Setup Extend Approvals file
145             file = new File(logDir(),EXTEND + sdate +CSV);
146             CSV extendCSV = new CSV(env.access(),file);
147             extendCW = extendCSV.writer();
148             extendCW.row(INFO,EXTEND,sdate,1);
149             writerList.put(EXTEND,extendCW);
150             
151             // Load full data of the following
152             Approval.load(trans, session, Approval.v2_0_17);
153             Role.load(trans, session);
154             ln = new LastNotified(session);
155
156         } finally {
157             tt0.done();
158         }
159     }
160
161     @Override
162     protected void run(AuthzTrans trans) {
163         AuthzTrans noAvg = trans.env().newTransNoAvg();
164         
165         ////////////////////
166         // Load all Notifieds, and either add to local Data, or mark for Deletion.
167         ln.loadAll(noAvg,expireRange.approveDelete,deleteCW);
168         
169                 ////////////////////
170                 final Map<UUID,Ticket> goodTickets = new TreeMap<>();
171         TimeTaken tt = trans.start("Analyze Expired Futures",Trans.SUB);
172         try {
173                         Future.load(noAvg, session, Future.withConstruct, fut -> {
174                                 List<Approval> appls = Approval.byTicket.get(fut.id());
175                                 if(!futureRange.inRange(fut.expires())) {
176                                         deleteCW.comment("Future %s expired", fut.id());
177                                         Future.row(deleteCW,fut);
178                                         if(appls!=null) {
179                                                 for(Approval a : appls) {
180                                                         Approval.row(deleteCW, a);
181                                                 }
182                                         }
183                                 } else if(appls==null) { // Orphaned Future (no Approvals)
184                                         deleteCW.comment("Future is Orphaned");
185                                         Future.row(deleteCW,fut);
186                                 } else  {
187                                         goodTickets.put(fut.fdd.id, new Ticket(fut));
188                                 }
189                         });
190         } finally {
191                 tt.done();
192         }
193                 
194         Set<String> approvers = new TreeSet<>();
195         tt = trans.start("Connect Approvals with Futures",Trans.SUB);
196         try {
197                         for(Approval appr : Approval.list) {
198                                 Ticket ticket=null;
199                                 UUID ticketID = appr.getTicket();
200                                 if(ticketID!=null) {
201                                         ticket = goodTickets.get(appr.getTicket());
202                                 }
203                                 if(ticket == null) { // Orphaned Approvals, no Futures
204                                         deleteCW.comment("Approval is Orphaned");
205                                         Approval.row(deleteCW, appr);
206                                 } else {
207                                         ticket.approvals.add(appr); // add to found Ticket
208                                         approvers.add(appr.getApprover());
209                                 }
210                         }
211         } finally {
212                 tt.done();
213         }
214
215                 /* Run through all Futures, and see if 
216                  * 1) they have been executed (no longer valid)
217                  * 2) The current Approvals indicate they can proceed 
218                  */
219                 Map<String,Pending> pendingApprs = new HashMap<>();
220                 Map<String,Pending> pendingTemp = new HashMap<>();
221
222                 // Convert Good Tickets to keyed User/Role for UserRole Step
223                 Map<String,Ticket> mur = new TreeMap<>();
224                 String approver;
225                 
226                 tt = trans.start("Analyze Good Tickets",Trans.SUB);
227                 try {
228                         for(Ticket ticket : goodTickets.values()) {
229                                 try {
230                                         pendingTemp.clear();
231                                         switch(ticket.f.target()) {
232                                                 case "user_role":
233                                                         int state[][] = new int[3][3];
234                                                         int type;
235                                                                         
236                                                         for(Approval appr : ticket.approvals) {
237                                                                 switch(appr.getType()) {
238                                                                         case "owner":
239                                                                                 type=owner;
240                                                                                 break;
241                                                                         case "supervisor":
242                                                                                 type=supervisor;
243                                                                                 break;
244                                                                         default:
245                                                                                 type=0;
246                                                                 }
247                                                                 ++state[type][total]; // count per type
248                                                                 switch(appr.getStatus()) {
249                                                                         case "pending":
250                                                                                 ++state[type][pending];
251                                                                                 approver = appr.getApprover();
252                                                                                 Pending n = pendingTemp.get(approver);
253                                                                                 if(n==null) {
254                                                                                         Date lastNotified = ln.lastNotified(approver,"ur",ticket.f.fdd.target_key);
255                                                                                         pendingTemp.put(approver,new Pending(lastNotified));
256                                                                                 } else {
257                                                                                         n.inc();
258                                                                                 }
259                                                                                 break;
260                                                                         case "approved":
261                                                                                 ++state[type][approved];
262                                                                                 break;
263                                                                         default:
264                                                                                 ++state[type][unknown];
265                                                                 }
266                                                         }
267                                                         
268                                                         // To Approve:
269                                                         // Always must have at least 1 owner
270                                                         if((state[owner][total]>0 && state[owner][approved]>0) &&
271                                                                 // If there are no Supervisors, that's ok
272                                                             (state[supervisor][total]==0 || 
273                                                             // But if there is a Supervisor, they must have approved 
274                                                             (state[supervisor][approved]>0))) {
275                                                                         UserRoleDAO.Data urdd = new UserRoleDAO.Data();
276                                                                         try {
277                                                                                 urdd.reconstitute(ticket.f.fdd.construct);
278                                                                                 if(urdd.expires.before(ticket.f.expires())) {
279                                                                                         extendCW.row("extend_ur",urdd.user,urdd.role,ticket.f.expires());
280                                                                                 }
281                                                                         } catch (IOException e) {
282                                                                                 trans.error().log("Could not reconstitute UserRole");
283                                                                         }
284                                                         } else { // Load all the Pending.
285                                                                 for(Entry<String, Pending> es : pendingTemp.entrySet()) {
286                                                                         Pending p = pendingApprs.get(es.getKey());
287                                                                         if(p==null) {
288                                                                                 pendingApprs.put(es.getKey(), es.getValue());
289                                                                         } else {
290                                                                                 p.inc(es.getValue());
291                                                                         }
292                                                                 }
293                                                         }
294                                                         break;
295                                         }
296                                 } finally {
297                                         if("user_role".equals(ticket.f.fdd.target)) {
298                                                 String key = ticket.f.fdd.target_key; 
299                                                 if(key!=null) {
300                                                         mur.put(key, ticket);
301                                                 }
302                                         }
303                                 }
304                         }
305                 } finally {
306                         tt.done();
307                 }
308
309                 // Good Tickets no longer needed
310                 goodTickets.clear();
311
312                 /**
313                  * Decide to Notify about Approvals, based on activity/last Notified
314                  */
315                 tt = trans.start("Analyze Approval Reminders", Trans.SUB);
316                 try {
317                         GregorianCalendar gc = new GregorianCalendar();
318                         gc.add(GregorianCalendar.DAY_OF_WEEK, 5);
319                         Date remind = gc.getTime();
320                         
321                         for(Entry<String, Pending> es : pendingApprs.entrySet()) {
322                                 Pending p = es.getValue();
323                                 if(p.newApprovals() 
324                                                 || p.earliest() == null 
325                                                 || p.earliest().after(remind)) {
326                                         p.row(needApproveCW,es.getKey());
327                                 }
328                         }
329                 } finally {
330                         tt.done();
331                 }
332                 
333                 // clear out Approval Intermediates
334                 pendingTemp = null;
335                 pendingApprs = null;
336                 
337                 /**
338                    Run through User Roles.  
339                    Owners are treated specially in next section.
340                    Regular roles are checked against Date Ranges.  If match Date Range, write out to appropriate file.
341                 */              
342                 try {
343                         tt = trans.start("Analyze UserRoles, storing Owners",Trans.SUB);
344                         Set<String> specialCommented = new HashSet<>();
345                         Map<String, Set<UserRole>> owners = new TreeMap<>();
346                         try {
347                                 UserRole.load(noAvg, session, UserRole.v2_0_11, ur -> {
348                                         Identity identity;
349                                         try {
350                                                 identity = trans.org().getIdentity(noAvg,ur.user());
351                                                 if(identity==null) {
352                                                         // Candidate for Delete, but not Users if Special
353                                                         String id = ur.user();
354                                                         for(String s : specialDomains) {
355                                                                 if(id.endsWith(s)) {
356                                                                         if(!specialCommented.contains(id)) {
357                                                                                 deleteCW.comment("ID %s is part of special Domain %s (UR Org Check)", id,s);
358                                                                                 specialCommented.add(id);
359                                                                         }
360                                                                         return;
361                                                                 }
362                                                         }
363                                                         if(specialNames.contains(id)) {
364                                                                 if(!specialCommented.contains(id)) {
365                                                                         deleteCW.comment("ID %s is a special ID  (UR Org Check)", id);
366                                                                         specialCommented.add(id);
367                                                                 }
368                                                                 return;
369                                                         }
370                                                         ur.row(deleteCW, UserRole.UR,"Not in Organization");
371                                                         return;
372                                                 } else if(Role.byName.get(ur.role())==null) {
373                                                         ur.row(deleteCW, UserRole.UR,String.format("Role %s does not exist", ur.role()));
374                                                         return;
375                                                 }
376                                                 // Just let expired UserRoles sit until deleted
377                                                 if(futureRange.inRange(ur.expires())) {
378                                                         if(!mur.containsKey(ur.user() + '|' + ur.role())) {
379                                                                 // Cannot just delete owners, unless there is at least one left. Process later
380                                                                 if ("owner".equals(ur.rname())) {
381                                                                         Set<UserRole> urs = owners.get(ur.role());
382                                                                         if (urs == null) {
383                                                                                 urs = new HashSet<UserRole>();
384                                                                                 owners.put(ur.role(), urs);
385                                                                         }
386                                                                         urs.add(ur);
387                                                                 } else {
388                                                                         Range r = writeAnalysis(noAvg,ur);
389                                                                         if(r!=null) {
390                                                                                 Approval existing = findApproval(ur);
391                                                                                 if(existing==null) {
392                                                                                         ur.row(needApproveCW,UserRole.APPROVE_UR);
393                                                                                 }
394                                                                         }
395                                                                 }
396                                                         }
397                                                 }
398                                         } catch (OrganizationException e) {
399                                                 noAvg.error().log(e);
400                                         }
401                                 });
402                         } finally {
403                                 tt.done();
404                         }
405                 
406                         /**
407                           Now Process Owners, one owner Role at a time, ensuring one is left,
408                           preferably a good one. If so, process the others as normal. 
409                           
410                           Otherwise, write to ExpiredOwners Report
411                         */
412                         tt = trans.start("Analyze Owners Separately",Trans.SUB);
413                         try {
414                                 if (!owners.values().isEmpty()) {
415                                         File file = new File(logDir(), EXPIRED_OWNERS + sdate + CSV);
416                                         final CSV ownerCSV = new CSV(env.access(),file);
417                                         CSV.Writer expOwner = ownerCSV.writer();
418                                         expOwner.row(INFO,EXPIRED_OWNERS,sdate,2);
419
420                                         try {
421                                                 for (Set<UserRole> sur : owners.values()) {
422                                                         int goodOwners = 0;
423                                                         for (UserRole ur : sur) {
424                                                                 if (ur.expires().after(now)) {
425                                                                         ++goodOwners;
426                                                                 }
427                                                         }
428         
429                                                         for (UserRole ur : sur) {
430                                                                 if (goodOwners >= minOwners) {
431                                                                         Range r = writeAnalysis(noAvg, ur);
432                                                                         if(r!=null) {
433                                                                                 Approval existing = findApproval(ur);
434                                                                                 if(existing==null) {
435                                                                                         ur.row(needApproveCW,UserRole.APPROVE_UR);
436                                                                                 }
437                                                                         }
438                                                                 } else {
439                                                                         expOwner.row("owner",ur.role(), ur.user(), Chrono.dateOnlyStamp(ur.expires()));
440                                                                         Approval existing = findApproval(ur);
441                                                                         if(existing==null) {
442                                                                                 ur.row(needApproveCW,UserRole.APPROVE_UR);
443                                                                         }
444                                                                 }
445                                                         }
446                                                 }
447                                         } finally {
448                                                 if(expOwner!=null) {
449                                                         expOwner.close();
450                                                 }
451                                         }
452                                 }
453                         } finally {
454                                 tt.done();
455                         }
456                         
457                         /**
458                          * Check for Expired Credentials
459                          * 
460                          * 
461                          */
462                         tt = trans.start("Analyze Expired Credentials",Trans.SUB);
463                         try {
464                                 for (Cred cred : Cred.data.values()) {
465                                 List<Instance> linst = cred.instances;
466                                 if(linst!=null) {
467                                         Instance lastBath = null;
468                                         for(Instance inst : linst) {
469         //                                      if(inst.attn>0) {
470         //                                              writeAnalysis(trans, cred, inst);
471         //                                              // Special Behavior: only eval the LAST Instance
472         //                                      } else 
473                                                 // All Creds go through Life Cycle
474                                                 if(deleteDate!=null && inst.expires.before(deleteDate)) {
475                                                         writeAnalysis(noAvg, cred, inst); // will go to Delete
476                                                 // Basic Auth has Pre-EOL notifications IF there is no Newer Credential
477                                                 } else if (inst.type == CredDAO.BASIC_AUTH || inst.type == CredDAO.BASIC_AUTH_SHA256) {
478                                                         if(lastBath==null || lastBath.expires.before(inst.expires)) {
479                                                                 lastBath = inst;
480                                                         }
481                                                 }
482                                         }
483                                         if(lastBath!=null) {
484                                                 writeAnalysis(noAvg, cred, lastBath);
485                                         }
486                                 }
487                                 }
488                         } finally {
489                                 tt.done();
490                         }
491
492                         ////////////////////
493                         tt = trans.start("Analyze Expired X509s",Trans.SUB);
494                         try {
495                                 X509.load(noAvg, session, x509 -> {
496                                         try {
497                                                 for(Certificate cert : Factory.toX509Certificate(x509.x509)) {
498                                                         writeAnalysis(noAvg, x509, (X509Certificate)cert);
499                                                 }
500                                         } catch (CertificateException | IOException e) {
501                                                 noAvg.error().log(e, "Error Decrypting X509");
502                                         }
503                                 });
504                         } finally {
505                                 tt.done();
506                         }
507                 } catch (FileNotFoundException e) {
508                         noAvg.info().log(e);
509                 }
510         }
511  
512         private Approval findApproval(UserRole ur) {
513                 Approval existing = null;
514                 List<Approval> apprs = Approval.byUser.get(ur.user());
515                 if(apprs!=null) {
516                         for(Approval appr : apprs) {
517                                 if(ur.role().equals(appr.getRole()) &&
518                                         appr.getMemo().contains(Chrono.dateOnlyStamp(ur.expires()))) {
519                                                 existing = appr; 
520                                 }
521                         }
522                 }
523                 return existing;
524         }
525
526         private Range writeAnalysis(AuthzTrans trans, UserRole ur) {
527                 Range r = expireRange.getRange("ur", ur.expires());
528                 if(r!=null) {
529                         Date lnd = ln.lastNotified(LastNotified.newKey(ur));
530                         // Note: lnd is NEVER null
531                         Identity i;
532                         try {
533                                 i = org.getIdentity(trans, ur.user());
534                         } catch (OrganizationException e) {
535                                 i=null;
536                         }
537                         if(r.needsContact(lnd,i)) {                             
538                                 CSV.Writer cw = writerList.get(r.name());
539                                 if(cw!=null) {
540                                         ur.row(cw,UserRole.UR);
541                                 }
542                         }
543                 }
544                 return r;
545         }
546     
547     private void writeAnalysis(AuthzTrans trans, Cred cred, Instance inst) {
548         if(cred!=null && inst!=null) {
549                         Range r = expireRange.getRange("cred", inst.expires);
550                         if(r!=null) {
551                                 Date lnd = ln.lastNotified(LastNotified.newKey(cred,inst));
552                                 // Note: lnd is NEVER null
553                                 Identity i;
554                                 try {
555                                         i = org.getIdentity(trans, cred.id);
556                                 } catch (OrganizationException e) {
557                                         i=null;
558                                 }
559                                 if(r.needsContact(lnd,i)) {                             
560                                         CSV.Writer cw = writerList.get(r.name());
561                                         if(cw!=null) {
562                                                 cred.row(cw,inst);
563                                         }
564                                 }
565                         }
566         }
567         }
568
569     private void writeAnalysis(AuthzTrans trans, X509 x509, X509Certificate x509Cert) throws IOException {
570                 Range r = expireRange.getRange("x509", x509Cert.getNotAfter());
571                 if(r!=null) {
572                         Date lnd = ln.lastNotified(LastNotified.newKey(x509,x509Cert));
573                         // Note: lnd is NEVER null
574                         Identity i;
575                         try {
576                                 i = org.getIdentity(trans, x509.id);
577                         } catch (OrganizationException e) {
578                                 i=null;
579                         }
580                         if(r.needsContact(lnd,i)) {
581                                 CSV.Writer cw = writerList.get(r.name());
582                                 if(cw!=null) {
583                                         x509.row(cw,x509Cert);
584                                 }
585                         }
586                 }
587         }
588     
589     @Override
590     protected void _close(AuthzTrans trans) {
591         session.close();
592         for(CSV.Writer cw : writerList.values()) {
593                 cw.close();
594         }
595     }
596
597 }