1 10 package org.mortbay.http.handler; 11 12 import java.io.BufferedReader ; 13 import java.io.IOException ; 14 import java.io.InputStreamReader ; 15 import java.security.Principal ; 16 import java.util.ArrayList ; 17 import java.util.HashMap ; 18 import java.util.HashSet ; 19 import java.util.Map ; 20 import java.util.StringTokenizer ; 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 49 public class HTAccessHandler extends AbstractHttpHandler 50 { 51 private static Log log = LogFactory.getLog(HTAccessHandler.class); 52 53 String _default=null; 54 String _accessFile=".htaccess"; 55 56 transient HashMap _htCache=new HashMap (); 57 58 59 public void handle(String pathInContext, 60 String pathParams, 61 HttpRequest request, 62 HttpResponse response) 63 throws HttpException,IOException 64 { 65 String user=null; 66 String password=null; 67 boolean IPValid=true; 68 69 if(log.isDebugEnabled())log.debug("HTAccessHandler pathInContext="+pathInContext); 70 71 String 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 directory=pathInContext.endsWith("/") 91 ?pathInContext: 92 URI.parentPath(pathInContext); 93 94 while (directory!=null) 96 { 97 String 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 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 if (pathInContext.endsWith(_accessFile)) 129 { 130 response.sendError(HttpResponse.__403_Forbidden); 131 request.setHandled(true); 132 return; 133 } 134 135 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 Map methods=ht.getMethods(); 146 if (methods.size()>0 && !methods.containsKey(request.getMethod())) 147 return; 149 int satisfy=ht.getSatisfy(); 151 152 IPValid=ht.checkAccess("",request.getRemoteAddr()); 154 if(log.isDebugEnabled())log.debug("IPValid = "+IPValid); 155 156 if (IPValid==true && satisfy==HTAccess.ANY) 158 return; 159 160 if (IPValid==false && satisfy==HTAccess.ALL) 162 { 163 response.sendError(HttpResponse.__403_Forbidden); 164 request.setHandled(true); 165 return; 166 } 167 168 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 if (user!=null) 181 { 182 request.setAuthType(SecurityConstraint.__BASIC_AUTH); 183 request.setAuthUser(user); 184 } 185 186 } 187 catch (Exception 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 212 public void setDefault(String dir) 213 { 214 _default=dir; 215 } 216 217 218 219 public void setAccessFile(String 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 static final int ANY=0; 234 static final int ALL=1; 235 static final String USER="user"; 236 static final String GROUP="group"; 237 static final String VALID_USER="valid-user"; 238 239 240 String _userFile; 241 Resource _userResource; 242 HashMap _users=null; 243 long _userModified; 244 245 246 String _groupFile; 247 Resource _groupResource; 248 HashMap _groups=null; 249 long _groupModified; 250 251 int _satisfy=0; 252 String _type; 253 String _name; 254 HashMap _methods=new HashMap (); 255 HashSet _requireEntities=new HashSet (); 256 String _requireName; 257 int _order; 258 ArrayList _allowList=new ArrayList (); 259 ArrayList _denyList=new ArrayList (); 260 long _lastModified; 261 boolean _forbidden=false; 262 263 264 public HTAccess(Resource resource) 265 { 266 BufferedReader htin=null; 267 try 268 { 269 htin=new BufferedReader (new InputStreamReader (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 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 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 getName() 342 { 343 return _name; 344 } 345 346 347 public String getType() 348 { 349 return _type; 350 } 351 352 353 public boolean checkAccess(String host,String ip) 354 { 355 String elm; 356 boolean alp=false; 357 boolean dep=false; 358 359 if (_allowList.size()==0 && _denyList.size()==0) 361 return (true); 362 363 for (int i=0;i<_allowList.size();i++) 365 { 366 elm=(String )_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 if (ip.startsWith(elm)) 379 { 380 alp=true; 381 break; 382 } 383 } 384 else 385 { 386 if (host.endsWith(elm)) 388 { 389 alp=true; 390 break; 391 } 392 } 393 } 394 } 395 396 for (int i=0;i<_denyList.size();i++) 398 { 399 elm=(String )_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 { if (ip.startsWith(elm)) 411 { 412 dep=true; 413 break; 414 } 415 } 416 else 417 { if (host.endsWith(elm)) 419 { 420 dep=true; 421 break; 422 } 423 } 424 } 425 } 426 427 if (_order<0) return !dep || alp; 429 return alp && !dep; 431 } 432 433 434 public boolean checkAuth(String user, 435 String pass, 436 HttpContext context, 437 HttpRequest request) 438 { 439 if (_requireName==null) 440 return true; 441 442 UserRealm realm=context.getRealm(); 444 Principal principal=realm==null?null:realm.authenticate(user,pass,request); 445 if (principal==null) 446 { 447 String code=getUserCode(user); 449 String salt=code!=null?code.substring(0,2):user; 450 String 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 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 getUserCode(String 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 (); 504 BufferedReader ufin=null; 505 try 506 { 507 ufin=new BufferedReader (new InputStreamReader (_userResource.getInputStream())); 508 _userModified=_userResource.lastModified(); 509 String 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 u=line.substring(0,spos).trim(); 518 String p=line.substring(spos+1).trim(); 519 _users.put(u,p); 520 } 521 } 522 catch (IOException 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 e2) 533 { 534 log.warn(LogSupport.EXCEPTION,e2); 535 } 536 } 537 } 538 539 return (String )_users.get(user); 540 } 541 542 543 private ArrayList getUserGroups(String 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 (); 553 BufferedReader ufin=null; 554 try 555 { 556 ufin=new BufferedReader (new InputStreamReader (_groupResource.getInputStream())); 557 _groupModified=_groupResource.lastModified(); 558 String line; 559 while ((line=ufin.readLine())!=null) 560 { 561 line=line.trim(); 562 if (line.startsWith("#") || line.length()==0) continue; 563 564 StringTokenizer tok=new StringTokenizer (line,": \t"); 565 566 if (!tok.hasMoreTokens()) 567 continue; 568 String g=tok.nextToken(); 569 if (!tok.hasMoreTokens()) 570 continue; 571 while (tok.hasMoreTokens()) 572 { 573 String u=tok.nextToken(); 574 ArrayList gl=(ArrayList )_groups.get(u); 575 if (gl==null) 576 { 577 gl=new ArrayList (); 578 _groups.put(u,gl); 579 } 580 gl.add(g); 581 } 582 } 583 } 584 catch (IOException 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 e2) 595 { 596 log.warn(LogSupport.EXCEPTION,e2); 597 } 598 } 599 } 600 601 return (ArrayList )_groups.get(group); 602 } 603 604 605 public String toString() 606 { 607 StringBuffer buf=new StringBuffer (); 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 htin) 642 throws IOException 643 { 644 String 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")) 668 { 669 int limit=line.length(); 670 int endp=line.indexOf('>'); 671 StringTokenizer tkns; 672 673 if (endp<0) endp=limit; 674 tkns=new StringTokenizer (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 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 (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 (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 (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 |