KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mortbay > http > handler > HTAccessHandler


1 // ========================================================================
2
// Authors : Van den Broeke Iris, Deville Daniel, Dubois Roger, Greg Wilkins
3
// Copyright (c) 2001 Deville Daniel. All rights reserved.
4
// Permission to use, copy, modify and distribute this software
5
// for non-commercial or commercial purposes and without fee is
6
// hereby granted provided that this copyright notice appears in
7
// all copies.
8
// ========================================================================
9

10 package org.mortbay.http.handler;
11
12 import java.io.BufferedReader JavaDoc;
13 import java.io.IOException JavaDoc;
14 import java.io.InputStreamReader JavaDoc;
15 import java.security.Principal JavaDoc;
16 import java.util.ArrayList JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.HashSet JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.StringTokenizer JavaDoc;
21
22 import org.apache.commons.logging.Log;
23 import org.mortbay.log.LogFactory;
24 import org.mortbay.http.HttpContext;
25 import org.mortbay.http.HttpException;
26 import org.mortbay.http.HttpFields;
27 import org.mortbay.http.HttpRequest;
28 import org.mortbay.http.HttpResponse;
29 import org.mortbay.http.SecurityConstraint;
30 import org.mortbay.http.UserRealm;
31 import org.mortbay.util.B64Code;
32 import org.mortbay.util.LogSupport;
33 import org.mortbay.util.Resource;
34 import org.mortbay.util.StringUtil;
35 import org.mortbay.util.URI;
36 import org.mortbay.util.UnixCrypt;
37
38 /* ------------------------------------------------------------ */
39 /** Handler to authenticate access using the Apache's .htaccess files.
40  *
41  * @version HTAccessHandler v1.0a
42  * @author Van den Broeke Iris
43  * @author Deville Daniel
44  * @author Dubois Roger
45  * @author Greg Wilkins
46  * @author Konstantin Metlov
47  *
48  */

