[AAF-21] Updated Copyright Headers for AAF
[aaf/authz.git] / authz-defOrg / src / main / java / com / osaaf / defOrg / DefaultOrg.java
1 /*******************************************************************************\r
2  * ============LICENSE_START====================================================\r
3  * * org.onap.aaf\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * * \r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * * \r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 package com.osaaf.defOrg;\r
24 \r
25 import java.io.File;\r
26 import java.io.IOException;\r
27 import java.util.ArrayList;\r
28 import java.util.Date;\r
29 import java.util.GregorianCalendar;\r
30 import java.util.HashSet;\r
31 import java.util.List;\r
32 import java.util.Set;\r
33 \r
34 import javax.mail.Address;\r
35 import javax.mail.Message;\r
36 import javax.mail.MessagingException;\r
37 import javax.mail.Session;\r
38 import javax.mail.Transport;\r
39 import javax.mail.internet.InternetAddress;\r
40 import javax.mail.internet.MimeMessage;\r
41 \r
42 import com.att.authz.env.AuthzEnv;\r
43 import com.att.authz.env.AuthzTrans;\r
44 import com.att.authz.org.EmailWarnings;\r
45 import com.att.authz.org.Executor;\r
46 import com.att.authz.org.Organization;\r
47 import com.att.authz.org.OrganizationException;\r
48 import com.osaaf.defOrg.Identities.Data;\r
49 \r
50 public class DefaultOrg implements Organization {\r
51         private static final String PROPERTY_IS_REQUIRED = " property is Required";\r
52         private static final String DOMAIN = "osaaf.com";\r
53         private static final String REALM = "com.osaaf";\r
54         private static final String NAME = "Default Organization";\r
55         private static final String NO_PASS = NAME + " does not support Passwords.  Use AAF";\r
56         private final String mailHost,mailFromUserId,supportAddress;\r
57         private String SUFFIX;\r
58         // Possible ID Pattern\r
59         private static final String ID_PATTERN = "a-z[a-z0-9]{5-8}@.*";\r
60 \r
61         public DefaultOrg(AuthzEnv env) throws OrganizationException {\r
62                 String s;\r
63                 mailHost = env.getProperty(s=(REALM + ".mailHost"), null);\r
64                 if(mailHost==null) {\r
65                         throw new OrganizationException(s + PROPERTY_IS_REQUIRED);\r
66                 }\r
67                 supportAddress = env.getProperty(s=(REALM + ".supportEmail"), null);\r
68                 if(supportAddress==null) {\r
69                         throw new OrganizationException(s + PROPERTY_IS_REQUIRED);\r
70                 }\r
71                 \r
72                 String temp = env.getProperty(s=(REALM + ".mailFromUserId"), null);\r
73                 mailFromUserId = temp==null?supportAddress:temp;\r
74 \r
75                 System.getProperties().setProperty("mail.smtp.host",mailHost);\r
76                 System.getProperties().setProperty("mail.user", mailFromUserId);\r
77                 // Get the default Session object.\r
78                 session = Session.getDefaultInstance(System.getProperties());\r
79 \r
80                 SUFFIX='.'+getDomain();\r
81                 \r
82                 try {\r
83                         String defFile;\r
84                         temp=env.getProperty(defFile = (getClass().getName()+".file"));\r
85                         File fIdentities=null;\r
86                         if(temp==null) {\r
87                                 temp = env.getProperty("aaf_data_dir");\r
88                                 if(temp!=null) {\r
89                                         env.warn().log(defFile, "is not defined. Using default: ",temp+"/identities.dat");\r
90                                         File dir = new File(temp);\r
91                                         fIdentities=new File(dir,"identities.dat");\r
92                                         if(!fIdentities.exists()) {\r
93                                                 env.warn().log("No",fIdentities.getCanonicalPath(),"exists.  Creating.");\r
94                                                 if(!dir.exists()) {\r
95                                                         dir.mkdirs();\r
96                                                 }\r
97                                                 fIdentities.createNewFile();\r
98                                         }\r
99                                 }\r
100                         } else {\r
101                                 fIdentities = new File(temp);\r
102                                 if(!fIdentities.exists()) {\r
103                                         String dataDir = env.getProperty("aaf_data_dir");\r
104                                         if(dataDir!=null) {\r
105                                                 fIdentities = new File(dataDir,temp);\r
106                                         }\r
107                                 }\r
108                         }\r
109                         \r
110                         if(fIdentities!=null && fIdentities.exists()) {\r
111                                 identities = new Identities(fIdentities);\r
112                         } else {\r
113                                 throw new OrganizationException(fIdentities.getCanonicalPath() + " does not exist.");\r
114                         }\r
115                 } catch (IOException e) {\r
116                         throw new OrganizationException(e);\r
117                 }\r
118         }\r
119         \r
120         // Implement your own Delegation System\r
121         static final List<String> NULL_DELEGATES = new ArrayList<String>();\r
122 \r
123         public Identities identities;\r
124         private boolean dryRun;\r
125         private Session session;\r
126         public enum Types {Employee, Contractor, Application, NotActive};\r
127         private final static Set<String> typeSet;\r
128         \r
129         static {\r
130                 typeSet = new HashSet<String>();\r
131                 for(Types t : Types.values()) {\r
132                         typeSet.add(t.name());\r
133                 }\r
134         }\r
135         \r
136         private static final EmailWarnings emailWarnings = new DefaultOrgWarnings();\r
137 \r
138         @Override\r
139         public String getName() {\r
140                 return NAME;\r
141         }\r
142 \r
143         @Override\r
144         public String getRealm() {\r
145                 return REALM;\r
146         }\r
147 \r
148         @Override\r
149         public String getDomain() {\r
150                 return DOMAIN;\r
151         }\r
152 \r
153         @Override\r
154         public DefaultOrgIdentity getIdentity(AuthzTrans trans, String id) throws OrganizationException {\r
155                 return new DefaultOrgIdentity(trans,id,this);\r
156         }\r
157 \r
158         // Note: Return a null if found; return a String Message explaining why not found. \r
159         @Override\r
160         public String isValidID(String id) {\r
161                 Data data;\r
162                 try {\r
163                         data = identities.find(id, identities.reuse());\r
164                 } catch (IOException e) {\r
165                         return getName() + " could not lookup " + id + ": " + e.getLocalizedMessage();\r
166                 }\r
167                 return data==null?id + "is not an Identity in " + getName():null;\r
168         }\r
169 \r
170         @Override\r
171         public String isValidPassword(String user, String password, String... prev) {\r
172                 // If you have an Organization user/Password scheme, use here, otherwise, just use AAF\r
173                 return NO_PASS;\r
174         }\r
175 \r
176         @Override\r
177         public Set<String> getIdentityTypes() {\r
178                 return typeSet;\r
179         }\r
180 \r
181         @Override\r
182         public Response notify(AuthzTrans trans, Notify type, String url, String[] identities, String[] ccs, String summary, Boolean urgent) {\r
183                 String system = trans.getProperty("CASS_ENV", "");\r
184 \r
185                 ArrayList<String> toList = new ArrayList<String>();\r
186                 Identity identity;\r
187                 if (identities != null) {\r
188                         for (String user : identities) {\r
189                                 try {\r
190                                         identity = getIdentity(trans, user);\r
191                                         if (identity == null) {\r
192                                                 trans.error().log(\r
193                                                                 "Failure to obtain User " + user + " for "\r
194                                                                                 + getName());\r
195                                         } else {\r
196                                                 toList.add(identity.email());\r
197                                         }\r
198                                 } catch (Exception e) {\r
199                                         trans.error().log(\r
200                                                         e,\r
201                                                         "Failure to obtain User " + user + " for "\r
202                                                                         + getName());\r
203                                 }\r
204                         }\r
205                 }\r
206 \r
207                 if (toList.isEmpty()) {\r
208                         trans.error().log("No Users listed to email");\r
209                         return Response.ERR_NotificationFailure;\r
210                 }\r
211 \r
212                 ArrayList<String> ccList = new ArrayList<String>();\r
213 \r
214                 // If we're sending an urgent email, CC the user's supervisor\r
215                 //\r
216                 if (urgent) {\r
217                         trans.info().log("urgent msg for: " + identities[0]);\r
218                         try {\r
219                                 List<Identity> supervisors = getApprovers(trans, identities[0]);\r
220                                 for (Identity us : supervisors) {\r
221                                         trans.info().log("supervisor: " + us.email());\r
222                                         ccList.add(us.email());\r
223                                 }\r
224                         } catch (Exception e) {\r
225                                 trans.error().log(e,\r
226                                                 "Failed to find supervisor for  " + identities[0]);\r
227                         }\r
228                 }\r
229 \r
230                 if (ccs != null) {\r
231                         for (String user : ccs) {\r
232                                 try {\r
233                                         identity = getIdentity(trans, user);\r
234                                         ccList.add(identity.email());\r
235                                 } catch (Exception e) {\r
236                                         trans.error().log(\r
237                                                         e,\r
238                                                         "Failure to obtain User " + user + " for "\r
239                                                                         + getName());\r
240                                 }\r
241                         }\r
242                 }\r
243 \r
244                 if (summary == null) {\r
245                         summary = "";\r
246                 }\r
247 \r
248                 switch (type) {\r
249                 case Approval:\r
250                         try {\r
251                                 sendEmail(trans, toList, ccList,\r
252                                                 "AAF Approval Notification "\r
253                                                                 + (system.length() == 0 ? "" : "(ENV: "\r
254                                                                                 + system + ")"),\r
255                                                 "AAF is the "\r
256                                                 + NAME\r
257                                                 + "System for Fine-Grained Authorizations.  You are being asked to Approve"\r
258                                                                 + (system.length() == 0 ? "" : " in the "\r
259                                                                                 + system + " environment")\r
260                                                                 + " before AAF Actions can be taken.\n\n"\r
261                                                                 + "Please follow this link: \n\n\t" + url\r
262                                                                 + "\n\n" + summary, urgent);\r
263                         } catch (Exception e) {\r
264                                 trans.error().log(e, "Failure to send Email");\r
265                                 return Response.ERR_NotificationFailure;\r
266                         }\r
267                         break;\r
268                 case PasswordExpiration:\r
269                         try {\r
270                                 sendEmail(trans,\r
271                                                 toList,\r
272                                                 ccList,\r
273                                                 "AAF Password Expiration Warning "\r
274                                                                 + (system.length() == 0 ? "" : "(ENV: "\r
275                                                                                 + system + ")"),\r
276                                                 "AAF is the "\r
277                                                 + NAME\r
278                                                 + " System for Authorizations.\n\nOne or more passwords will expire soon or have expired"\r
279                                                                 + (system.length() == 0 ? "" : " in the "\r
280                                                                                 + system + " environment")\r
281                                                                 + ".\n\nPasswords expired for more than 30 days without action are subject to deletion.\n\n"\r
282                                                                 + "Please follow each link to add a New Password with Expiration Date. Either are valid until expiration. "\r
283                                                                 + "Use this time to change the passwords on your system. If issues, reply to this email.\n\n"\r
284                                                                 + summary, urgent);\r
285                         } catch (Exception e) {\r
286                                 trans.error().log(e, "Failure to send Email");\r
287                                 return Response.ERR_NotificationFailure;\r
288                         }\r
289                         break;\r
290 \r
291                 case RoleExpiration:\r
292                         try {\r
293                                 sendEmail(\r
294                                                 trans,\r
295                                                 toList,\r
296                                                 ccList,\r
297                                                 "AAF Role Expiration Warning "\r
298                                                                 + (system.length() == 0 ? "" : "(ENV: "\r
299                                                                                 + system + ")"),\r
300                                                 "AAF is the "\r
301                                                 + NAME\r
302                                                 + " System for Authorizations. One or more roles will expire soon"\r
303                                                                 + (system.length() == 0 ? "" : " in the "\r
304                                                                                 + system + " environment")\r
305                                                                 + ".\n\nRoles expired for more than 30 days are subject to deletion."\r
306                                                                 + "Please follow this link the GUI Command line, and either 'extend' or 'del' the user in the role.\n"\r
307                                                                 + "If issues, reply to this email.\n\n\t" + url\r
308                                                                 + "\n\n" + summary, urgent);\r
309                         } catch (Exception e) {\r
310                                 trans.error().log(e, "Failure to send Email");\r
311                                 return Response.ERR_NotificationFailure;\r
312                         }\r
313                         break;\r
314                 default:\r
315                         return Response.ERR_NotImplemented;\r
316                 }\r
317                 return Response.OK;\r
318         }\r
319 \r
320         @Override\r
321         public int sendEmail(AuthzTrans trans, List<String> toList, List<String> ccList, String subject, String body,\r
322                         Boolean urgent) throws OrganizationException {\r
323                 int status = 1;\r
324                 \r
325                 List<String> to = new ArrayList<String>();\r
326                 for(String em : toList) {\r
327                         if(em.indexOf('@')<0) {\r
328                                 to.add(new DefaultOrgIdentity(trans, em, this).email());\r
329                         } else {\r
330                                 to.add(em);\r
331                         }\r
332                 }\r
333                 \r
334                 List<String> cc = new ArrayList<String>();\r
335                 if(ccList!=null && !ccList.isEmpty()) {\r
336                         for(String em : ccList) {\r
337                                 if(em.indexOf('@')<0) {\r
338                                         cc.add(new DefaultOrgIdentity(trans, em, this).email());\r
339                                 } else {\r
340                                         cc.add(em);\r
341                                 }\r
342                         }\r
343                 }\r
344                 \r
345         \r
346                 // for now, I want all emails so we can see what goes out. Remove later\r
347                 if (!ccList.contains(supportAddress)) {\r
348                         ccList.add(supportAddress);\r
349                 }\r
350 \r
351                 try {\r
352                         // Create a default MimeMessage object.\r
353                         MimeMessage message = new MimeMessage(session);\r
354 \r
355                         // Set From: header field of the header.\r
356                         message.setFrom(new InternetAddress(mailFromUserId));\r
357 \r
358                         if (!dryRun) {\r
359                                 // Set To: header field of the header. This is a required field\r
360                                 // and calling module should make sure that it is not null or\r
361                                 // blank\r
362                                 message.addRecipients(Message.RecipientType.TO,\r
363                                                 getAddresses(to));\r
364 \r
365                                 // Set CC: header field of the header.\r
366                                 if ((ccList != null) && (ccList.size() > 0)) {\r
367                                         message.addRecipients(Message.RecipientType.CC,\r
368                                                         getAddresses(cc));\r
369                                 }\r
370 \r
371                                 // Set Subject: header field\r
372                                 message.setSubject(subject);\r
373 \r
374                                 if (urgent) {\r
375                                         message.addHeader("X-Priority", "1");\r
376                                 }\r
377 \r
378                                 // Now set the actual message\r
379                                 message.setText(body);\r
380                         } else {\r
381                                 // override recipients\r
382                                 message.addRecipients(Message.RecipientType.TO,\r
383                                                 InternetAddress.parse(supportAddress));\r
384 \r
385                                 // Set Subject: header field\r
386                                 message.setSubject("[TESTMODE] " + subject);\r
387 \r
388                                 if (urgent) {\r
389                                         message.addHeader("X-Priority", "1");\r
390                                 }\r
391 \r
392                                 ArrayList<String> newBody = new ArrayList<String>();\r
393 \r
394                                 Address temp[] = getAddresses(to);\r
395                                 String headerString = "TO:\t" + InternetAddress.toString(temp)\r
396                                                 + "\n";\r
397 \r
398                                 temp = getAddresses(cc);\r
399                                 headerString += "CC:\t" + InternetAddress.toString(temp) + "\n";\r
400 \r
401                                 newBody.add(headerString);\r
402 \r
403                                 newBody.add("Text: \n");\r
404 \r
405                                 newBody.add(body);\r
406                                 String outString = "";\r
407                                 for (String s : newBody) {\r
408                                         outString += s + "\n";\r
409                                 }\r
410 \r
411                                 message.setText(outString);\r
412                         }\r
413                         // Send message\r
414                         Transport.send(message);\r
415                         status = 0;\r
416 \r
417                 } catch (MessagingException mex) {\r
418                         throw new OrganizationException("Exception send email message "\r
419                                         + mex.getMessage());\r
420                 }\r
421 \r
422                 return status;  \r
423         }\r
424 \r
425         /**\r
426          * Default Policy is to set to 6 Months for Notification Types.\r
427          * add others/change as required\r
428          */\r
429         @Override\r
430         public Date whenToValidate(Notify type, Date lastValidated) {\r
431                 switch(type) {\r
432                         case Approval:\r
433                         case PasswordExpiration:\r
434                                 return null;\r
435                         default:\r
436                                 GregorianCalendar gc = new GregorianCalendar();\r
437                                 gc.setTime(lastValidated);\r
438                                 gc.add(GregorianCalendar.MONTH, 6);  // 6 month policy\r
439                                 return gc.getTime();\r
440                 }\r
441         }\r
442 \r
443         @Override\r
444         public GregorianCalendar expiration(GregorianCalendar gc, Expiration exp, String... extra) {\r
445         GregorianCalendar rv = gc==null?new GregorianCalendar():(GregorianCalendar)gc.clone();\r
446                 switch (exp) {\r
447                         case ExtendPassword:\r
448                                 // Extending Password give 5 extra days\r
449                                 rv.add(GregorianCalendar.DATE, 5);\r
450                                 break;\r
451                         case Future:\r
452                                 // Future Requests last 15 days before subject to deletion.\r
453                                 rv.add(GregorianCalendar.DATE, 15);\r
454                                 break;\r
455                         case Password:\r
456                                 // Passwords expire in 90 days\r
457                                 rv.add(GregorianCalendar.DATE, 90);\r
458                                 break;\r
459                         case TempPassword:\r
460                                 // Temporary Passwords last for 12 hours.\r
461                                 rv.add(GregorianCalendar.HOUR, 12);\r
462                                 break;\r
463                         case UserDelegate:\r
464                                 // Delegations expire max in 2 months\r
465                                 rv.add(GregorianCalendar.MONTH, 2);\r
466                                 break;\r
467                         case UserInRole:\r
468                                 // Roles expire in 6 months\r
469                                 rv.add(GregorianCalendar.MONTH, 6);\r
470                                 break;\r
471                         default:\r
472                                 // Unless other wise set, 6 months is default\r
473                                 rv.add(GregorianCalendar.MONTH, 6);\r
474                                 break;\r
475                 }\r
476                 return rv;\r
477         }\r
478 \r
479         @Override\r
480         public EmailWarnings emailWarningPolicy() {\r
481                 return emailWarnings;\r
482         }\r
483 \r
484         /**\r
485          * Assume the Supervisor is the Approver.\r
486          */\r
487         @Override\r
488         public List<Identity> getApprovers(AuthzTrans trans, String user) throws OrganizationException {\r
489                 Identity orgIdentity = getIdentity(trans, user);\r
490                 List<Identity> orgIdentitys = new ArrayList<Identity>();\r
491                 if(orgIdentity!=null) {\r
492                         String supervisorID = orgIdentity.responsibleTo();\r
493                         if (supervisorID.indexOf('@') < 0) {\r
494                             supervisorID += getDomain();\r
495                         }\r
496                         Identity supervisor = getIdentity(trans, supervisorID);\r
497                         orgIdentitys.add(supervisor);\r
498                 }\r
499                 return orgIdentitys;    \r
500         }\r
501 \r
502         @Override\r
503         public String getApproverType() {\r
504                 return "supervisor";\r
505         }\r
506 \r
507         @Override\r
508         public int startOfDay() {\r
509                 // TODO Auto-generated method stub\r
510                 return 0;\r
511         }\r
512 \r
513         @Override\r
514         public boolean canHaveMultipleCreds(String id) {\r
515                 // External entities are likely mono-password... if you change it, it is a global change.\r
516                 // This is great for people, but horrible for Applications.  \r
517                 //\r
518                 // AAF's Password can have multiple Passwords, each with their own Expiration Date.\r
519                 // For Default Org, we'll assume true for all, but when you add your external\r
520                 // Identity stores, you need to return "false" if they cannot support multiple Passwords like AAF\r
521                 return true;\r
522         }\r
523 \r
524         @Override\r
525         public boolean isValidCred(String id) {\r
526                 if(id.endsWith(SUFFIX)) {\r
527                         return true;\r
528                 }\r
529                 return id.matches(ID_PATTERN);\r
530         }\r
531 \r
532         @Override\r
533         public String validate(AuthzTrans trans, Policy policy, Executor executor, String... vars) throws OrganizationException {\r
534                 switch(policy) {\r
535                         case OWNS_MECHID:\r
536                         case CREATE_MECHID:\r
537                                 if(vars.length>0) {\r
538                                         Identity requestor = getIdentity(trans, trans.user());\r
539                                         if(requestor!=null) {\r
540                                                 Identity mechid = getIdentity(trans, vars[0]);\r
541                                                 if(requestor.equals(mechid.owner())) {\r
542                                                         return null;\r
543                                                 }\r
544                                         }\r
545                                 }\r
546                                 return trans.user() + " is not the Sponsor of MechID " + vars[0];\r
547                                 \r
548                         case CREATE_MECHID_BY_PERM_ONLY:\r
549                                 return getName() + " only allows sponsors to create MechIDs";\r
550                                 \r
551                         default:\r
552                                 return policy.name() + " is unsupported at " + getName();\r
553                 }       \r
554         }\r
555 \r
556         @Override\r
557         public boolean isTestEnv() {\r
558                 return false;\r
559         }\r
560 \r
561         @Override\r
562         public void setTestMode(boolean dryRun) {\r
563                 this.dryRun = dryRun;\r
564         }\r
565 \r
566         /**\r
567          * Convert the delimiter String into Internet addresses with the default\r
568          * delimiter of ";"\r
569          * @param strAddress\r
570          * @return\r
571          */\r
572         private Address[] getAddresses(List<String> strAddress) throws OrganizationException {\r
573                 return this.getAddresses(strAddress,";");\r
574         }\r
575         /**\r
576          * Convert the delimiter String into Internet addresses with the \r
577          * delimiter of provided\r
578          * @param strAddress\r
579          * @param delimiter\r
580          * @return\r
581          */\r
582         private Address[] getAddresses(List<String> strAddresses, String delimiter) throws OrganizationException {\r
583                 Address[] addressArray = new Address[strAddresses.size()];\r
584                 int count = 0;\r
585                 for (String addr : strAddresses)\r
586                 {\r
587             try{\r
588                 addressArray[count] = new InternetAddress(addr);\r
589                 count++;\r
590             }catch(Exception e){\r
591                 throw new OrganizationException("Failed to parse the email address "+ addr +": "+e.getMessage());\r
592             }\r
593         }\r
594         return addressArray;\r
595         }\r
596 }\r