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