Create Helm based Certificates for Clients
[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
110             minOwners=1;
111
112             // Create Intermediate Output 
113             writerList = new HashMap<>();
114             
115             expireRange = new ExpireRange(trans.env().access());
116             sdate = Chrono.dateOnlyStamp(now);
117             for( List<Range> lr : expireRange.ranges.values()) {
118                 for(Range r : lr ) {
119                         if(writerList.get(r.name())==null) {
120                         File file = new File(logDir(),r.name() + sdate +CSV);
121                         CSV csv = new CSV(env.access(),file);
122                         CSV.Writer cw = csv.writer(false);
123                         cw.row(INFO,r.name(),sdate,r.reportingLevel());
124                         writerList.put(r.name(),cw);
125                         if("Delete".equals(r.name())) {
126                                 deleteDate = r.getEnd();
127                                 deleteCW = cw;
128                         }
129                         trans.init().log("Creating File:",file.getAbsolutePath());
130                         }
131                 }
132             }
133             
134             // Setup New Approvals file
135             futureRange = expireRange.newFutureRange();
136             File file = new File(logDir(),NEED_APPROVALS + sdate +CSV);
137             CSV approveCSV = new CSV(env.access(),file);
138             needApproveCW = approveCSV.writer();
139             needApproveCW.row(INFO,NEED_APPROVALS,sdate,1);
140             writerList.put(NEED_APPROVALS,needApproveCW);
141             
142             // Setup Extend Approvals file
143             file = new File(logDir(),EXTEND + sdate +CSV);
144             CSV extendCSV = new CSV(env.access(),file);
145             extendCW = extendCSV.writer();
146             extendCW.row(INFO,EXTEND,sdate,1);
147             writerList.put(EXTEND,extendCW);
148             
149             // Load full data of the following
150             ln = new LastNotified(session);
151
152         } finally {
153             tt0.done();
154         }
155     }
156
157     @Override
158     protected void run(AuthzTrans trans) {
159         TimeTaken tt;
160         AuthzTrans noAvg = trans.env().newTransNoAvg();
161         
162         ////////////////////
163         // Load all Notifieds, and either add to local Data, or mark for Deletion.
164         ln.loadAll(noAvg,expireRange.approveDelete,deleteCW);
165         
166                 // Hold Good Tickets to keyed User/Role for UserRole Step
167                 Map<String,Ticket> mur = new TreeMap<>();
168
169         try {
170                 Approval.load(trans, session, Approval.v2_0_17);
171         
172                         ////////////////////
173                         final Map<UUID,Ticket> goodTickets = new TreeMap<>();
174                 tt = trans.start("Analyze Expired Futures",Trans.SUB);
175                 try {
176                                 Future.load(noAvg, session, Future.withConstruct, fut -> {
177                                         List<Approval> appls = Approval.byTicket.get(fut.id());
178                                         if(!futureRange.inRange(fut.expires())) {
179                                                 deleteCW.comment("Future %s expired", fut.id());
180                                                 Future.row(deleteCW,fut);
181                                                 if(appls!=null) {
182                                                         for(Approval a : appls) {
183                                                                 Approval.row(deleteCW, a);
184                                                         }
185                                                 }
186                                         } else if(appls==null) { // Orphaned Future (no Approvals)
187                                                 deleteCW.comment("Future is Orphaned");
188                                                 Future.row(deleteCW,fut);
189                                         } else  {
190                                                 goodTickets.put(fut.fdd.id, new Ticket(fut));
191                                         }
192                                 });
193                 } finally {
194                         tt.done();
195                 }
196                         
197                 Set<String> approvers = new TreeSet<>();
198                 tt = trans.start("Connect Approvals with Futures",Trans.SUB);
199                 try {
200                                 for(Approval appr : Approval.list) {
201                                         Ticket ticket=null;
202                                         UUID ticketID = appr.getTicket();
203                                         if(ticketID!=null) {
204                                                 ticket = goodTickets.get(appr.getTicket());
205                                         }
206                                         if(ticket == null) { // Orphaned Approvals, no Futures
207                                                 deleteCW.comment("Approval is Orphaned");
208                                                 Approval.row(deleteCW, appr);
209                                         } else {
210                                                 ticket.approvals.add(appr); // add to found Ticket
211                                                 approvers.add(appr.getApprover());
212                                         }
213                                 }
214                 } finally {
215                         tt.done();
216                 }
217         
218                         /* Run through all Futures, and see if 
219                          * 1) they have been executed (no longer valid)
220                          * 2) The current Approvals indicate they can proceed 
221                          */
222                         Map<String,Pending> pendingApprs = new HashMap<>();
223                         Map<String,Pending> pendingTemp = new HashMap<>();
224         
225                         String approver;
226                         
227                         tt = trans.start("Analyze Good Tickets",Trans.SUB);
228                         try {
229                                 for(Ticket ticket : goodTickets.values()) {
230                                         try {
231                                                 pendingTemp.clear();
232                                                 switch(ticket.f.target()) {
233                                                         case "user_role":
234                                                                 int state[][] = new int[3][3];
235                                                                 int type;
236                                                                                 
237                                                                 for(Approval appr : ticket.approvals) {
238                                                                         switch(appr.getType()) {
239                                                                                 case "owner":
240                                                                                         type=owner;
241                                                                                         break;
242                                                                                 case "supervisor":
243                                                                                         type=supervisor;
244                                                                                         break;
245                                                                                 default:
246                                                                                         type=0;
247                                                                         }
248                                                                         ++state[type][total]; // count per type
249                                                                         switch(appr.getStatus()) {
250                                                                                 case "pending":
251                                                                                         ++state[type][pending];
252                                                                                         approver = appr.getApprover();
253                                                                                         Pending n = pendingTemp.get(approver);
254                                                                                         if(n==null) {
255                                                                                                 Date lastNotified = ln.lastNotified(approver,"ur",ticket.f.fdd.target_key);
256                                                                                                 pendingTemp.put(approver,new Pending(lastNotified));
257                                                                                         } else {
258                                                                                                 n.inc();
259                                                                                         }
260                                                                                         break;
261                                                                                 case "approved":
262                                                                                         ++state[type][approved];
263                                                                                         break;
264                                                                                 default:
265                                                                                         ++state[type][unknown];
266                                                                         }
267                                                                 }
268                                                                 
269                                                                 // To Approve:
270                                                                 // Always must have at least 1 owner
271                                                                 if((state[owner][total]>0 && state[owner][approved]>0) &&
272                                                                         // If there are no Supervisors, that's ok
273                                                                     (state[supervisor][total]==0 || 
274                                                                     // But if there is a Supervisor, they must have approved 
275                                                                     (state[supervisor][approved]>0))) {
276                                                                                 UserRoleDAO.Data urdd = new UserRoleDAO.Data();
277                                                                                 try {
278                                                                                         urdd.reconstitute(ticket.f.fdd.construct);
279                                                                                         if(urdd.expires.before(ticket.f.expires())) {
280                                                                                                 extendCW.row("extend_ur",urdd.user,urdd.role,ticket.f.expires());
281                                                                                         }
282                                                                                 } catch (IOException e) {
283                                                                                         trans.error().log("Could not reconstitute UserRole");
284                                                                                 }
285                                                                 } else { // Load all the Pending.
286                                                                         for(Entry<String, Pending> es : pendingTemp.entrySet()) {
287                                                                                 Pending p = pendingApprs.get(es.getKey());
288                                                                                 if(p==null) {
289                                                                                         pendingApprs.put(es.getKey(), es.getValue());
290                                                                                 } else {
291                                                                                         p.inc(es.getValue());
292                                                                                 }
293                                                                         }
294                                                                 }
295                                                                 break;
296                                                 }
297                                         } finally {
298                                                 if("user_role".equals(ticket.f.fdd.target)) {
299                                                         String key = ticket.f.fdd.target_key; 
300                                                         if(key!=null) {
301                                                                 mur.put(key, ticket);
302                                                         }
303                                                 }
304                                         }
305                                 }
306                         } finally {
307                                 tt.done();
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         } finally {
337                 Approval.clear();
338         }
339                         
340                 /**
341                    Run through User Roles.  
342                    Owners are treated specially in next section.
343                    Regular roles are checked against Date Ranges.  If match Date Range, write out to appropriate file.
344                 */      
345         
346         try {
347                 Role.load(trans, session);
348         
349                         try {
350                                 tt = trans.start("Analyze UserRoles, storing Owners",Trans.SUB);
351                                 Set<String> specialCommented = new HashSet<>();
352                                 Map<String, Set<UserRole>> owners = new TreeMap<>();
353                                 try {
354                                         UserRole.load(noAvg, session, UserRole.v2_0_11, ur -> {
355                                                 Identity identity;
356                                                 try {
357                                                         identity = trans.org().getIdentity(noAvg,ur.user());
358                                                         if(identity==null) {
359                                                                 // Candidate for Delete, but not Users if Special
360                                                                 String id = ur.user();
361                                                                 for(String s : specialDomains) {
362                                                                         if(id.endsWith(s)) {
363                                                                                 if(!specialCommented.contains(id)) {
364                                                                                         deleteCW.comment("ID %s is part of special Domain %s (UR Org Check)", id,s);
365                                                                                         specialCommented.add(id);
366                                                                                 }
367                                                                                 return;
368                                                                         }
369                                                                 }
370                                                                 if(specialNames.contains(id)) {
371                                                                         if(!specialCommented.contains(id)) {
372                                                                                 deleteCW.comment("ID %s is a special ID  (UR Org Check)", id);
373                                                                                 specialCommented.add(id);
374                                                                         }
375                                                                         return;
376                                                                 }
377                                                                 ur.row(deleteCW, UserRole.UR,"Not in Organization");
378                                                                 return;
379                                                         } else if(Role.byName.get(ur.role())==null) {
380                                                                 ur.row(deleteCW, UserRole.UR,String.format("Role %s does not exist", ur.role()));
381                                                                 return;
382                                                         }
383                                                         // Just let expired UserRoles sit until deleted
384                                                         if(futureRange.inRange(ur.expires())) {
385                                                                 if(!mur.containsKey(ur.user() + '|' + ur.role())) {
386                                                                         // Cannot just delete owners, unless there is at least one left. Process later
387                                                                         if ("owner".equals(ur.rname())) {
388                                                                                 Set<UserRole> urs = owners.get(ur.role());
389                                                                                 if (urs == null) {
390                                                                                         urs = new HashSet<UserRole>();
391                                                                                         owners.put(ur.role(), urs);
392                                                                                 }
393                                                                                 urs.add(ur);
394                                                                         } else {
395                                                                                 Range r = writeAnalysis(noAvg,ur);
396                                                                                 if(r!=null) {
397                                                                                         Approval existing = findApproval(ur);
398                                                                                         if(existing==null) {
399                                                                                                 ur.row(needApproveCW,UserRole.APPROVE_UR);
400                                                                                         }
401                                                                                 }
402                                                                         }
403                                                                 }
404                                                         }
405                                                 } catch (OrganizationException e) {
406                                                         noAvg.error().log(e);
407                                                 }
408                                         });
409                                 } finally {
410                                         tt.done();
411                                 }
412                                 mur.clear();
413                                 
414                                 /**
415                                   Now Process Owners, one owner Role at a time, ensuring one is left,
416                                   preferably a good one. If so, process the others as normal. 
417                                   
418                                   Otherwise, write to ExpiredOwners Report
419                                 */
420                                 tt = trans.start("Analyze Owners Separately",Trans.SUB);
421                                 try {
422                                         if (!owners.values().isEmpty()) {
423                                                 File file = new File(logDir(), EXPIRED_OWNERS + sdate + CSV);
424                                                 final CSV ownerCSV = new CSV(env.access(),file);
425                                                 CSV.Writer expOwner = ownerCSV.writer();
426                                                 expOwner.row(INFO,EXPIRED_OWNERS,sdate,2);
427         
428                                                 try {
429                                                         for (Set<UserRole> sur : owners.values()) {
430                                                                 int goodOwners = 0;
431                                                                 for (UserRole ur : sur) {
432                                                                         if (ur.expires().after(now)) {
433                                                                                 ++goodOwners;
434                                                                         }
435                                                                 }
436                 
437                                                                 for (UserRole ur : sur) {
438                                                                         if (goodOwners >= minOwners) {
439                                                                                 Range r = writeAnalysis(noAvg, ur);
440                                                                                 if(r!=null) {
441                                                                                         Approval existing = findApproval(ur);
442                                                                                         if(existing==null) {
443                                                                                                 ur.row(needApproveCW,UserRole.APPROVE_UR);
444                                                                                         }
445                                                                                 }
446                                                                         } else {
447                                                                                 expOwner.row("owner",ur.role(), ur.user(), Chrono.dateOnlyStamp(ur.expires()));
448                                                                                 Approval existing = findApproval(ur);
449                                                                                 if(existing==null) {
450                                                                                         ur.row(needApproveCW,UserRole.APPROVE_UR);
451                                                                                 }
452                                                                         }
453                                                                 }
454                                                         }
455                                                 } finally {
456                                                         if(expOwner!=null) {
457                                                                 expOwner.close();
458                                                         }
459                                                 }
460                                         }
461                                 } finally {
462                                         tt.done();
463                                 }
464                         } finally {
465                                 Role.clear();
466                                 UserRole.clear();
467                         }
468                         
469                         /**
470                          * Check for Expired Credentials
471                          */
472                         try {
473                         // Load Cred.  We don't follow Visitor, because we have to gather up everything into Identity Anyway
474                         Cred.load(trans, session);
475         
476                                 tt = trans.start("Analyze Expired Credentials",Trans.SUB);
477                                 try {
478                                         for (Cred cred : Cred.data.values()) {
479                                         List<Instance> linst = cred.instances;
480                                         if(linst!=null) {
481                                                 Instance lastBath = null;
482                                                 for(Instance inst : linst) {
483                 //                                      if(inst.attn>0) {
484                 //                                              writeAnalysis(trans, cred, inst);
485                 //                                              // Special Behavior: only eval the LAST Instance
486                 //                                      } else 
487                                                         // All Creds go through Life Cycle
488                                                         if(deleteDate!=null && inst.expires.before(deleteDate)) {
489                                                                 writeAnalysis(noAvg, cred, inst); // will go to Delete
490                                                         // Basic Auth has Pre-EOL notifications IF there is no Newer Credential
491                                                         } else if (inst.type == CredDAO.BASIC_AUTH || inst.type == CredDAO.BASIC_AUTH_SHA256) {
492                                                                 if(lastBath==null || lastBath.expires.before(inst.expires)) {
493                                                                         lastBath = inst;
494                                                                 }
495                                                         }
496                                                 }
497                                                 if(lastBath!=null) {
498                                                         writeAnalysis(noAvg, cred, lastBath);
499                                                 }
500                                         }
501                                         }
502                                 } finally {
503                                         tt.done();
504                                 }
505                         } finally {
506                                 Cred.clear();
507                         }
508         
509                         ////////////////////
510                         tt = trans.start("Analyze Expired X509s",Trans.SUB);
511                         try {
512                                 X509.load(noAvg, session, x509 -> {
513                                         try {
514                                                 for(Certificate cert : Factory.toX509Certificate(x509.x509)) {
515                                                         writeAnalysis(noAvg, x509, (X509Certificate)cert);
516                                                 }
517                                         } catch (CertificateException | IOException e) {
518                                                 noAvg.error().log(e, "Error Decrypting X509");
519                                         }
520                                 });
521                         } finally {
522                                 tt.done();
523                         }
524                 } catch (FileNotFoundException e) {
525                         noAvg.info().log(e);
526                 }
527         }
528  
529         private Approval findApproval(UserRole ur) {
530                 Approval existing = null;
531                 List<Approval> apprs = Approval.byUser.get(ur.user());
532                 if(apprs!=null) {
533                         for(Approval appr : apprs) {
534                                 if(ur.role().equals(appr.getRole()) &&
535                                         appr.getMemo().contains(Chrono.dateOnlyStamp(ur.expires()))) {
536                                                 existing = appr; 
537                                 }
538                         }
539                 }
540                 return existing;
541         }
542
543         private Range writeAnalysis(AuthzTrans trans, UserRole ur) {
544                 Range r = expireRange.getRange("ur", ur.expires());
545                 if(r!=null) {
546                         Date lnd = ln.lastNotified(LastNotified.newKey(ur));
547                         // Note: lnd is NEVER null
548                         Identity i;
549                         try {
550                                 i = org.getIdentity(trans, ur.user());
551                         } catch (OrganizationException e) {
552                                 i=null;
553                         }
554                         if(r.needsContact(lnd,i)) {                             
555                                 CSV.Writer cw = writerList.get(r.name());
556                                 if(cw!=null) {
557                                         ur.row(cw,UserRole.UR);
558                                 }
559                         }
560                 }
561                 return r;
562         }
563     
564     private void writeAnalysis(AuthzTrans trans, Cred cred, Instance inst) {
565         if(cred!=null && inst!=null) {
566                         Range r = expireRange.getRange("cred", inst.expires);
567                         if(r!=null) {
568                                 Date lnd = ln.lastNotified(LastNotified.newKey(cred,inst));
569                                 // Note: lnd is NEVER null
570                                 Identity i;
571                                 try {
572                                         i = org.getIdentity(trans, cred.id);
573                                 } catch (OrganizationException e) {
574                                         i=null;
575                                 }
576                                 if(r.needsContact(lnd,i)) {                             
577                                         CSV.Writer cw = writerList.get(r.name());
578                                         if(cw!=null) {
579                                                 cred.row(cw,inst);
580                                         }
581                                 }
582                         }
583         }
584         }
585
586     private void writeAnalysis(AuthzTrans trans, X509 x509, X509Certificate x509Cert) throws IOException {
587                 Range r = expireRange.getRange("x509", x509Cert.getNotAfter());
588                 if(r!=null) {
589                         Date lnd = ln.lastNotified(LastNotified.newKey(x509,x509Cert));
590                         // Note: lnd is NEVER null
591                         Identity i;
592                         try {
593                                 i = org.getIdentity(trans, x509.id);
594                         } catch (OrganizationException e) {
595                                 i=null;
596                         }
597                         if(r.needsContact(lnd,i)) {
598                                 CSV.Writer cw = writerList.get(r.name());
599                                 if(cw!=null) {
600                                         x509.row(cw,x509Cert);
601                                 }
602                         }
603                 }
604         }
605     
606     @Override
607     protected void _close(AuthzTrans trans) {
608         session.close();
609         for(CSV.Writer cw : writerList.values()) {
610                 cw.close();
611         }
612     }
613
614 }