2 * ============LICENSE_START====================================================
4 * ===========================================================================
5 * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
7 * Modifications Copyright (C) 2018 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
13 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
24 package org.onap.aaf.auth.batch.update;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.PrintStream;
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.GregorianCalendar;
32 import java.util.List;
33 import java.util.Map.Entry;
35 import org.onap.aaf.auth.batch.Batch;
36 import org.onap.aaf.auth.batch.BatchPrincipal;
37 import org.onap.aaf.auth.batch.actions.Email;
38 import org.onap.aaf.auth.batch.actions.EmailPrint;
39 import org.onap.aaf.auth.batch.actions.Message;
40 import org.onap.aaf.auth.batch.helpers.Approval;
41 import org.onap.aaf.auth.batch.helpers.Future;
42 import org.onap.aaf.auth.dao.CassAccess;
43 import org.onap.aaf.auth.dao.cass.ApprovalDAO;
44 import org.onap.aaf.auth.dao.cass.FutureDAO;
45 import org.onap.aaf.auth.dao.cass.HistoryDAO;
46 import org.onap.aaf.auth.env.AuthzTrans;
47 import org.onap.aaf.auth.org.Organization;
48 import org.onap.aaf.auth.org.Organization.Identity;
49 import org.onap.aaf.auth.org.OrganizationException;
50 import org.onap.aaf.auth.org.OrganizationFactory;
51 import org.onap.aaf.cadi.Access;
52 import org.onap.aaf.cadi.CadiException;
53 import org.onap.aaf.cadi.config.RegistrationPropHolder;
54 import org.onap.aaf.misc.env.APIException;
55 import org.onap.aaf.misc.env.util.Chrono;
57 public class NotifyApprovals extends Batch {
58 private static final String LINE = "----------------------------------------------------------------";
59 private final HistoryDAO historyDAO;
60 private final ApprovalDAO apprDAO;
61 private final FutureDAO futureDAO;
63 private int maxEmails;
64 private final PrintStream ps;
65 private final AuthzTrans noAvg;
67 public NotifyApprovals(AuthzTrans trans) throws APIException, IOException, OrganizationException, CadiException {
69 Access access = trans.env().access();
70 RegistrationPropHolder rph = new RegistrationPropHolder(access, 0);
71 String guiURL = rph.replacements(access.getProperty(GUI_URL,"https://%P/gui"),"","");
72 noAvg = env.newTransNoAvg();
73 noAvg.setUser(new BatchPrincipal("batch:NotifyApprovals"));
75 historyDAO = new HistoryDAO(trans, cluster, CassAccess.KEYSPACE);
76 session = historyDAO.getSession(trans);
77 apprDAO = new ApprovalDAO(trans, historyDAO);
78 futureDAO = new FutureDAO(trans, historyDAO);
80 email = new EmailPrint();
84 maxEmails = Integer.parseInt(trans.getProperty("MAX_EMAILS","3"));
86 email.subject("AAF Approval Notification (ENV: %s)",batchEnv);
87 email.preamble("AAF is the ONAP Authorization System." +
88 "\n Your approval is required, which you may enter on the following page:"
89 + "\n\n\t%s/approve\n\n"
91 email.signature("Sincerely,\nAAF Team\n");
93 Approval.load(trans, session, Approval.v2_0_17);
94 Future.load(trans, session, Future.v2_0_17); // Skip the Construct Data
96 ps = new PrintStream(new FileOutputStream(logDir() + "/email"+Chrono.dateOnlyStamp()+".log",true));
97 ps.printf("### Approval Notify %s for %s%s\n",Chrono.dateTime(),batchEnv,dryRun?", DryRun":"");
101 protected void run(AuthzTrans trans) {
102 GregorianCalendar gc = new GregorianCalendar();
103 Date now = gc.getTime();
104 String today = Chrono.dateOnlyStamp(now);
105 gc.add(GregorianCalendar.MONTH, -1);
109 Message msg = new Message();
111 List<Approval> pending = new ArrayList<>();
113 boolean isSupervisor;
114 for (Entry<String, List<Approval>> es : Approval.byApprover.entrySet()) {
115 isOwner = isSupervisor = false;
116 String approver = es.getKey();
117 if (approver.indexOf('@')<0) {
118 approver += org.getRealm();
120 Date latestNotify=null;
121 Date soonestExpire=null;
122 GregorianCalendar latest=new GregorianCalendar();
123 GregorianCalendar soonest=new GregorianCalendar();
126 for (Approval app : es.getValue()) {
127 Future f = app.getTicket()==null?null:Future.data.get(app.getTicket());
128 if (f==null) { // only Ticketed Approvals are valid.. the others are records.
129 // Approvals without Tickets are no longer valid.
130 if ("pending".equals(app.getStatus())) {
131 app.setStatus("lapsed");
132 app.update(noAvg,apprDAO,dryRun); // obeys dryRun
135 if ((soonestExpire==null && f.expires()!=null) || (soonestExpire!=null && f.expires()!=null && soonestExpire.before(f.expires()))) {
136 soonestExpire=f.expires();
139 if ("pending".equals(app.getStatus())) {
141 isOwner = "owner".equals(app.getType());
144 isSupervisor = "supervisor".equals(app.getType());
147 if ((latestNotify==null && app.getLast_notified()!=null) ||(latestNotify!=null && app.getLast_notified()!=null && latestNotify.before(app.getLast_notified()))) {
148 latestNotify=app.getLast_notified();
155 if (!pending.isEmpty()) {
157 if (latestNotify==null) { // never notified... make it so
160 if (!today.equals(Chrono.dateOnlyStamp(latest))) { // already notified today
161 latest.setTime(latestNotify);
162 soonest.setTime(soonestExpire);
164 int days = soonest.get(GregorianCalendar.DAY_OF_YEAR)-latest.get(GregorianCalendar.DAY_OF_YEAR);
165 days+=((year=soonest.get(GregorianCalendar.YEAR))-latest.get(GregorianCalendar.YEAR))*365 +
166 (soonest.isLeapYear(year)?1:0);
167 if (days<7) { // If Expirations get within a Week (or expired), notify everytime.
172 if (go && (maxEmails>emailCount++)) {
174 Organization org = OrganizationFactory.obtain(env, approver);
175 Identity user = org.getIdentity(noAvg, approver);
177 ps.printf("Invalid Identity: %s\n", approver);
181 email.addTo(user.email());
183 msg.line("Why are you receiving this Notification?\n");
185 msg.line("%sYou are the supervisor of one or more employees who need access to tools which are protected by AAF. " +
186 "Your employees may ask for access to various tools and applications to do their jobs. ASPR requires "
187 + "that you are notified and approve their requests. The details of each need is provided when you click "
188 + "on webpage above.\n",isOwner?"1) ":"");
189 msg.line("Your participation in this process fulfills the ASPR requirement to re-authorize users in roles on a regular basis.\n\n");
193 msg.line("%sYou are the listed owner of one or more AAF Namespaces. ASPR requires that those responsible for "
194 + "applications and their access review them regularly for accuracy. The AAF WIKI page for AT&T is https://wiki.web.att.com/display/aaf. "
195 + "More info regarding questions of being a Namespace Owner is available at https://wiki.web.att.com/pages/viewpage.action?pageId=594741363\n",isSupervisor?"2) ":"");
196 msg.line("Additionally, Credentials attached to the Namespace must be renewed regularly. While you may delegate certain functions to " +
197 "Administrators within your Namespace, you are ultimately responsible to make sure credentials do not expire.\n");
198 msg.line("You may view the Namespaces you listed as Owner for in this AAF Env by viewing the following webpage:\n");
199 msg.line(" %s/ns\n\n",env.getProperty(GUI_URL));
202 msg.line(" If you are unfamiliar with AAF, you might like to peruse the following links:"
203 + "\n\thttps://wiki.web.att.com/display/aaf/AAF+in+a+Nutshell"
204 + "\n\thttps://wiki.web.att.com/display/aaf/The+New+Person%%27s+Guide+to+AAF");
205 msg.line("\n SPECIAL NOTE about SWM Management Groups: Understand that SWM management Groups correlate one-to-one to AAF Namespaces. "
206 + "(SWM uses AAF for the Authorization piece of Management Groups). You may be assigned the SWM Management Group by asking "
207 + "directly, or through any of the above stated automated processes. Auto-generated Namespaces typically look like 'com.att.44444.PROD' "
208 + "where '44444' is a MOTS ID, and 'PROD' is PROD|DEV|TEST, etc. For your convenience, the MOTS link is http://ebiz.sbc.com/mots.\n");
209 msg.line(" Finally, realize that there are automated processes which create Machines and Resources via SWM, Kubernetes or other "
210 + "such tooling. If you or your predecessor requested them, you were set as the owner of the AAF Namespace created during "
211 + "that process.\n");
212 msg.line(" For ALL QUESTIONS of why and how of SWM, and whether you or your reports can be removed, please contact SWM at "
213 + "https://wiki.web.att.com/display/swm/Support\n");
216 email.exec(noAvg, org,"");
218 email.log(ps,"NotifyApprovals");
219 for (Approval app : pending) {
220 app.setLastNotified(now);
221 app.update(noAvg, apprDAO, dryRun);
225 } catch (OrganizationException e) {
231 trans.info().printf("%d emails sent for %s", emailCount,batchEnv);
235 protected void _close(AuthzTrans trans) {
236 futureDAO.close(trans);
237 apprDAO.close(trans);
238 historyDAO.close(trans);