49 public class HTAccessHandler extends AbstractHttpHandler
50 {
51     private static Log log = LogFactory.getLog(HTAccessHandler.class);
52
53     String JavaDoc _default=null;
54     String JavaDoc _accessFile=".htaccess";
55
56     transient HashMap JavaDoc _htCache=new HashMap JavaDoc();
57
58     /* ------------------------------------------------------------ */
59     public void handle(String JavaDoc pathInContext,
60                        String JavaDoc pathParams,
61                        HttpRequest request,
62                        HttpResponse response)
63             throws HttpException,IOException JavaDoc
64     {
65         String JavaDoc user=null;
66         String JavaDoc password=null;
67         boolean IPValid=true;
68
69         if(log.isDebugEnabled())log.debug("HTAccessHandler pathInContext="+pathInContext);
70
71         String JavaDoc credentials=request.getField(HttpFields.__Authorization);
72
73         if (credentials!=null)
74         {
75             credentials=credentials.substring(credentials.indexOf(' ')+1);
76             credentials=B64Code.decode(credentials,StringUtil.__ISO_8859_1);
77             int i=credentials.indexOf(':');
78             user=credentials.substring(0,i);
79             password=credentials.substring(i+1);
80
81             if(log.isDebugEnabled())log.debug("User="+user+", password="+
82                     "******************************".substring(0,password.length()));
83         }
84
85         HTAccess ht=null;
86
87         try
88         {
89             Resource resource=null;
90             String JavaDoc directory=pathInContext.endsWith("/")
91                     ?pathInContext:
92                     URI.parentPath(pathInContext);
93
94             // Look for htAccess resource
95
while (directory!=null)
96             {
97                 String JavaDoc htPath=directory+_accessFile;
98                 resource=getHttpContext().getResource(htPath);
99                 if(log.isDebugEnabled())log.debug("directory="+directory+" resource="+resource);
100
101                 if (resource!=null && resource.exists() && !resource.isDirectory())
102                     break;
103                 resource=null;
104                 directory=URI.parentPath(directory);
105             }
106
107             // Try default directory
108
if (resource==null && _default!=null)
109             {
110                 resource=Resource.newResource(_default);
111                 if (!resource.exists() || resource.isDirectory())
112                     return;
113             }
114             if (resource==null)
115                 return;
116
117             if(log.isDebugEnabled())log.debug("HTACCESS="+resource);
118
119             ht=(HTAccess)_htCache.get(resource);
120             if (ht==null || ht.getLastModified()!=resource.lastModified())
121             {
122                 ht=new HTAccess(resource);
123                 _htCache.put(resource,ht);
124                 if(log.isDebugEnabled())log.debug("HTCache loaded "+ht);
125             }
126
127             // prevent access to htaccess files
128
if (pathInContext.endsWith(_accessFile))
129             {
130                 response.sendError(HttpResponse.__403_Forbidden);
131                 request.setHandled(true);
132                 return;
133             }
134
135             // See if there is a config problem
136
if (ht.isForbidden())
137             {
138                 log.warn("Mis-configured htaccess: "+ht);
139                 response.sendError(HttpResponse.__403_Forbidden);
140                 request.setHandled(true);
141                 return;
142             }
143
144             //first see if we need to handle based on method type
145
Map JavaDoc methods=ht.getMethods();
146             if (methods.size()>0 && !methods.containsKey(request.getMethod()))
147                 return; //Nothing to check
148

149             // Check the accesss
150
int satisfy=ht.getSatisfy();
151
152             // second check IP address
153
IPValid=ht.checkAccess("",request.getRemoteAddr());
154             if(log.isDebugEnabled())log.debug("IPValid = "+IPValid);
155
156             // If IP is correct and satify is ANY then access is allowed
157
if (IPValid==true && satisfy==HTAccess.ANY)
158                 return;
159
160             // If IP is NOT correct and satify is ALL then access is forbidden
161
if (IPValid==false && satisfy==HTAccess.ALL)
162             {
163                 response.sendError(HttpResponse.__403_Forbidden);
164                 request.setHandled(true);
165                 return;
166             }
167
168             // set required page
169
if (!ht.checkAuth(user,password,getHttpContext(),request))
170             {
171                 log.debug("Auth Failed");
172                 response.setField(HttpFields.__WwwAuthenticate,"basic realm="+ht.getName());
173                 response.sendError(HttpResponse.__401_Unauthorized);
174                 response.commit();
175                 request.setHandled(true);
176                 return;
177             }
178
179             // set user
180
if (user!=null)
181             {
182                 request.setAuthType(SecurityConstraint.__BASIC_AUTH);
183                 request.setAuthUser(user);
184             }
185
186         }
187         catch (Exception JavaDoc ex)
188         {
189             log.warn(LogSupport.EXCEPTION,ex);
190             if (ht!=null)
191             {
192                 response.sendError(HttpResponse.__500_Internal_Server_Error);
193                 request.setHandled(true);
194             }
195         }
196     }
197
198
199     /* ------------------------------------------------------------ */
200     /** set functions for the following .xml administration statements.
201      *
202      * <Call name="addHandler">
203      * <Arg>
204      * <New class="org.mortbay.http.handler.HTAccessHandler">
205      * <Set name="Default">./etc/htaccess</Set>
206      * <Set name="AccessFile">.htaccess</Set>
207      * </New>
208      * </Arg>
209      * </Call>
210      *
211      */

212     public void setDefault(String JavaDoc dir)
213     {
214         _default=dir;
215     }
216
217
218     /* ------------------------------------------------------------ */
219     public void setAccessFile(String JavaDoc anArg)
220     {
221         if (anArg==null)
222             _accessFile=".htaccess";
223         else
224             _accessFile=anArg;
225     }
226
227     /* ------------------------------------------------------------ */
228     /* ------------------------------------------------------------ */
229     /* ------------------------------------------------------------ */
230     private static class HTAccess
231     {
232         // private boolean _debug = false;
233
static final int ANY=0;
234         static final int ALL=1;
235         static final String JavaDoc USER="user";
236         static final String JavaDoc GROUP="group";
237         static final String JavaDoc VALID_USER="valid-user";
238
239         /* ------------------------------------------------------------ */
240         String JavaDoc _userFile;
241         Resource _userResource;
242         HashMap JavaDoc _users=null;
243         long _userModified;
244
245         /* ------------------------------------------------------------ */
246         String JavaDoc _groupFile;
247         Resource _groupResource;
248         HashMap JavaDoc _groups=null;
249         long _groupModified;
250
251         int _satisfy=0;
252         String JavaDoc _type;
253         String JavaDoc _name;
254         HashMap JavaDoc _methods=new HashMap JavaDoc();
255         HashSet JavaDoc _requireEntities=new HashSet JavaDoc();
256         String JavaDoc _requireName;
257         int _order;
258         ArrayList JavaDoc _allowList=new ArrayList JavaDoc();
259         ArrayList JavaDoc _denyList=new ArrayList JavaDoc();
260         long _lastModified;
261         boolean _forbidden=false;
262
263         /* ------------------------------------------------------------ */
264         public HTAccess(Resource resource)
265         {
266             BufferedReader JavaDoc htin=null;
267             try
268             {
269                 htin=new BufferedReader JavaDoc(new InputStreamReader JavaDoc(resource.getInputStream()));
270                 parse(htin);
271                 _lastModified=resource.lastModified();
272
273                 if (_userFile!=null)
274                 {
275                     _userResource=Resource.newResource(_userFile);
276                     if (!_userResource.exists())
277                     {
278                         _forbidden=true;
279                         log.warn("Could not find ht user file: "+_userFile);
280                     }
281                     else
282                         if(log.isDebugEnabled())log.debug("user file: "+_userResource);
283                 }
284
285                 if (_groupFile!=null)
286                 {
287                     _groupResource=Resource.newResource(_groupFile);
288                     if (!_groupResource.exists())
289                     {
290                         _forbidden=true;
291                         log.warn("Could not find ht group file: "+_groupResource);
292                     }
293                     else
294                         if(log.isDebugEnabled())log.debug("group file: "+_groupResource);
295                 }
296             }
297             catch (IOException JavaDoc e)
298             {
299                 _forbidden=true;
300                 log.warn(LogSupport.EXCEPTION,e);
301             }
302         }
303
304         /* ------------------------------------------------------------ */
305         public boolean isForbidden()
306         {
307             return _forbidden;
308         }
309
310         /* ------------------------------------------------------------ */
311         public HashMap JavaDoc getMethods()
312         {
313             return _methods;
314         }
315
316         /* ------------------------------------------------------------ */
317         public long getLastModified()
318         {
319             return _lastModified;
320         }
321
322         /* ------------------------------------------------------------ */
323         public Resource getUserResource()
324         {
325             return _userResource;
326         }
327
328         /* ------------------------------------------------------------ */
329         public Resource getGroupResource()
330         {
331             return _groupResource;
332         }
333
334         /* ------------------------------------------------------------ */
335         public int getSatisfy()
336         {
337             return (_satisfy);
338         }
339
340         /* ------------------------------------------------------------ */
341         public String JavaDoc getName()
342         {
343             return _name;
344         }
345
346         /* ------------------------------------------------------------ */
347         public String JavaDoc getType()
348         {
349             return _type;
350         }
351
352         /* ------------------------------------------------------------ */
353         public boolean checkAccess(String JavaDoc host,String JavaDoc ip)
354         {
355             String JavaDoc elm;
356             boolean alp=false;
357             boolean dep=false;
358
359             // if no allows and no deny defined, then return true
360
if (_allowList.size()==0 && _denyList.size()==0)
361                 return (true);
362
363             // looping for allows
364
for (int i=0;i<_allowList.size();i++)
365             {
366                 elm=(String JavaDoc)_allowList.get(i);
367                 if (elm.equals("all"))
368                 {
369                     alp=true;
370                     break;
371                 }
372                 else
373                 {
374                     char c=elm.charAt(0);
375                     if (c>='0' && c<='9')
376                     {
377                         // ip
378
if (ip.startsWith(elm))
379                         {
380                             alp=true;
381                             break;
382                         }
383                     }
384                     else
385                     {
386                         // hostname
387
if (host.endsWith(elm))
388                         {
389                             alp=true;
390                             break;
391                         }
392                     }
393                 }
394             }
395
396             // looping for denies
397
for (int i=0;i<_denyList.size();i++)
398             {
399                 elm=(String JavaDoc)_denyList.get(i);
400                 if (elm.equals("all"))
401                 {
402                     dep=true;
403                     break;
404                 }
405                 else
406                 {
407                     char c=elm.charAt(0);
408                     if (c>='0' && c<='9')
409                     { // ip
410
if (ip.startsWith(elm))
411                         {
412                             dep=true;
413                             break;
414                         }
415                     }
416                     else
417                     { // hostname
418
if (host.endsWith(elm))
419                         {
420                             dep=true;
421                             break;
422                         }
423                     }
424                 }
425             }
426
427             if (_order<0) //deny,allow
428
return !dep || alp;
429             //mutual failure == allow,deny
430
return alp && !dep;
431         }
432
433         /* ------------------------------------------------------------ */
434         public boolean checkAuth(String JavaDoc user,
435                                  String JavaDoc pass,
436                                  HttpContext context,
437                                  HttpRequest request)
438         {
439             if (_requireName==null)
440                 return true;
441
442             // Authenticate with realm
443
UserRealm realm=context.getRealm();
444             Principal JavaDoc principal=realm==null?null:realm.authenticate(user,pass,request);
445             if (principal==null)
446             {
447                 // Have to authenticate the user with the password file
448
String JavaDoc code=getUserCode(user);
449                 String JavaDoc salt=code!=null?code.substring(0,2):user;
450                 String JavaDoc cred=(user!=null&&pass!=null)?UnixCrypt.crypt(pass,salt):null;
451                 if (code==null || (code.equals("") && !pass.equals("")) || !code.equals(cred))
452                     return false;
453             }
454
455             if (_requireName.equalsIgnoreCase(USER))
456             {
457                 if (_requireEntities.contains(user))
458                     return true;
459             }
460             else if (_requireName.equalsIgnoreCase(GROUP))
461             {
462                 ArrayList JavaDoc gps=getUserGroups(user);
463                 if (gps!=null)
464                     for (int g=gps.size();g-->0;)
465                         if (_requireEntities.contains(gps.get(g)))
466                             return true;
467             }
468             else if (_requireName.equalsIgnoreCase(VALID_USER))
469             {
470                 return true;
471             }
472             
473             return false;
474         }
475
476         /* ------------------------------------------------------------ */
477         public boolean isAccessLimited()
478         {
479             if (_allowList.size()>0 || _denyList.size()>0)
480                 return true;
481             else
482                 return false;
483         }
484
485         /* ------------------------------------------------------------ */
486         public boolean isAuthLimited()
487         {
488             if (_requireName!=null)
489                 return true;
490             else
491                 return false;
492         }
493
494         /* ------------------------------------------------------------ */
495         private String JavaDoc getUserCode(String JavaDoc user)
496         {
497             if (_userResource==null)
498                 return null;
499
500             if (_users==null || _userModified!=_userResource.lastModified())
501             {
502                 if(log.isDebugEnabled())log.debug("LOAD "+_userResource);
503                 _users=new HashMap JavaDoc();
504                 BufferedReader JavaDoc ufin=null;
505                 try
506                 {
507                     ufin=new BufferedReader JavaDoc(new InputStreamReader JavaDoc(_userResource.getInputStream()));
508                     _userModified=_userResource.lastModified();
509                     String JavaDoc line;
510                     while ((line=ufin.readLine())!=null)
511                     {
512                         line=line.trim();
513                         if (line.startsWith("#")) continue;
514                         int spos=line.indexOf(':');
515                         if (spos<0)
516                             continue;
517                         String JavaDoc u=line.substring(0,spos).trim();
518                         String JavaDoc p=line.substring(spos+1).trim();
519                         _users.put(u,p);
520                     }
521                 }
522                 catch (IOException JavaDoc e)
523                 {
524                     log.warn(LogSupport.EXCEPTION,e);
525                 }
526                 finally
527                 {
528                     try
529                     {
530                         if (ufin!=null) ufin.close();
531                     }
532                     catch (IOException JavaDoc e2)
533                     {
534                         log.warn(LogSupport.EXCEPTION,e2);
535                     }
536                 }
537             }
538
539             return (String JavaDoc)_users.get(user);
540         }
541
542         /* ------------------------------------------------------------ */
543         private ArrayList JavaDoc getUserGroups(String JavaDoc group)
544         {
545             if (_groupResource==null)
546                 return null;
547
548             if (_groups==null || _groupModified!=_groupResource.lastModified())
549             {
550                 if(log.isDebugEnabled())log.debug("LOAD "+_groupResource);
551
552                 _groups=new HashMap JavaDoc();
553                 BufferedReader JavaDoc ufin=null;
554                 try
555                 {
556                     ufin=new BufferedReader JavaDoc(new InputStreamReader JavaDoc(_groupResource.getInputStream()));
557                     _groupModified=_groupResource.lastModified();
558                     String JavaDoc line;
559                     while ((line=ufin.readLine())!=null)
560                     {
561                         line=line.trim();
562                         if (line.startsWith("#") || line.length()==0) continue;
563
564                         StringTokenizer JavaDoc tok=new StringTokenizer JavaDoc(line,": \t");
565
566                         if (!tok.hasMoreTokens())
567                             continue;
568                         String JavaDoc g=tok.nextToken();
569                         if (!tok.hasMoreTokens())
570                             continue;
571                         while (tok.hasMoreTokens())
572                         {
573                             String JavaDoc u=tok.nextToken();
574                             ArrayList JavaDoc gl=(ArrayList JavaDoc)_groups.get(u);
575                             if (gl==null)
576                             {
577                                 gl=new ArrayList JavaDoc();
578                                 _groups.put(u,gl);
579                             }
580                             gl.add(g);
581                         }
582                     }
583                 }
584                 catch (IOException JavaDoc e)
585                 {
586                     log.warn(LogSupport.EXCEPTION,e);
587                 }
588                 finally
589                 {
590                     try
591                     {
592                         if (ufin!=null) ufin.close();
593                     }
594                     catch (IOException JavaDoc e2)
595                     {
596                         log.warn(LogSupport.EXCEPTION,e2);
597                     }
598                 }
599             }
600             
601             return (ArrayList JavaDoc)_groups.get(group);
602         }
603
604         /* ------------------------------------------------------------ */
605         public String JavaDoc toString()
606         {
607             StringBuffer JavaDoc buf=new StringBuffer JavaDoc();
608
609             buf.append("AuthUserFile=");
610             buf.append(_userFile);
611             buf.append(", AuthGroupFile=");
612             buf.append(_groupFile);
613             buf.append(", AuthName=");
614             buf.append(_name);
615             buf.append(", AuthType=");
616             buf.append(_type);
617             buf.append(", Methods=");
618             buf.append(_methods);
619             buf.append(", satisfy=");
620             buf.append(_satisfy);
621             if (_order<0)
622                 buf.append(", order=deny,allow");
623             else if (_order>0)
624                 buf.append(", order=allow,deny");
625             else
626                 buf.append(", order=mutual-failure");
627
628             buf.append(", Allow from=");
629             buf.append(_allowList);
630             buf.append(", deny from=");
631             buf.append(_denyList);
632             buf.append(", requireName=");
633             buf.append(_requireName);
634             buf.append(" ");
635             buf.append(_requireEntities);
636
637             return buf.toString();
638         }
639
640         /* ------------------------------------------------------------ */
641         private void parse(BufferedReader JavaDoc htin)
642                 throws IOException JavaDoc
643         {
644             String JavaDoc line;
645             while ((line=htin.readLine())!=null)
646             {
647                 line=line.trim();
648                 if (line.startsWith("#"))
649                     continue;
650                 else if (line.startsWith("AuthUserFile"))
651                 {
652                     _userFile=line.substring(13).trim();
653                 }
654                 else if (line.startsWith("AuthGroupFile"))
655                 {
656                     _groupFile=line.substring(14).trim();
657                 }
658                 else if (line.startsWith("AuthName"))
659                 {
660                     _name=line.substring(8).trim();
661                 }
662                 else if (line.startsWith("AuthType"))
663                 {
664                     _type=line.substring(8).trim();
665                 }
666                 //else if (line.startsWith("<Limit")) {
667
else if (line.startsWith("<Limit"))
668                 {
669                     int limit=line.length();
670                     int endp=line.indexOf('>');
671                     StringTokenizer JavaDoc tkns;
672
673                     if (endp<0) endp=limit;
674                     tkns=new StringTokenizer JavaDoc(line.substring(6,endp));
675                     while (tkns.hasMoreTokens())
676                     {
677                         _methods.put(tkns.nextToken(),Boolean.TRUE);
678                     }
679
680                     while ((line=htin.readLine())!=null)
681                     {
682                         line=line.trim();
683                         if (line.startsWith("#"))
684                             continue;
685                         else if (line.startsWith("satisfy"))
686                         {
687                             int pos1=7;
688                             limit=line.length();
689                             while ((pos1<limit) && (line.charAt(pos1)<=' ')) pos1++;
690                             int pos2=pos1;
691                             while ((pos2<limit) && (line.charAt(pos2)>' ')) pos2++;
692                             String JavaDoc l_string=line.substring(pos1,pos2);
693                             if (l_string.equals("all"))
694                                 _satisfy=1;
695                             else if (l_string.equals("any")) _satisfy=0;
696                         }
697                         else if (line.startsWith("require"))
698                         {
699                             int pos1=7;
700                             limit=line.length();
701                             while ((pos1<limit) && (line.charAt(pos1)<=' ')) pos1++;
702                             int pos2=pos1;
703                             while ((pos2<limit) && (line.charAt(pos2)>' ')) pos2++;
704                             _requireName=line.substring(pos1,pos2).toLowerCase();
705                             if (USER.equals(_requireName))
706                                 _requireName=USER;
707                             else if (GROUP.equals(_requireName))
708                                 _requireName=GROUP;
709                             else if (VALID_USER.equals(_requireName))
710                                 _requireName=VALID_USER;
711
712                             pos1=pos2+1;
713                             if (pos1<limit)
714                             {
715                                 while ((pos1<limit) && (line.charAt(pos1)<=' ')) pos1++;
716
717                                 tkns=new StringTokenizer JavaDoc(line.substring(pos1));
718                                 while (tkns.hasMoreTokens())
719                                 {
720                                     _requireEntities.add(tkns.nextToken());
721                                 }
722                             }
723
724                         }
725                         else if (line.startsWith("order"))
726                         {
727                             if(log.isDebugEnabled())log.debug("orderline="+line+"order="+_order);
728                             if (line.indexOf("allow,deny")>0)
729                             {
730                                 log.debug("==>allow+deny");
731                                 _order=1;
732                             }
733                             else if (line.indexOf("deny,allow")>0)
734                             {
735                                 log.debug("==>deny,allow");
736                                 _order=-1;
737                             }
738                             else if (line.indexOf("mutual-failure")>0)
739                             {
740                                 log.debug("==>mutual");
741                                 _order=0;
742                             }
743                             else
744                             {
745                             }
746                         }
747                         else if (line.startsWith("allow from"))
748                         {
749                             int pos1=10;
750                             limit=line.length();
751                             while ((pos1<limit) && (line.charAt(pos1)<=' ')) pos1++;
752                             if(log.isDebugEnabled())log.debug("allow process:"+line.substring(pos1));
753                             tkns=new StringTokenizer JavaDoc(line.substring(pos1));
754                             while (tkns.hasMoreTokens())
755                             {
756                                 _allowList.add(tkns.nextToken());
757                             }
758                         }
759                         else if (line.startsWith("deny from"))
760                         {
761                             int pos1=9;
762                             limit=line.length();
763                             while ((pos1<limit) && (line.charAt(pos1)<=' ')) pos1++;
764                             if(log.isDebugEnabled())log.debug("deny process:"+line.substring(pos1));
765
766                             tkns=new StringTokenizer JavaDoc(line.substring(pos1));
767                             while (tkns.hasMoreTokens())
768                             {
769                                 _denyList.add(tkns.nextToken());
770                             }
771                         }
772                         else if (line.startsWith("</Limit>")) break;
773                     }
774                 }
775             }
776         }
777     }
778 }
779
Popular Tags