[AAF-21] Initial code import
[aaf/authz.git] / authz-defOrg / src / main / java / com / osaaf / defOrg / DefaultOrg.java
1 /*******************************************************************************\r
2  * ============LICENSE_START====================================================\r
3  * * org.onap.aai\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
11  * * \r
12  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
13  * * \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
20  * *\r
21  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
22  * *\r
23  ******************************************************************************/\r
24 package com.osaaf.defOrg;\r
25 \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
34 \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
42 \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
50 \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
61 \r
62         public DefaultOrg(AuthzEnv env) throws OrganizationException {\r
63                 String s;\r
64                 mailHost = env.getProperty(s=(REALM + ".mailHost"), null);\r
65                 if(mailHost==null) {\r
66                         throw new OrganizationException(s + PROPERTY_IS_REQUIRED);\r
67                 }\r
68                 supportAddress = env.getProperty(s=(REALM + ".supportEmail"), null);\r
69                 if(supportAddress==null) {\r
70                         throw new OrganizationException(s + PROPERTY_IS_REQUIRED);\r
71                 }\r
72                 \r
73                 String temp = env.getProperty(s=(REALM + ".mailFromUserId"), null);\r
74                 mailFromUserId = temp==null?supportAddress:temp;\r
75 \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
80 \r
81                 SUFFIX='.'+getDomain();\r
82                 \r
83                 try {\r
84                         String defFile;\r
85                         temp=env.getProperty(defFile = (getClass().getName()+".file"));\r
86                         File fIdentities=null;\r
87                         if(temp==null) {\r
88                                 temp = env.getProperty("aaf_data_dir");\r
89                                 if(temp!=null) {\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
95                                                 if(!dir.exists()) {\r
96                                                         dir.mkdirs();\r
97                                                 }\r
98                                                 fIdentities.createNewFile();\r
99                                         }\r
100                                 }\r
101                         } else {\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
107                                         }\r
108                                 }\r
109                         }\r
110                         \r
111                         if(fIdentities!=null && fIdentities.exists()) {\r
112                                 identities = new Identities(fIdentities);\r
113                         } else {\r
114                                 throw new OrganizationException(fIdentities.getCanonicalPath() + " does not exist.");\r
115                         }\r
116                 } catch (IOException e) {\r
117                         throw new OrganizationException(e);\r
118                 }\r
119         }\r
120         \r
121         // Implement your own Delegation System\r
122         static final List<String> NULL_DELEGATES = new ArrayList<String>();\r
123 \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
129         \r
130         static {\r
131                 typeSet = new HashSet<String>();\r
132                 for(Types t : Types.values()) {\r
133                         typeSet.add(t.name());\r
134                 }\r
135         }\r
136         \r
137         private static final EmailWarnings emailWarnings = new DefaultOrgWarnings();\r
138 \r
139         @Override\r
140         public String getName() {\r
141                 return NAME;\r
142         }\r
143 \r
144         @Override\r
145         public String getRealm() {\r
146                 return REALM;\r
147         }\r
148 \r
149         @Override\r
150         public String getDomain() {\r
151                 return DOMAIN;\r
152         }\r
153 \r
154         @Override\r
155         public DefaultOrgIdentity getIdentity(AuthzTrans trans, String id) throws OrganizationException {\r
156                 return new DefaultOrgIdentity(trans,id,this);\r
157         }\r
158 \r
159         // Note: Return a null if found; return a String Message explaining why not found. \r
160         @Override\r
161         public String isValidID(String id) {\r
162                 Data data;\r
163                 try {\r
164                         data = identities.find(id, identities.reuse());\r
165                 } catch (IOException e) {\r
166                         return getName() + " could not lookup " + id + ": " + e.getLocalizedMessage();\r
167                 }\r
168                 return data==null?id + "is not an Identity in " + getName():null;\r
169         }\r
170 \r
171         @Override\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
174                 return NO_PASS;\r
175         }\r
176 \r
177         @Override\r
178         public Set<String> getIdentityTypes() {\r
179                 return typeSet;\r
180         }\r
181 \r
182         @Override\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
185 \r
186                 ArrayList<String> toList = new ArrayList<String>();\r
187                 Identity identity;\r
188                 if (identities != null) {\r
189                         for (String user : identities) {\r
190                                 try {\r
191                                         identity = getIdentity(trans, user);\r
192                                         if (identity == null) {\r
193                                                 trans.error().log(\r
194                                                                 "Failure to obtain User " + user + " for "\r
195                                                                                 + getName());\r
196                                         } else {\r
197                                                 toList.add(identity.email());\r
198                                         }\r
199                                 } catch (Exception e) {\r
200                                         trans.error().log(\r
201                                                         e,\r
202                                                         "Failure to obtain User " + user + " for "\r
203                                                                         + getName());\r
204                                 }\r
205                         }\r
206                 }\r
207 \r
208                 if (toList.isEmpty()) {\r
209                         trans.error().log("No Users listed to email");\r
210                         return Response.ERR_NotificationFailure;\r
211                 }\r
212 \r
213                 ArrayList<String> ccList = new ArrayList<String>();\r
214 \r
215                 // If we're sending an urgent email, CC the user's supervisor\r
216                 //\r
217                 if (urgent) {\r
218                         trans.info().log("urgent msg for: " + identities[0]);\r
219                         try {\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
224                                 }\r
225                         } catch (Exception e) {\r
226                                 trans.error().log(e,\r
227                                                 "Failed to find supervisor for  " + identities[0]);\r
228                         }\r
229                 }\r
230 \r
231                 if (ccs != null) {\r
232                         for (String user : ccs) {\r
233                                 try {\r
234                                         identity = getIdentity(trans, user);\r
235                                         ccList.add(identity.email());\r
236                                 } catch (Exception e) {\r
237                                         trans.error().log(\r
238                                                         e,\r
239                                                         "Failure to obtain User " + user + " for "\r
240                                                                         + getName());\r
241                                 }\r
242                         }\r
243                 }\r
244 \r
245                 if (summary == null) {\r
246                         summary = "";\r
247                 }\r
248 \r
249                 switch (type) {\r
250                 case Approval:\r
251                         try {\r
252                                 sendEmail(trans, toList, ccList,\r
253                                                 "AAF Approval Notification "\r
254                                                                 + (system.length() == 0 ? "" : "(ENV: "\r
255                                                                                 + system + ")"),\r
256                                                 "AAF is the "\r
257                                                 + NAME\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
267                         }\r
268                         break;\r
269                 case PasswordExpiration:\r
270                         try {\r
271                                 sendEmail(trans,\r
272                                                 toList,\r
273                                                 ccList,\r
274                                                 "AAF Password Expiration Warning "\r
275                                                                 + (system.length() == 0 ? "" : "(ENV: "\r
276                                                                                 + system + ")"),\r
277                                                 "AAF is the "\r
278                                                 + NAME\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
289                         }\r
290                         break;\r
291 \r
292                 case RoleExpiration:\r
293                         try {\r
294                                 sendEmail(\r
295                                                 trans,\r
296                                                 toList,\r
297                                                 ccList,\r
298                                                 "AAF Role Expiration Warning "\r
299                                                                 + (system.length() == 0 ? "" : "(ENV: "\r
300                                                                                 + system + ")"),\r
301                                                 "AAF is the "\r
302                                                 + NAME\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
313                         }\r
314                         break;\r
315                 default:\r
316                         return Response.ERR_NotImplemented;\r
317                 }\r
318                 return Response.OK;\r
319         }\r
320 \r
321         @Override\r
322         public int sendEmail(AuthzTrans trans, List<String> toList, List<String> ccList, String subject, String body,\r
323                         Boolean urgent) throws OrganizationException {\r
324                 int status = 1;\r
325                 \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
330                         } else {\r
331                                 to.add(em);\r
332                         }\r
333                 }\r
334                 \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
340                                 } else {\r
341                                         cc.add(em);\r
342                                 }\r
343                         }\r
344                 }\r
345                 \r
346         \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
350                 }\r
351 \r
352                 try {\r
353                         // Create a default MimeMessage object.\r
354                         MimeMessage message = new MimeMessage(session);\r
355 \r
356                         // Set From: header field of the header.\r
357                         message.setFrom(new InternetAddress(mailFromUserId));\r
358 \r
359                         if (!dryRun) {\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
362                                 // blank\r
363                                 message.addRecipients(Message.RecipientType.TO,\r
364                                                 getAddresses(to));\r
365 \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
369                                                         getAddresses(cc));\r
370                                 }\r
371 \r
372                                 // Set Subject: header field\r
373                                 message.setSubject(subject);\r
374 \r
375                                 if (urgent) {\r
376                                         message.addHeader("X-Priority", "1");\r
377                                 }\r
378 \r
379                                 // Now set the actual message\r
380                                 message.setText(body);\r
381                         } else {\r
382                                 // override recipients\r
383                                 message.addRecipients(Message.RecipientType.TO,\r
384                                                 InternetAddress.parse(supportAddress));\r
385 \r
386                                 // Set Subject: header field\r
387                                 message.setSubject("[TESTMODE] " + subject);\r
388 \r
389                                 if (urgent) {\r
390                                         message.addHeader("X-Priority", "1");\r
391                                 }\r
392 \r
393                                 ArrayList<String> newBody = new ArrayList<String>();\r
394 \r
395                                 Address temp[] = getAddresses(to);\r
396                                 String headerString = "TO:\t" + InternetAddress.toString(temp)\r
397                                                 + "\n";\r
398 \r
399                                 temp = getAddresses(cc);\r
400                                 headerString += "CC:\t" + InternetAddress.toString(temp) + "\n";\r
401 \r
402                                 newBody.add(headerString);\r
403 \r
404                                 newBody.add("Text: \n");\r
405 \r
406                                 newBody.add(body);\r
407                                 String outString = "";\r
408                                 for (String s : newBody) {\r
409                                         outString += s + "\n";\r
410                                 }\r
411 \r
412                                 message.setText(outString);\r
413                         }\r
414                         // Send message\r
415                         Transport.send(message);\r
416                         status = 0;\r
417 \r
418                 } catch (MessagingException mex) {\r
419                         throw new OrganizationException("Exception send email message "\r
420                                         + mex.getMessage());\r
421                 }\r
422 \r
423                 return status;  \r
424         }\r
425 \r
426         /**\r
427          * Default Policy is to set to 6 Months for Notification Types.\r
428          * add others/change as required\r
429          */\r
430         @Override\r
431         public Date whenToValidate(Notify type, Date lastValidated) {\r
432                 switch(type) {\r
433                         case Approval:\r
434                         case PasswordExpiration:\r
435                                 return null;\r
436                         default:\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
441                 }\r
442         }\r
443 \r
444         @Override\r
445         public GregorianCalendar expiration(GregorianCalendar gc, Expiration exp, String... extra) {\r
446         GregorianCalendar rv = gc==null?new GregorianCalendar():(GregorianCalendar)gc.clone();\r
447                 switch (exp) {\r
448                         case ExtendPassword:\r
449                                 // Extending Password give 5 extra days\r
450                                 rv.add(GregorianCalendar.DATE, 5);\r
451                                 break;\r
452                         case Future:\r
453                                 // Future Requests last 15 days before subject to deletion.\r
454                                 rv.add(GregorianCalendar.DATE, 15);\r
455                                 break;\r
456                         case Password:\r
457                                 // Passwords expire in 90 days\r
458                                 rv.add(GregorianCalendar.DATE, 90);\r
459                                 break;\r
460                         case TempPassword:\r
461                                 // Temporary Passwords last for 12 hours.\r
462                                 rv.add(GregorianCalendar.HOUR, 12);\r
463                                 break;\r
464                         case UserDelegate:\r
465                                 // Delegations expire max in 2 months\r
466                                 rv.add(GregorianCalendar.MONTH, 2);\r
467                                 break;\r
468                         case UserInRole:\r
469                                 // Roles expire in 6 months\r
470                                 rv.add(GregorianCalendar.MONTH, 6);\r
471                                 break;\r
472                         default:\r
473                                 // Unless other wise set, 6 months is default\r
474                                 rv.add(GregorianCalendar.MONTH, 6);\r
475                                 break;\r
476                 }\r
477                 return rv;\r
478         }\r
479 \r
480         @Override\r
481         public EmailWarnings emailWarningPolicy() {\r
482                 return emailWarnings;\r
483         }\r
484 \r
485         /**\r
486          * Assume the Supervisor is the Approver.\r
487          */\r
488         @Override\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
496                         }\r
497                         Identity supervisor = getIdentity(trans, supervisorID);\r
498                         orgIdentitys.add(supervisor);\r
499                 }\r
500                 return orgIdentitys;    \r
501         }\r
502 \r
503         @Override\r
504         public String getApproverType() {\r
505                 return "supervisor";\r
506         }\r
507 \r
508         @Override\r
509         public int startOfDay() {\r
510                 // TODO Auto-generated method stub\r
511                 return 0;\r
512         }\r
513 \r
514         @Override\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
518                 //\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
522                 return true;\r
523         }\r
524 \r
525         @Override\r
526         public boolean isValidCred(String id) {\r
527                 if(id.endsWith(SUFFIX)) {\r
528                         return true;\r
529                 }\r
530                 return id.matches(ID_PATTERN);\r
531         }\r
532 \r
533         @Override\r
534         public String validate(AuthzTrans trans, Policy policy, Executor executor, String... vars) throws OrganizationException {\r
535                 switch(policy) {\r
536                         case OWNS_MECHID:\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
543                                                         return null;\r
544                                                 }\r
545                                         }\r
546                                 }\r
547                                 return trans.user() + " is not the Sponsor of MechID " + vars[0];\r
548                                 \r
549                         case CREATE_MECHID_BY_PERM_ONLY:\r
550                                 return getName() + " only allows sponsors to create MechIDs";\r
551                                 \r
552                         default:\r
553                                 return policy.name() + " is unsupported at " + getName();\r
554                 }       \r
555         }\r
556 \r
557         @Override\r
558         public boolean isTestEnv() {\r
559                 return false;\r
560         }\r
561 \r
562         @Override\r
563         public void setTestMode(boolean dryRun) {\r
564                 this.dryRun = dryRun;\r
565         }\r
566 \r
567         /**\r
568          * Convert the delimiter String into Internet addresses with the default\r
569          * delimiter of ";"\r
570          * @param strAddress\r
571          * @return\r
572          */\r
573         private Address[] getAddresses(List<String> strAddress) throws OrganizationException {\r
574                 return this.getAddresses(strAddress,";");\r
575         }\r
576         /**\r
577          * Convert the delimiter String into Internet addresses with the \r
578          * delimiter of provided\r
579          * @param strAddress\r
580          * @param delimiter\r
581          * @return\r
582          */\r
583         private Address[] getAddresses(List<String> strAddresses, String delimiter) throws OrganizationException {\r
584                 Address[] addressArray = new Address[strAddresses.size()];\r
585                 int count = 0;\r
586                 for (String addr : strAddresses)\r
587                 {\r
588             try{\r
589                 addressArray[count] = new InternetAddress(addr);\r
590                 count++;\r
591             }catch(Exception e){\r
592                 throw new OrganizationException("Failed to parse the email address "+ addr +": "+e.getMessage());\r
593             }\r
594         }\r
595         return addressArray;\r
596         }\r
597 }\r