X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=auth%2Fauth-deforg%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Faaf%2Forg%2FDefaultOrg.java;h=92db46945288e8637fecbeba34b9e8e368a516e0;hb=d86e3224e6a5af2bd2b713f93bea5e6677d3ebc2;hp=eefb27325b44d04eb4e9e329c5a3eb50c0bb1f77;hpb=eeed2ae14fceb21c9518a0056b2a29039b69f092;p=aaf%2Fauthz.git diff --git a/auth/auth-deforg/src/main/java/org/onap/aaf/org/DefaultOrg.java b/auth/auth-deforg/src/main/java/org/onap/aaf/org/DefaultOrg.java index eefb2732..92db4694 100644 --- a/auth/auth-deforg/src/main/java/org/onap/aaf/org/DefaultOrg.java +++ b/auth/auth-deforg/src/main/java/org/onap/aaf/org/DefaultOrg.java @@ -31,679 +31,611 @@ import java.util.List; import java.util.Set; import java.util.regex.Pattern; -import javax.mail.Address; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; - import org.onap.aaf.auth.env.AuthzTrans; import org.onap.aaf.auth.org.EmailWarnings; import org.onap.aaf.auth.org.Executor; +import org.onap.aaf.auth.org.Mailer; import org.onap.aaf.auth.org.Organization; import org.onap.aaf.auth.org.OrganizationException; +import org.onap.aaf.cadi.config.Config; import org.onap.aaf.cadi.util.FQI; import org.onap.aaf.misc.env.Env; public class DefaultOrg implements Organization { - private static final String AAF_DATA_DIR = "aaf_data_dir"; - private static final String PROPERTY_IS_REQUIRED = " property is Required"; - // Package on Purpose - final String domain; - final String atDomain; - final String realm; - - private final String NAME,mailHost,mailFrom; - private final Set supportedRealms; - - - public DefaultOrg(Env env, String realm) throws OrganizationException { - - this.realm = realm; - supportedRealms=new HashSet(); - supportedRealms.add(realm); - domain=FQI.reverseDomain(realm); - atDomain = '@'+domain; - String s; - NAME=env.getProperty(realm + ".name","Default Organization"); - mailHost = env.getProperty(s=(realm + ".mailHost"), null); - if(mailHost==null) { - throw new OrganizationException(s + PROPERTY_IS_REQUIRED); - } - mailFrom = env.getProperty(s=(realm + ".mailFrom"), null); - if(mailFrom==null) { - throw new OrganizationException(s + PROPERTY_IS_REQUIRED); - } - - System.getProperties().setProperty("mail.smtp.host",mailHost); - System.getProperties().setProperty("mail.user", mailFrom); - // Get the default Session object. - session = Session.getDefaultInstance(System.getProperties()); - - try { - String defFile; - String temp=env.getProperty(defFile = (getClass().getName()+".file")); - File fIdentities=null; - if(temp==null) { - temp = env.getProperty(AAF_DATA_DIR); - if(temp!=null) { - env.warn().log(defFile, " is not defined. Using default: ",temp+"/identities.dat"); - File dir = new File(temp); - fIdentities=new File(dir,"identities.dat"); - - if(!fIdentities.exists()) { - env.warn().log("No",fIdentities.getCanonicalPath(),"exists. Creating."); - if(!dir.exists()) { - dir.mkdirs(); - } - fIdentities.createNewFile(); - } - } - } else { - fIdentities = new File(temp); - if(!fIdentities.exists()) { - String dataDir = env.getProperty(AAF_DATA_DIR); - if(dataDir!=null) { - fIdentities = new File(dataDir,temp); - } - } - } - - if(fIdentities!=null && fIdentities.exists()) { - identities = new Identities(fIdentities); - } else { - if(fIdentities==null) { - throw new OrganizationException("No Identities"); - } else { - throw new OrganizationException(fIdentities.getCanonicalPath() + " does not exist."); - } - } - } catch (IOException e) { - throw new OrganizationException(e); - } - } - - // Implement your own Delegation System - static final List NULL_DELEGATES = new ArrayList(); - - public Identities identities; - private boolean dryRun; - private Session session; - public enum Types {Employee, Contractor, Application, NotActive}; - private final static Set typeSet; - - static { - typeSet = new HashSet(); - for(Types t : Types.values()) { - typeSet.add(t.name()); - } - } - - private static final EmailWarnings emailWarnings = new DefaultOrgWarnings(); - - @Override - public String getName() { - return NAME; - } - - @Override - public String getRealm() { - return realm; - } - - @Override - public String getDomain() { - return domain; - } - - @Override - public DefaultOrgIdentity getIdentity(AuthzTrans trans, String id) throws OrganizationException { - int at = id.indexOf('@'); - return new DefaultOrgIdentity(trans,at<0?id:id.substring(0, at),this); - } - - // Note: Return a null if found; return a String Message explaining why not found. - @Override - public String isValidID(final AuthzTrans trans, final String id) { - try { - DefaultOrgIdentity u = getIdentity(trans,id); - return (u==null||!u.isFound())?id + "is not an Identity in " + getName():null; - } catch (OrganizationException e) { - return getName() + " could not lookup " + id + ": " + e.getLocalizedMessage(); - } - } - // Possible ID Pattern - // private static final Pattern ID_PATTERN=Pattern.compile("([\\w.-]+@[\\w.-]+).{4-13}"); - // Another one: ID_PATTERN = "(a-z[a-z0-9]{5-8}@.*).{4-13}"; - - @Override - public boolean isValidCred(final AuthzTrans trans, final String id) { - // have domain? - int at = id.indexOf('@'); - String sid; - if(at > 0) { - // Use this to prevent passwords to any but THIS domain. -// if(!id.regionMatches(at+1, domain, 0, id.length()-at-1)) { -// return false; -// } - sid = id.substring(0,at); - } else { - sid = id; - } - // We'll validate that it exists, rather than check patterns. - - return isValidID(trans, sid)==null; - // Check Pattern (if checking existing is too long) - // if(id.endsWith(SUFFIX) && ID_PATTERN.matcher(id).matches()) { - // return true; - // } - // return false; - } - - private static final String SPEC_CHARS = "!@#$%^*-+?/,:;."; - private static final Pattern PASS_PATTERN=Pattern.compile("((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[" + SPEC_CHARS +"]).{6,20})"); - /** - * Attribution: from mkyong.com - * ( # Start of group - * (?=.*\d) # must contains one digit from 0-9 - * (?=.*[a-z]) # must contains one lowercase characters - * (?=.*[A-Z]) # must contains one uppercase characters - * (?=.*[@#$%]) # must contains one special symbols in the list SPEC_CHARS - * . # match anything with previous condition checking - * {6,20} # length at least 6 characters and maximum of 20 - * ) # End of group + private static final String AAF_DATA_DIR = "aaf_data_dir"; + // Package on Purpose + final String domain; + final String atDomain; + final String realm; + + private final String root_ns; + + private final String NAME; + private final Set supportedRealms; + + + + public DefaultOrg(Env env, String realm) throws OrganizationException { + + this.realm = realm; + supportedRealms=new HashSet<>(); + supportedRealms.add(realm); + domain=FQI.reverseDomain(realm); + atDomain = '@'+domain; + NAME=env.getProperty(realm + ".name","Default Organization"); + root_ns = env.getProperty(Config.AAF_ROOT_NS,Config.AAF_ROOT_NS_DEF); + + try { + String defFile; + String temp=env.getProperty(defFile = (getClass().getName()+".file")); + File fIdentities=null; + if (temp==null) { + temp = env.getProperty(AAF_DATA_DIR); + if (temp!=null) { + env.warn().log(defFile, " is not defined. Using default: ",temp+"/identities.dat"); + File dir = new File(temp); + fIdentities=new File(dir,"identities.dat"); + + if (!fIdentities.exists()) { + env.warn().log("No",fIdentities.getCanonicalPath(),"exists. Creating."); + if (!dir.exists()) { + dir.mkdirs(); + } + fIdentities.createNewFile(); + } + } + } else { + fIdentities = new File(temp); + if (!fIdentities.exists()) { + String dataDir = env.getProperty(AAF_DATA_DIR); + if (dataDir!=null) { + fIdentities = new File(dataDir,temp); + } + } + } + + if (fIdentities!=null && fIdentities.exists()) { + identities = new Identities(fIdentities); + } else { + if (fIdentities==null) { + throw new OrganizationException("No Identities: set \"" + AAF_DATA_DIR + '"'); + } else { + throw new OrganizationException(fIdentities.getCanonicalPath() + " does not exist."); + } + } + } catch (IOException e) { + throw new OrganizationException(e); + } + } + + // Implement your own Delegation System + static final List NULL_DELEGATES = new ArrayList<>(); + + public Identities identities; + private boolean dryRun; + private Mailer mailer; + public enum Types {Employee, Contractor, Application, NotActive}; + private final static Set typeSet; + + static { + typeSet = new HashSet<>(); + for (Types t : Types.values()) { + typeSet.add(t.name()); + } + } + + private static final EmailWarnings emailWarnings = new DefaultOrgWarnings(); + + @Override + public String getName() { + return NAME; + } + + @Override + public String getRealm() { + return realm; + } + + @Override + public String getDomain() { + return domain; + } + + @Override + public DefaultOrgIdentity getIdentity(AuthzTrans trans, String id) throws OrganizationException { + int at = id.indexOf('@'); + return new DefaultOrgIdentity(trans,at<0?id:id.substring(0, at),this); + } + + /* (non-Javadoc) + * @see org.onap.aaf.auth.org.Organization#getEsclaations(org.onap.aaf.auth.env.AuthzTrans, java.lang.String, int) */ @Override - public String isValidPassword(final AuthzTrans trans, final String user, final String password, final String... prev) { - for(String p : prev) { - if(password.contains(p)) { // A more sophisticated algorithm might be better. - return "Password too similar to previous passwords"; - } - } - // If you have an Organization user/Password scheme, replace the following - if(PASS_PATTERN.matcher(password).matches()) { - return ""; - } - return "Password does not match " + NAME + " Password Standards"; - } - - private static final String[] rules = new String[] { - "Passwords must contain one digit from 0-9", - "Passwords must contain one lowercase character", - "Passwords must contain one uppercase character", - "Passwords must contain one special symbols in the list \""+ SPEC_CHARS + '"', - "Passwords must be between 6 and 20 chars in length" - }; - - @Override - public String[] getPasswordRules() { - return rules; - } - - @Override - public Set getIdentityTypes() { - return typeSet; - } - - @Override - public Response notify(AuthzTrans trans, Notify type, String url, String[] identities, String[] ccs, String summary, Boolean urgent) { - String system = trans.getProperty("CASS_ENV", ""); - - ArrayList toList = new ArrayList(); - Identity identity; - if (identities != null) { - for (String user : identities) { - try { - identity = getIdentity(trans, user); - if (identity == null) { - trans.error().log( - "Failure to obtain User " + user + " for " - + getName()); - } else { - toList.add(identity.email()); - } - } catch (Exception e) { - trans.error().log( - e, - "Failure to obtain User " + user + " for " - + getName()); - } - } - } - - if (toList.isEmpty()) { - trans.error().log("No Users listed to email"); - return Response.ERR_NotificationFailure; - } - - ArrayList ccList = new ArrayList(); - - // If we're sending an urgent email, CC the user's supervisor - // - if (urgent) { - trans.info().log("urgent msg for: " + identities[0]); - try { - List supervisors = getApprovers(trans, identities[0]); - for (Identity us : supervisors) { - trans.info().log("supervisor: " + us.email()); - ccList.add(us.email()); - } - } catch (Exception e) { - trans.error().log(e, - "Failed to find supervisor for " + identities[0]); - } - } - - if (ccs != null) { - for (String user : ccs) { - try { - identity = getIdentity(trans, user); - ccList.add(identity.email()); - } catch (Exception e) { - trans.error().log( - e, - "Failure to obtain User " + user + " for " - + getName()); - } - } - } - - if (summary == null) { - summary = ""; - } - - switch (type) { - case Approval: - try { - sendEmail(trans, toList, ccList, - "AAF Approval Notification " - + (system.length() == 0 ? "" : "(ENV: " - + system + ")"), - "AAF is the " - + NAME - + "System for Fine-Grained Authorizations. You are being asked to Approve" - + (system.length() == 0 ? "" : " in the " - + system + " environment") - + " before AAF Actions can be taken.\n\n" - + "Please follow this link: \n\n\t" + url - + "\n\n" + summary, urgent); - } catch (Exception e) { - - trans.error().log(e, "Failure to send Email"); - return Response.ERR_NotificationFailure; - } - break; - case PasswordExpiration: - try { - sendEmail(trans, - toList, - ccList, - "AAF Password Expiration Warning " - + (system.length() == 0 ? "" : "(ENV: " - + system + ")"), - "AAF is the " - + NAME - + " System for Authorizations.\n\nOne or more passwords will expire soon or have expired" - + (system.length() == 0 ? "" : " in the " - + system + " environment") - + ".\n\nPasswords expired for more than 30 days without action are subject to deletion.\n\n" - + "Please follow each link to add a New Password with Expiration Date. Either are valid until expiration. " - + "Use this time to change the passwords on your system. If issues, reply to this email.\n\n" - + summary, urgent); - } catch (Exception e) { - trans.error().log(e, "Failure to send Email"); - return Response.ERR_NotificationFailure; - } - break; - - case RoleExpiration: - try { - sendEmail( - trans, - toList, - ccList, - "AAF Role Expiration Warning " - + (system.length() == 0 ? "" : "(ENV: " - + system + ")"), - "AAF is the " - + NAME - + " System for Authorizations. One or more roles will expire soon" - + (system.length() == 0 ? "" : " in the " - + system + " environment") - + ".\n\nRoles expired for more than 30 days are subject to deletion." - + "Please follow this link the GUI Command line, and either 'extend' or 'del' the user in the role.\n" - + "If issues, reply to this email.\n\n\t" + url - + "\n\n" + summary, urgent); - } catch (Exception e) { - trans.error().log(e, "Failure to send Email"); - return Response.ERR_NotificationFailure; - } - break; - default: - return Response.ERR_NotImplemented; - } - return Response.OK; - } - - @Override - public int sendEmail(AuthzTrans trans, List toList, List ccList, String subject, String body, - Boolean urgent) throws OrganizationException { - - int status = 1; - - List to = new ArrayList(); - for(String em : toList) { - if(em.indexOf('@')<0) { - to.add(new DefaultOrgIdentity(trans, em, this).email()); + public List getIDs(AuthzTrans trans, String user, int escalate) throws OrganizationException { + List rv = new ArrayList<>(); + int end = Math.min(3,Math.abs(escalate)); + Identity id = null; + for(int i=0;i cc = new ArrayList(); - if(ccList!=null) { - if(!ccList.isEmpty()) { - - for(String em : ccList) { - if(em.indexOf('@')<0) { - cc.add(new DefaultOrgIdentity(trans, em, this).email()); - } else { - cc.add(em); - } - } + id = id.responsibleTo(); } - - // for now, I want all emails so we can see what goes out. Remove later - if (!ccList.contains(mailFrom)) { - ccList.add(mailFrom); - } - } - - try { - // Create a default MimeMessage object. - MimeMessage message = new MimeMessage(session); - - // Set From: header field of the header. - message.setFrom(new InternetAddress(mailFrom)); - - if (!dryRun) { - // Set To: header field of the header. This is a required field - // and calling module should make sure that it is not null or - // blank - message.addRecipients(Message.RecipientType.TO,getAddresses(to)); - - // Set CC: header field of the header. - if ((ccList != null) && (ccList.size() > 0)) { - message.addRecipients(Message.RecipientType.CC,getAddresses(cc)); - } - - // Set Subject: header field - message.setSubject(subject); - - if (urgent) { - message.addHeader("X-Priority", "1"); - } - - // Now set the actual message - message.setText(body); + if(id==null) { + break; } else { - - // override recipients - message.addRecipients(Message.RecipientType.TO, - InternetAddress.parse(mailFrom)); - - // Set Subject: header field - message.setSubject("[TESTMODE] " + subject); - - if (urgent) { - message.addHeader("X-Priority", "1"); - } - - ArrayList newBody = new ArrayList(); - - Address temp[] = getAddresses(to); - String headerString = "TO:\t" + InternetAddress.toString(temp) + "\n"; - - temp = getAddresses(cc); - headerString += "CC:\t" + InternetAddress.toString(temp) + "\n"; - - newBody.add(headerString); - - newBody.add("Text: \n"); - - newBody.add(body); - String outString = ""; - for (String s : newBody) { - outString += s + "\n"; - } - - message.setText(outString); + rv.add(id); } - // Send message - Transport.send(message); - status = 0; - - } catch (MessagingException mex) { - System.out.println("Error messaging: "+ mex.getMessage()); - System.out.println("Error messaging: "+ mex.toString()); - throw new OrganizationException("Exception send email message " - + mex.getMessage()); - } - - return status; - } - - /** - * Default Policy is to set to 6 Months for Notification Types. - * add others/change as required - */ - @Override - public Date whenToValidate(Notify type, Date lastValidated) { - switch(type) { - case Approval: - case PasswordExpiration: - return null; - default: - GregorianCalendar gc = new GregorianCalendar(); - gc.setTime(lastValidated); - gc.add(GregorianCalendar.MONTH, 6); // 6 month policy - return gc.getTime(); - } - } - - @Override - public GregorianCalendar expiration(GregorianCalendar gc, Expiration exp, String... extra) { - GregorianCalendar now = new GregorianCalendar(); - GregorianCalendar rv = gc==null?now:(GregorianCalendar)gc.clone(); - switch (exp) { - case ExtendPassword: - // Extending Password give 5 extra days, max 8 days from now - rv.add(GregorianCalendar.DATE, 5); - now.add(GregorianCalendar.DATE, 8); - if(rv.after(now)) { - rv = now; - } - break; - case Future: - // Future requests last 15 days. - now.add(GregorianCalendar.DATE, 15); - rv = now; - break; - case Password: - // Passwords expire in 90 days - now.add(GregorianCalendar.DATE, 90); - rv = now; - break; - case TempPassword: - // Temporary Passwords last for 12 hours. - now.add(GregorianCalendar.DATE, 90); - rv = now; - break; - case UserDelegate: - // Delegations expire max in 2 months, renewable to 3 - rv.add(GregorianCalendar.MONTH, 2); - now.add(GregorianCalendar.MONTH, 3); - if(rv.after(now)) { - rv = now; - } - break; - case UserInRole: - // Roles expire in 6 months - now.add(GregorianCalendar.MONTH, 6); - rv = now; - break; - default: - // Unless other wise set, 6 months is default - now.add(GregorianCalendar.MONTH, 6); - rv = now; - break; } return rv; } - @Override - public EmailWarnings emailWarningPolicy() { - return emailWarnings; - } - - /** - * Assume the Supervisor is the Approver. - */ - @Override - public List getApprovers(AuthzTrans trans, String user) throws OrganizationException { - Identity orgIdentity = getIdentity(trans, user); - List orgIdentitys = new ArrayList(); - if(orgIdentity!=null) { - Identity supervisor = orgIdentity.responsibleTo(); - if(supervisor!=null) { - orgIdentitys.add(supervisor); - } - } - return orgIdentitys; - } - - @Override - public String getApproverType() { - return "supervisor"; - } - - @Override - public int startOfDay() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public boolean canHaveMultipleCreds(String id) { - // External entities are likely mono-password... if you change it, it is a global change. - // This is great for people, but horrible for Applications. - // - // AAF's Password can have multiple Passwords, each with their own Expiration Date. - // For Default Org, we'll assume true for all, but when you add your external - // Identity stores, you need to return "false" if they cannot support multiple Passwords like AAF - return true; - } - - @Override - public String validate(AuthzTrans trans, Policy policy, Executor executor, String... vars) throws OrganizationException { - switch(policy) { - case OWNS_MECHID: - case CREATE_MECHID: - if(vars.length>0) { - DefaultOrgIdentity thisID = getIdentity(trans,vars[0]); - if("a".equals(thisID.identity.status)) { // MechID - DefaultOrgIdentity requestor = getIdentity(trans, trans.user()); - if(requestor!=null) { - Identity mechid = getIdentity(trans, vars[0]); - if(mechid!=null) { - Identity sponsor = mechid.responsibleTo(); - if(sponsor!=null && requestor.fullID().equals(sponsor.fullID())) { - return null; - } else { - return trans.user() + " is not the Sponsor of MechID " + vars[0]; - } - } - } - } - } - return null; - - case CREATE_MECHID_BY_PERM_ONLY: - return getName() + " only allows sponsors to create MechIDs"; - - default: - return policy.name() + " is unsupported at " + getName(); - } - } - - @Override - public boolean isTestEnv() { - return false; - } - - @Override - public void setTestMode(boolean dryRun) { - this.dryRun = dryRun; - } - - /** - * Convert the delimiter String into Internet addresses with the default - * delimiter of ";" - * @param strAddress - * @return - */ - private Address[] getAddresses(List strAddress) throws OrganizationException { - return this.getAddresses(strAddress,";"); - } - /** - * Convert the delimiter String into Internet addresses with the - * delimiter of provided - * @param strAddresses - * @param delimiter - * @return - */ - private Address[] getAddresses(List strAddresses, String delimiter) throws OrganizationException { - Address[] addressArray = new Address[strAddresses.size()]; - int count = 0; - for (String addr : strAddresses) - { - try{ - addressArray[count] = new InternetAddress(addr); - count++; - }catch(Exception e){ - throw new OrganizationException("Failed to parse the email address "+ addr +": "+e.getMessage()); - } - } - return addressArray; - } + // Note: Return a null if found; return a String Message explaining why not found. + @Override + public String isValidID(final AuthzTrans trans, final String id) { + try { + DefaultOrgIdentity u = getIdentity(trans,id); + return (u==null||!u.isFound())?id + "is not an Identity in " + getName():null; + } catch (OrganizationException e) { + return getName() + " could not lookup " + id + ": " + e.getLocalizedMessage(); + } + } + // Possible ID Pattern + // private static final Pattern ID_PATTERN=Pattern.compile("([\\w.-]+@[\\w.-]+).{4-13}"); + // Another one: ID_PATTERN = "(a-z[a-z0-9]{5-8}@.*).{4-13}"; + + @Override + public boolean isValidCred(final AuthzTrans trans, final String id) { + // have domain? + int at = id.indexOf('@'); + String sid; + if (at > 0) { + // Use this to prevent passwords to any but THIS domain. +// if (!id.regionMatches(at+1, domain, 0, id.length()-at-1)) { +// return false; +// } + sid = id.substring(0,at); + } else { + sid = id; + } + // We'll validate that it exists, rather than check patterns. + + return isValidID(trans, sid)==null; + // Check Pattern (if checking existing is too long) + // if (id.endsWith(SUFFIX) && ID_PATTERN.matcher(id).matches()) { + // return true; + // } + // return false; + } + + private static final String SPEC_CHARS = "!@#$%^*-+?/,:;."; + private static final Pattern PASS_PATTERN=Pattern.compile("(((?=.*[a-z,A-Z])(((?=.*\\d))|(?=.*[" + SPEC_CHARS +"]))).{6,20})"); + /** + * ( # Start of group + * (?=.*[a-z,A-Z]) # must contain one character + * + * (?=.*\d) # must contain one digit from 0-9 + * OR + * (?=.*[@#$%]) # must contain one special symbols in the list SPEC_CHARS + * + * . # match anything with previous condition checking + * {6,20} # length at least 6 characters and maximum of 20 + * ) # End of group + * + * Another example, more stringent pattern + private static final Pattern PASS_PATTERN=Pattern.compile("((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[" + SPEC_CHARS +"]).{6,20})"); + * Attribution: from mkyong.com + * ( # Start of group + * (?=.*\d) # must contain one digit from 0-9 + * (?=.*[a-z]) # must contain one lowercase characters + * (?=.*[A-Z]) # must contain one uppercase characters + * (?=.*[@#$%]) # must contain one special symbols in the list SPEC_CHARS + * . # match anything with previous condition checking + * {6,20} # length at least 6 characters and maximum of 20 + * ) # End of group + */ + @Override + public String isValidPassword(final AuthzTrans trans, final String user, final String password, final String... prev) { + for (String p : prev) { + if (password.contains(p)) { // A more sophisticated algorithm might be better. + return "Password too similar to previous passwords"; + } + } + // If you have an Organization user/Password scheme, replace the following + if (PASS_PATTERN.matcher(password).matches()) { + return ""; + } + return "Password does not match " + NAME + " Password Standards"; + } + + private static final String[] rules = new String[] { + "Passwords must contain letters", + "Passwords must contain one of the following:", + " Number", + " One special symbols in the list \""+ SPEC_CHARS + '"', + "Passwords must be between 6 and 20 chars in length", + }; + + @Override + public String[] getPasswordRules() { + return rules; + } + + @Override + public Set getIdentityTypes() { + return typeSet; + } + + @Override + public Response notify(AuthzTrans trans, Notify type, String url, String[] identities, String[] ccs, String summary, Boolean urgent) { + String system = trans.getProperty("CASS_ENV", ""); + + ArrayList toList = new ArrayList<>(); + Identity identity; + if (identities != null) { + for (String user : identities) { + try { + identity = getIdentity(trans, user); + if (identity == null) { + trans.error().log( + "Failure to obtain User " + user + " for " + + getName()); + } else { + toList.add(identity.email()); + } + } catch (Exception e) { + trans.error().log( + e, + "Failure to obtain User " + user + " for " + + getName()); + } + } + } + + if (toList.isEmpty()) { + trans.error().log("No Users listed to email"); + return Response.ERR_NotificationFailure; + } + + ArrayList ccList = new ArrayList<>(); + + // If we're sending an urgent email, CC the user's supervisor + // + if (urgent) { + trans.info().log("urgent msg for: " + identities[0]); + try { + List supervisors = getApprovers(trans, identities[0]); + for (Identity us : supervisors) { + trans.info().log("supervisor: " + us.email()); + ccList.add(us.email()); + } + } catch (Exception e) { + trans.error().log(e, + "Failed to find supervisor for " + identities[0]); + } + } + + if (ccs != null) { + for (String user : ccs) { + try { + identity = getIdentity(trans, user); + ccList.add(identity.email()); + } catch (Exception e) { + trans.error().log( + e, + "Failure to obtain User " + user + " for " + + getName()); + } + } + } + + if (summary == null) { + summary = ""; + } + + switch (type) { + case Approval: + try { + sendEmail(trans, toList, ccList, + "AAF Approval Notification " + + (system.length() == 0 ? "" : "(ENV: " + + system + ")"), + "AAF is the " + + NAME + + "System for Fine-Grained Authorizations. You are being asked to Approve" + + (system.length() == 0 ? "" : " in the " + + system + " environment") + + " before AAF Actions can be taken.\n\n" + + "Please follow this link: \n\n\t" + url + + "\n\n" + summary, urgent); + } catch (Exception e) { + + trans.error().log(e, "Failure to send Email"); + return Response.ERR_NotificationFailure; + } + break; + case PasswordExpiration: + try { + sendEmail(trans, + toList, + ccList, + "AAF Password Expiration Warning " + + (system.length() == 0 ? "" : "(ENV: " + + system + ")"), + "AAF is the " + + NAME + + " System for Authorizations.\n\nOne or more passwords will expire soon or have expired" + + (system.length() == 0 ? "" : " in the " + + system + " environment") + + ".\n\nPasswords expired for more than 30 days without action are subject to deletion.\n\n" + + "Please follow each link to add a New Password with Expiration Date. Either are valid until expiration. " + + "Use this time to change the passwords on your system. If issues, reply to this email.\n\n" + + summary, urgent); + } catch (Exception e) { + trans.error().log(e, "Failure to send Email"); + return Response.ERR_NotificationFailure; + } + break; + + case RoleExpiration: + try { + sendEmail( + trans, + toList, + ccList, + "AAF Role Expiration Warning " + + (system.length() == 0 ? "" : "(ENV: " + + system + ")"), + "AAF is the " + + NAME + + " System for Authorizations. One or more roles will expire soon" + + (system.length() == 0 ? "" : " in the " + + system + " environment") + + ".\n\nRoles expired for more than 30 days are subject to deletion." + + "Please follow this link the GUI Command line, and either 'extend' or 'del' the user in the role.\n" + + "If issues, reply to this email.\n\n\t" + url + + "\n\n" + summary, urgent); + } catch (Exception e) { + trans.error().log(e, "Failure to send Email"); + return Response.ERR_NotificationFailure; + } + break; + default: + return Response.ERR_NotImplemented; + } + return Response.OK; + } + + + /** + * Default Policy is to set to 6 Months for Notification Types. + * add others/change as required + */ + @Override + public Date whenToValidate(Notify type, Date lastValidated) { + switch(type) { + case Approval: + case PasswordExpiration: + return null; + default: + GregorianCalendar gc = new GregorianCalendar(); + gc.setTime(lastValidated); + gc.add(GregorianCalendar.MONTH, 6); // 6 month policy + return gc.getTime(); + } + } + + @Override + public GregorianCalendar expiration(GregorianCalendar gc, Expiration exp, String... extra) { + GregorianCalendar now = new GregorianCalendar(); + GregorianCalendar rv = gc==null?now:(GregorianCalendar)gc.clone(); + switch (exp) { + case ExtendPassword: + // Extending Password give 5 extra days, max 8 days from now + rv.add(GregorianCalendar.DATE, 5); + now.add(GregorianCalendar.DATE, 8); + if (rv.after(now)) { + rv = now; + } + break; + case Future: + // Future requests last 15 days. + now.add(GregorianCalendar.DATE, 15); + rv = now; + break; + case Password: + // Passwords expire in 90 days + now.add(GregorianCalendar.DATE, 90); + rv = now; + break; + case TempPassword: + // Temporary Passwords last for 12 hours. + now.add(GregorianCalendar.DATE, 90); + rv = now; + break; + case UserDelegate: + // Delegations expire max in 2 months, renewable to 3 + rv.add(GregorianCalendar.MONTH, 2); + now.add(GregorianCalendar.MONTH, 3); + if (rv.after(now)) { + rv = now; + } + break; + case UserInRole: + // Roles expire in 6 months + now.add(GregorianCalendar.MONTH, 6); + rv = now; + break; + default: + // Unless other wise set, 6 months is default + now.add(GregorianCalendar.MONTH, 6); + rv = now; + break; + } + return rv; + } + + @Override + public EmailWarnings emailWarningPolicy() { + return emailWarnings; + } + + /** + * Assume the Supervisor is the Approver. + */ + @Override + public List getApprovers(AuthzTrans trans, String user) throws OrganizationException { + Identity orgIdentity = getIdentity(trans, user); + List orgIdentitys = new ArrayList<>(); + if (orgIdentity!=null) { + Identity supervisor = orgIdentity.responsibleTo(); + if (supervisor!=null) { + orgIdentitys.add(supervisor); + } + } + return orgIdentitys; + } + + @Override + public String getApproverType() { + return "supervisor"; + } + + @Override + public int startOfDay() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean canHaveMultipleCreds(String id) { + // External entities are likely mono-password... if you change it, it is a global change. + // This is great for people, but horrible for Applications. + // + // AAF's Password can have multiple Passwords, each with their own Expiration Date. + // For Default Org, we'll assume true for all, but when you add your external + // Identity stores, you need to return "false" if they cannot support multiple Passwords like AAF + return true; + } + + @Override + public String validate(AuthzTrans trans, Policy policy, Executor executor, String... vars) throws OrganizationException { + String user; + switch(policy) { + case OWNS_MECHID: + case CREATE_MECHID: + if (vars.length>0) { + DefaultOrgIdentity thisID = getIdentity(trans,vars[0]); + if ("a".equals(thisID.identity.status)) { // MechID + DefaultOrgIdentity requestor = getIdentity(trans, trans.user()); + if (requestor!=null) { + Identity mechid = getIdentity(trans, vars[0]); + if (mechid!=null) { + Identity sponsor = mechid.responsibleTo(); + if (sponsor!=null && requestor.fullID().equals(sponsor.fullID())) { + return null; + } else { + return trans.user() + " is not the Sponsor of MechID " + vars[0]; + } + } + } + } + } + return null; + + case CREATE_MECHID_BY_PERM_ONLY: + return getName() + " only allows sponsors to create MechIDs"; + + case MAY_EXTEND_CRED_EXPIRES: + // If parm, use it, otherwise, trans + user = vars.length>1?vars[1]:trans.user(); + return executor.hasPermission(user, root_ns,"password", root_ns , "extend") + ?null:user + " does not have permission to extend passwords at " + getName(); + + default: + return policy.name() + " is unsupported at " + getName(); + } + } + + @Override + public boolean isTestEnv() { + return false; + } + + @Override + public void setTestMode(boolean dryRun) { + this.dryRun = dryRun; + } + + private String extractRealm(final String r) { + int at; + if ((at=r.indexOf('@'))>=0) { + return FQI.reverseDomain(r.substring(at+1)); + } + return r; + } + @Override + public boolean supportsRealm(final String r) { + if (r.endsWith(realm)) { + return true; + } else { + String erealm = extractRealm(r); + for (String sr : supportedRealms) { + if (erealm.startsWith(sr)) { + return true; + } + } + } + return false; + } + + @Override + public synchronized void addSupportedRealm(final String r) { + supportedRealms.add(extractRealm(r)); + } + + @Override + public int sendEmail(AuthzTrans trans, List toList, List ccList, String subject, String body, + Boolean urgent) throws OrganizationException { + if (mailer!=null) { + String mailFrom = mailer.mailFrom(); + List to = new ArrayList<>(); + for (String em : toList) { + if (em.indexOf('@')<0) { + to.add(new DefaultOrgIdentity(trans, em, this).email()); + } else { + to.add(em); + } + } + + List cc = new ArrayList<>(); + if (ccList!=null) { + if (!ccList.isEmpty()) { + + for (String em : ccList) { + if (em.indexOf('@')<0) { + cc.add(new DefaultOrgIdentity(trans, em, this).email()); + } else { + cc.add(em); + } + } + } + + // for now, I want all emails so we can see what goes out. Remove later + if (!ccList.contains(mailFrom)) { + ccList.add(mailFrom); + } + } + + return mailer.sendEmail(trans,dryRun?"DefaultOrg":null,to,cc,subject,body,urgent)?0:1; + } else { + return 0; + } + } - private String extractRealm(final String r) { - int at; - if((at=r.indexOf('@'))>=0) { - return FQI.reverseDomain(r.substring(at+1)); - } - return r; - } @Override - public boolean supportsRealm(final String r) { - if(r.endsWith(realm)) { - return true; - } else { - String erealm = extractRealm(r); - for(String sr : supportedRealms) { - if(erealm.startsWith(sr)) { - return true; - } - } - } + public boolean mayAutoDelete(AuthzTrans trans, String user) { + // provide a corresponding feed that indicates that an ID has been intentionally removed from identities.dat table. return false; } - - @Override - public synchronized void addSupportedRealm(final String r) { - supportedRealms.add(extractRealm(r)); - } - }