1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * Copyright © 2017 Amdocs
\r
7 * * ===========================================================================
\r
8 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
9 * * you may not use this file except in compliance with the License.
\r
10 * * You may obtain a copy of the License at
\r
12 * * http://www.apache.org/licenses/LICENSE-2.0
\r
14 * * Unless required by applicable law or agreed to in writing, software
\r
15 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
16 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
17 * * See the License for the specific language governing permissions and
\r
18 * * limitations under the License.
\r
19 * * ============LICENSE_END====================================================
\r
21 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
23 ******************************************************************************/
\r
24 package com.osaaf.defOrg;
\r
26 import java.io.File;
\r
27 import java.io.IOException;
\r
28 import java.util.ArrayList;
\r
29 import java.util.Date;
\r
30 import java.util.GregorianCalendar;
\r
31 import java.util.HashSet;
\r
32 import java.util.List;
\r
33 import java.util.Set;
\r
35 import javax.mail.Address;
\r
36 import javax.mail.Message;
\r
37 import javax.mail.MessagingException;
\r
38 import javax.mail.Session;
\r
39 import javax.mail.Transport;
\r
40 import javax.mail.internet.InternetAddress;
\r
41 import javax.mail.internet.MimeMessage;
\r
43 import com.att.authz.env.AuthzEnv;
\r
44 import com.att.authz.env.AuthzTrans;
\r
45 import com.att.authz.org.EmailWarnings;
\r
46 import com.att.authz.org.Executor;
\r
47 import com.att.authz.org.Organization;
\r
48 import com.att.authz.org.OrganizationException;
\r
49 import com.osaaf.defOrg.Identities.Data;
\r
51 public class DefaultOrg implements Organization {
\r
52 private static final String PROPERTY_IS_REQUIRED = " property is Required";
\r
53 private static final String DOMAIN = "osaaf.com";
\r
54 private static final String REALM = "com.osaaf";
\r
55 private static final String NAME = "Default Organization";
\r
56 private static final String NO_PASS = NAME + " does not support Passwords. Use AAF";
\r
57 private final String mailHost,mailFromUserId,supportAddress;
\r
58 private String SUFFIX;
\r
59 // Possible ID Pattern
\r
60 private static final String ID_PATTERN = "a-z[a-z0-9]{5-8}@.*";
\r
62 public DefaultOrg(AuthzEnv env) throws OrganizationException {
\r
64 mailHost = env.getProperty(s=(REALM + ".mailHost"), null);
\r
65 if(mailHost==null) {
\r
66 throw new OrganizationException(s + PROPERTY_IS_REQUIRED);
\r
68 supportAddress = env.getProperty(s=(REALM + ".supportEmail"), null);
\r
69 if(supportAddress==null) {
\r
70 throw new OrganizationException(s + PROPERTY_IS_REQUIRED);
\r
73 String temp = env.getProperty(s=(REALM + ".mailFromUserId"), null);
\r
74 mailFromUserId = temp==null?supportAddress:temp;
\r
76 System.getProperties().setProperty("mail.smtp.host",mailHost);
\r
77 System.getProperties().setProperty("mail.user", mailFromUserId);
\r
78 // Get the default Session object.
\r
79 session = Session.getDefaultInstance(System.getProperties());
\r
81 SUFFIX='.'+getDomain();
\r
85 temp=env.getProperty(defFile = (getClass().getName()+".file"));
\r
86 File fIdentities=null;
\r
88 temp = env.getProperty("aaf_data_dir");
\r
90 env.warn().log(defFile, "is not defined. Using default: ",temp+"/identities.dat");
\r
91 File dir = new File(temp);
\r
92 fIdentities=new File(dir,"identities.dat");
\r
93 if(!fIdentities.exists()) {
\r
94 env.warn().log("No",fIdentities.getCanonicalPath(),"exists. Creating.");
\r
98 fIdentities.createNewFile();
\r
102 fIdentities = new File(temp);
\r
103 if(!fIdentities.exists()) {
\r
104 String dataDir = env.getProperty("aaf_data_dir");
\r
105 if(dataDir!=null) {
\r
106 fIdentities = new File(dataDir,temp);
\r
111 if(fIdentities!=null && fIdentities.exists()) {
\r
112 identities = new Identities(fIdentities);
\r
114 throw new OrganizationException(fIdentities.getCanonicalPath() + " does not exist.");
\r
116 } catch (IOException e) {
\r
117 throw new OrganizationException(e);
\r
121 // Implement your own Delegation System
\r
122 static final List<String> NULL_DELEGATES = new ArrayList<String>();
\r
124 public Identities identities;
\r
125 private boolean dryRun;
\r
126 private Session session;
\r
127 public enum Types {Employee, Contractor, Application, NotActive};
\r
128 private final static Set<String> typeSet;
\r
131 typeSet = new HashSet<String>();
\r
132 for(Types t : Types.values()) {
\r
133 typeSet.add(t.name());
\r
137 private static final EmailWarnings emailWarnings = new DefaultOrgWarnings();
\r
140 public String getName() {
\r
145 public String getRealm() {
\r
150 public String getDomain() {
\r
155 public DefaultOrgIdentity getIdentity(AuthzTrans trans, String id) throws OrganizationException {
\r
156 return new DefaultOrgIdentity(trans,id,this);
\r
159 // Note: Return a null if found; return a String Message explaining why not found.
\r
161 public String isValidID(String id) {
\r
164 data = identities.find(id, identities.reuse());
\r
165 } catch (IOException e) {
\r
166 return getName() + " could not lookup " + id + ": " + e.getLocalizedMessage();
\r
168 return data==null?id + "is not an Identity in " + getName():null;
\r
172 public String isValidPassword(String user, String password, String... prev) {
\r
173 // If you have an Organization user/Password scheme, use here, otherwise, just use AAF
\r
178 public Set<String> getIdentityTypes() {
\r
183 public Response notify(AuthzTrans trans, Notify type, String url, String[] identities, String[] ccs, String summary, Boolean urgent) {
\r
184 String system = trans.getProperty("CASS_ENV", "");
\r
186 ArrayList<String> toList = new ArrayList<String>();
\r
188 if (identities != null) {
\r
189 for (String user : identities) {
\r
191 identity = getIdentity(trans, user);
\r
192 if (identity == null) {
\r
194 "Failure to obtain User " + user + " for "
\r
197 toList.add(identity.email());
\r
199 } catch (Exception e) {
\r
202 "Failure to obtain User " + user + " for "
\r
208 if (toList.isEmpty()) {
\r
209 trans.error().log("No Users listed to email");
\r
210 return Response.ERR_NotificationFailure;
\r
213 ArrayList<String> ccList = new ArrayList<String>();
\r
215 // If we're sending an urgent email, CC the user's supervisor
\r
218 trans.info().log("urgent msg for: " + identities[0]);
\r
220 List<Identity> supervisors = getApprovers(trans, identities[0]);
\r
221 for (Identity us : supervisors) {
\r
222 trans.info().log("supervisor: " + us.email());
\r
223 ccList.add(us.email());
\r
225 } catch (Exception e) {
\r
226 trans.error().log(e,
\r
227 "Failed to find supervisor for " + identities[0]);
\r
232 for (String user : ccs) {
\r
234 identity = getIdentity(trans, user);
\r
235 ccList.add(identity.email());
\r
236 } catch (Exception e) {
\r
239 "Failure to obtain User " + user + " for "
\r
245 if (summary == null) {
\r
252 sendEmail(trans, toList, ccList,
\r
253 "AAF Approval Notification "
\r
254 + (system.length() == 0 ? "" : "(ENV: "
\r
258 + "System for Fine-Grained Authorizations. You are being asked to Approve"
\r
259 + (system.length() == 0 ? "" : " in the "
\r
260 + system + " environment")
\r
261 + " before AAF Actions can be taken.\n\n"
\r
262 + "Please follow this link: \n\n\t" + url
\r
263 + "\n\n" + summary, urgent);
\r
264 } catch (Exception e) {
\r
265 trans.error().log(e, "Failure to send Email");
\r
266 return Response.ERR_NotificationFailure;
\r
269 case PasswordExpiration:
\r
274 "AAF Password Expiration Warning "
\r
275 + (system.length() == 0 ? "" : "(ENV: "
\r
279 + " System for Authorizations.\n\nOne or more passwords will expire soon or have expired"
\r
280 + (system.length() == 0 ? "" : " in the "
\r
281 + system + " environment")
\r
282 + ".\n\nPasswords expired for more than 30 days without action are subject to deletion.\n\n"
\r
283 + "Please follow each link to add a New Password with Expiration Date. Either are valid until expiration. "
\r
284 + "Use this time to change the passwords on your system. If issues, reply to this email.\n\n"
\r
285 + summary, urgent);
\r
286 } catch (Exception e) {
\r
287 trans.error().log(e, "Failure to send Email");
\r
288 return Response.ERR_NotificationFailure;
\r
292 case RoleExpiration:
\r
298 "AAF Role Expiration Warning "
\r
299 + (system.length() == 0 ? "" : "(ENV: "
\r
303 + " System for Authorizations. One or more roles will expire soon"
\r
304 + (system.length() == 0 ? "" : " in the "
\r
305 + system + " environment")
\r
306 + ".\n\nRoles expired for more than 30 days are subject to deletion."
\r
307 + "Please follow this link the GUI Command line, and either 'extend' or 'del' the user in the role.\n"
\r
308 + "If issues, reply to this email.\n\n\t" + url
\r
309 + "\n\n" + summary, urgent);
\r
310 } catch (Exception e) {
\r
311 trans.error().log(e, "Failure to send Email");
\r
312 return Response.ERR_NotificationFailure;
\r
316 return Response.ERR_NotImplemented;
\r
318 return Response.OK;
\r
322 public int sendEmail(AuthzTrans trans, List<String> toList, List<String> ccList, String subject, String body,
\r
323 Boolean urgent) throws OrganizationException {
\r
326 List<String> to = new ArrayList<String>();
\r
327 for(String em : toList) {
\r
328 if(em.indexOf('@')<0) {
\r
329 to.add(new DefaultOrgIdentity(trans, em, this).email());
\r
335 List<String> cc = new ArrayList<String>();
\r
336 if(ccList!=null && !ccList.isEmpty()) {
\r
337 for(String em : ccList) {
\r
338 if(em.indexOf('@')<0) {
\r
339 cc.add(new DefaultOrgIdentity(trans, em, this).email());
\r
347 // for now, I want all emails so we can see what goes out. Remove later
\r
348 if (!ccList.contains(supportAddress)) {
\r
349 ccList.add(supportAddress);
\r
353 // Create a default MimeMessage object.
\r
354 MimeMessage message = new MimeMessage(session);
\r
356 // Set From: header field of the header.
\r
357 message.setFrom(new InternetAddress(mailFromUserId));
\r
360 // Set To: header field of the header. This is a required field
\r
361 // and calling module should make sure that it is not null or
\r
363 message.addRecipients(Message.RecipientType.TO,
\r
366 // Set CC: header field of the header.
\r
367 if ((ccList != null) && (ccList.size() > 0)) {
\r
368 message.addRecipients(Message.RecipientType.CC,
\r
372 // Set Subject: header field
\r
373 message.setSubject(subject);
\r
376 message.addHeader("X-Priority", "1");
\r
379 // Now set the actual message
\r
380 message.setText(body);
\r
382 // override recipients
\r
383 message.addRecipients(Message.RecipientType.TO,
\r
384 InternetAddress.parse(supportAddress));
\r
386 // Set Subject: header field
\r
387 message.setSubject("[TESTMODE] " + subject);
\r
390 message.addHeader("X-Priority", "1");
\r
393 ArrayList<String> newBody = new ArrayList<String>();
\r
395 Address temp[] = getAddresses(to);
\r
396 String headerString = "TO:\t" + InternetAddress.toString(temp)
\r
399 temp = getAddresses(cc);
\r
400 headerString += "CC:\t" + InternetAddress.toString(temp) + "\n";
\r
402 newBody.add(headerString);
\r
404 newBody.add("Text: \n");
\r
407 String outString = "";
\r
408 for (String s : newBody) {
\r
409 outString += s + "\n";
\r
412 message.setText(outString);
\r
415 Transport.send(message);
\r
418 } catch (MessagingException mex) {
\r
419 throw new OrganizationException("Exception send email message "
\r
420 + mex.getMessage());
\r
427 * Default Policy is to set to 6 Months for Notification Types.
\r
428 * add others/change as required
\r
431 public Date whenToValidate(Notify type, Date lastValidated) {
\r
434 case PasswordExpiration:
\r
437 GregorianCalendar gc = new GregorianCalendar();
\r
438 gc.setTime(lastValidated);
\r
439 gc.add(GregorianCalendar.MONTH, 6); // 6 month policy
\r
440 return gc.getTime();
\r
445 public GregorianCalendar expiration(GregorianCalendar gc, Expiration exp, String... extra) {
\r
446 GregorianCalendar rv = gc==null?new GregorianCalendar():(GregorianCalendar)gc.clone();
\r
448 case ExtendPassword:
\r
449 // Extending Password give 5 extra days
\r
450 rv.add(GregorianCalendar.DATE, 5);
\r
453 // Future Requests last 15 days before subject to deletion.
\r
454 rv.add(GregorianCalendar.DATE, 15);
\r
457 // Passwords expire in 90 days
\r
458 rv.add(GregorianCalendar.DATE, 90);
\r
461 // Temporary Passwords last for 12 hours.
\r
462 rv.add(GregorianCalendar.HOUR, 12);
\r
465 // Delegations expire max in 2 months
\r
466 rv.add(GregorianCalendar.MONTH, 2);
\r
469 // Roles expire in 6 months
\r
470 rv.add(GregorianCalendar.MONTH, 6);
\r
473 // Unless other wise set, 6 months is default
\r
474 rv.add(GregorianCalendar.MONTH, 6);
\r
481 public EmailWarnings emailWarningPolicy() {
\r
482 return emailWarnings;
\r
486 * Assume the Supervisor is the Approver.
\r
489 public List<Identity> getApprovers(AuthzTrans trans, String user) throws OrganizationException {
\r
490 Identity orgIdentity = getIdentity(trans, user);
\r
491 List<Identity> orgIdentitys = new ArrayList<Identity>();
\r
492 if(orgIdentity!=null) {
\r
493 String supervisorID = orgIdentity.responsibleTo();
\r
494 if (supervisorID.indexOf('@') < 0) {
\r
495 supervisorID += getDomain();
\r
497 Identity supervisor = getIdentity(trans, supervisorID);
\r
498 orgIdentitys.add(supervisor);
\r
500 return orgIdentitys;
\r
504 public String getApproverType() {
\r
505 return "supervisor";
\r
509 public int startOfDay() {
\r
510 // TODO Auto-generated method stub
\r
515 public boolean canHaveMultipleCreds(String id) {
\r
516 // External entities are likely mono-password... if you change it, it is a global change.
\r
517 // This is great for people, but horrible for Applications.
\r
519 // AAF's Password can have multiple Passwords, each with their own Expiration Date.
\r
520 // For Default Org, we'll assume true for all, but when you add your external
\r
521 // Identity stores, you need to return "false" if they cannot support multiple Passwords like AAF
\r
526 public boolean isValidCred(String id) {
\r
527 if(id.endsWith(SUFFIX)) {
\r
530 return id.matches(ID_PATTERN);
\r
534 public String validate(AuthzTrans trans, Policy policy, Executor executor, String... vars) throws OrganizationException {
\r
537 case CREATE_MECHID:
\r
538 if(vars.length>0) {
\r
539 Identity requestor = getIdentity(trans, trans.user());
\r
540 if(requestor!=null) {
\r
541 Identity mechid = getIdentity(trans, vars[0]);
\r
542 if(requestor.equals(mechid.owner())) {
\r
547 return trans.user() + " is not the Sponsor of MechID " + vars[0];
\r
549 case CREATE_MECHID_BY_PERM_ONLY:
\r
550 return getName() + " only allows sponsors to create MechIDs";
\r
553 return policy.name() + " is unsupported at " + getName();
\r
558 public boolean isTestEnv() {
\r
563 public void setTestMode(boolean dryRun) {
\r
564 this.dryRun = dryRun;
\r
568 * Convert the delimiter String into Internet addresses with the default
\r
570 * @param strAddress
\r
573 private Address[] getAddresses(List<String> strAddress) throws OrganizationException {
\r
574 return this.getAddresses(strAddress,";");
\r
577 * Convert the delimiter String into Internet addresses with the
\r
578 * delimiter of provided
\r
579 * @param strAddress
\r
583 private Address[] getAddresses(List<String> strAddresses, String delimiter) throws OrganizationException {
\r
584 Address[] addressArray = new Address[strAddresses.size()];
\r
586 for (String addr : strAddresses)
\r
589 addressArray[count] = new InternetAddress(addr);
\r
591 }catch(Exception e){
\r
592 throw new OrganizationException("Failed to parse the email address "+ addr +": "+e.getMessage());
\r
595 return addressArray;
\r