1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.security.impl.ntlm;
18
19 import java.security.Principal;
20 import java.util.HashSet;
21 import java.util.Set;
22
23 import javax.security.auth.Subject;
24
25 import org.apache.commons.lang.StringUtils;
26 import org.apache.jetspeed.administration.PortalAuthenticationConfiguration;
27 import org.apache.jetspeed.pipeline.PipelineException;
28 import org.apache.jetspeed.request.RequestContext;
29 import org.apache.jetspeed.security.SecurityException;
30 import org.apache.jetspeed.security.SecurityHelper;
31 import org.apache.jetspeed.security.User;
32 import org.apache.jetspeed.security.UserManager;
33 import org.apache.jetspeed.security.UserPrincipal;
34 import org.apache.jetspeed.security.impl.AbstractSecurityValve;
35 import org.apache.jetspeed.security.impl.UserPrincipalImpl;
36 import org.apache.jetspeed.statistics.PortalStatistics;
37 /***
38 * NTLMSecurityValve provides Subject creation based on the
39 * NTLM provided request.getRemoteUser() user name. When request.getRemoteUser() holds
40 * a valid value, then this user is authorized. Otherwise the username is retrieved
41 * from the Principal name in the request. In this way you can use NTLM authentication, with
42 * a fallback authentication method in case the user is not properly authenticated / authorized using
43 * NTLM.
44 *
45 * There are basically three authentication scenarios:
46 * <ol>
47 * <li>
48 * <p><b>The user is successfully authenticated and authorized by Ntml authentication</b></p>
49 * <p>A Subject is created, with Principal derived from the remoteUser value from Ntlm authentication</p>
50 * </li>
51 * <li>
52 * <p><b>The user is not authenticated by Ntlm, or the authenticated (can be NTLM or any other method) user cannot be authorized by Jetspeed.</b></p>
53 * <p>An anonymous Subject is created. The user can then be redirected to a login page for example.</p>
54 * </li>
55 * <li>
56 * <p><b>The user is authenticated by a (non-NTLM) authentication method, e.g. container-based form authentication.</b></p>
57 * <p>
58 * A subject is created based on the Principal name in the request.
59 * </p>
60 * </li>
61 * </ol>
62 * @author <a href="mailto:taylor@apache.org">David Sean Taylor </a>
63 * @author <a href="mailto:rwatler@finali.com">Randy Walter </a>
64 * @author <a href="mailto:weaver@apache.org">Scott T. Weaver</a>
65 * @author <a href="mailto:d.dam@hippo.nl">Dennis Dam</a>
66 * @version $Id$
67 */
68 public class NtlmSecurityValve extends AbstractSecurityValve
69 {
70 private UserManager userMgr;
71 private PortalStatistics statistics;
72 private String networkDomain;
73 private boolean ntlmAuthRequired;
74 private boolean omitDomain;
75
76
77 /***
78 * @param userMgr A UserManager
79 * @param statistics Portal Statistics
80 * @param networkDomain The network domain is used in combination with the <code>omitDomain</code> flag.
81 * @param omitDomain If <code>true</code>, then the network domain is stripped from the remoteUser name.
82 * @param ntlmAuthRequired if <code>true</code>, then an exception is thrown when there is no valid remoteUser,
83 * or the remoteUser cannot be authorized.
84 *
85 */
86 public NtlmSecurityValve(UserManager userMgr, String networkDomain, boolean omitDomain, boolean ntlmAuthRequired,
87 PortalStatistics statistics, PortalAuthenticationConfiguration authenticationConfiguration)
88 {
89 this.userMgr = userMgr;
90 this.statistics = statistics;
91 this.networkDomain = networkDomain;
92 this.ntlmAuthRequired = ntlmAuthRequired;
93 this.omitDomain = omitDomain;
94 this.authenticationConfiguration = authenticationConfiguration;
95 }
96
97 public NtlmSecurityValve(UserManager userMgr, String networkDomain, boolean omitDomain, boolean ntlmAuthRequired, PortalStatistics statistics)
98 {
99 this(userMgr, networkDomain, omitDomain, ntlmAuthRequired, statistics, null);
100 }
101
102 public NtlmSecurityValve(UserManager userMgr, String networkDomain, boolean omitDomain, boolean ntlmAuthRequired)
103 {
104 this(userMgr, networkDomain, omitDomain, ntlmAuthRequired, null);
105 }
106
107 public String toString()
108 {
109 return "NtlmSecurityValve";
110 }
111
112 protected Principal getUserPrincipal(RequestContext context) throws Exception
113 {
114 Subject subject = getSubjectFromSession(context);
115 if (subject != null)
116 {
117 return SecurityHelper.getPrincipal(subject, UserPrincipal.class);
118 }
119
120 return new UserPrincipalImpl(userMgr.getAnonymousUser());
121 }
122
123 protected Subject getSubject(RequestContext context) throws Exception
124 {
125 Subject subject = getSubjectFromSession(context);
126
127 String userName = context.getRequest().getRemoteUser();
128 if ( userName == null )
129 {
130 if (ntlmAuthRequired){
131 throw new PipelineException("Authorization failed.");
132 } else if (context.getRequest().getUserPrincipal() != null){
133 userName = context.getRequest().getUserPrincipal().getName();
134 }
135 } else {
136 if (omitDomain && networkDomain != null){
137 userName = StringUtils.stripStart( userName , networkDomain+"//");
138 }
139 }
140
141
142 if (subject != null)
143 {
144 Principal subjectUserPrincipal = SecurityHelper.getPrincipal(subject, UserPrincipal.class);
145 if ((subjectUserPrincipal == null) || !subjectUserPrincipal.getName().equals(userName))
146 {
147 subject = null;
148 }
149 }
150 if ( subject == null ){
151 if (userName != null){
152 try
153 {
154 User user = userMgr.getUser(userName);
155 if ( user != null )
156 {
157 subject = user.getSubject();
158 }
159 } catch (SecurityException sex)
160 {
161 subject = null;
162 }
163
164 if (subject == null && this.ntlmAuthRequired){
165 throw new PipelineException("Authorization failed for user '"+userName+"'.");
166 }
167 }
168 if (subject == null){
169
170 Principal userPrincipal = getUserPrincipal(context);
171 Set principals = new HashSet();
172 principals.add(userPrincipal);
173 subject = new Subject(true, principals, new HashSet(), new HashSet());
174 }
175
176
177 if (statistics != null)
178 {
179 statistics.logUserLogin(context, 0);
180 }
181
182 context.setSessionAttribute(IP_ADDRESS, context.getRequest().getRemoteAddr());
183 }
184
185 return subject;
186 }
187 }