2 Copyright (C) 2002-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "SoHTTPAuthenticator.h"
25 #include "SoPermissions.h"
26 #include "NSException+HTTP.h"
27 #include <NGObjWeb/WOApplication.h>
28 #include <NGObjWeb/WORequest.h>
29 #include <NGObjWeb/WOResponse.h>
30 #include <NGObjWeb/WOContext.h>
31 #include <NGExtensions/NSString+Ext.h>
34 #if APPLE_RUNTIME || NeXT_RUNTIME
35 @interface NSObject(Miss)
36 - (void)subclassResponsibility:(SEL)cmd;
40 @implementation SoHTTPAuthenticator
46 /* HTTP basic authentication */
48 - (NSString *)authRealm {
49 return [(WOApplication *)[WOApplication application] name];
54 - (BOOL)checkLogin:(NSString *)_login password:(NSString *)_pwd {
55 [self subclassResponsibility:_cmd];
59 + (NSArray *)parseCredentials:(NSString *)_creds {
61 NSString *login, *pwd;
64 if ([_creds length] == 0) {
65 static NSArray *anon = nil;
67 anon = [[NSArray alloc] initWithObjects:@"anonymous", @"", nil];
70 if ([_creds length] < 6) {
71 [self logWithFormat:@"cannot handle authentication token: %@", _creds];
75 k = [[_creds substringToIndex:5] lowercaseString];
76 if (![k hasPrefix:@"basic"]) {
77 [self logWithFormat:@"tried unknown authentication method: %@", _creds];
81 k = [_creds substringFromIndex:6];
82 k = [k stringByDecodingBase64];
83 if (k == nil) return nil;
85 rng = [k rangeOfString:@":"];
86 if (rng.length <= 0) {
87 [self logWithFormat:@"got malformed basic credentials!"];
90 login = [k substringToIndex:rng.location];
91 pwd = [k substringFromIndex:(rng.location + rng.length)];
93 rng = [login rangeOfString:@"\\"];
95 [self debugWithFormat:@"splitting of domain in login: '%@'", login];
96 login = [login substringFromIndex:(rng.location + rng.length)];
98 return [NSArray arrayWithObjects:login, pwd, nil];
100 - (NSArray *)parseCredentials:(NSString *)_creds {
101 return [[self class] parseCredentials:_creds];
104 - (NSString *)checkCredentials:(NSString *)_creds {
105 /* checks credentials, returnes login if successful */
106 NSString *login, *pwd;
109 if ((creds = [self parseCredentials:_creds]) == nil)
112 login = [creds objectAtIndex:0];
113 if ([login isEqualToString:@"anonymous"])
116 pwd = [creds objectAtIndex:1];
117 if (![self checkLogin:login password:pwd])
123 - (NSString *)checkCredentialsInContext:(WOContext *)_ctx {
128 if ((auth = [rq headerForKey:@"authorization"]) == nil) {
129 /* no auth supplied */
132 return [self checkCredentials:auth];
135 - (NSArray *)rolesForLogin:(NSString *)_login {
136 NSArray *uroles = nil;
138 // could add manager of login=root
140 uroles = [NSArray arrayWithObjects:
141 SoRole_Authenticated,
147 - (SoUser *)userInContext:(WOContext *)_ctx {
148 static SoUser *anonymous = nil;
152 if (anonymous == nil) {
153 NSArray *ar = [NSArray arrayWithObject:SoRole_Anonymous];
154 anonymous = [[SoUser alloc] initWithLogin:@"anonymous" roles:ar];
157 if ((login = [self checkCredentialsInContext:_ctx]) == nil)
158 /* some error (otherwise result would have been anonymous */
161 if ([login isEqualToString:@"anonymous"])
164 uroles = [self rolesForLogin:login];
165 return [[[SoUser alloc] initWithLogin:login roles:uroles] autorelease];
168 - (WOResponse *)preprocessCredentialsInContext:(WOContext *)_ctx {
172 NSString *user, *pwd;
175 if ((auth = [[_ctx request] headerForKey:@"authorization"]) == nil) {
176 /* no authentication provided */
177 static NSArray *anon = nil;
179 anon = [[NSArray alloc] initWithObjects:SoRole_Anonymous, nil];
181 [_ctx setObject:anon forKey:@"SoAuthenticatedRoles"];
185 /* authentication provided, check whether it's valid */
188 if ([auth length] < 6) {
189 [self logWithFormat:@"tried unknown authentication method: %@ (A)", auth];
190 [r setStatus:400 /* bad request */];
191 [r appendContentString:@"tried unsupported authentication"];
194 k = [[auth substringToIndex:5] lowercaseString];
195 if (![k hasPrefix:@"basic"]) {
196 [self logWithFormat:@"tried unknown authentication method: %@ (B)", auth];
197 [r setStatus:400 /* bad request */];
198 [r appendContentString:@"tried unsupported authentication"];
202 k = [auth substringFromIndex:6];
203 if ((k = [k stringByDecodingBase64]) == nil) {
204 [self logWithFormat:@"tried unknown authentication method: %@ (C)", auth];
205 [r setStatus:400 /* bad request */];
206 [r appendContentString:@"could not decode base64 credentials"];
210 rng = [k rangeOfString:@":"];
211 if (rng.length <= 0) {
212 [self logWithFormat:@"got malformed basic credentials!"];
213 [r setStatus:400 /* bad request */];
214 [r appendContentString:@"did not find colon separator in credentials"];
218 user = [k substringToIndex:rng.location];
219 pwd = [k substringFromIndex:(rng.location + rng.length)];
221 rng = [user rangeOfString:@"\\"];
222 if (rng.length > 0) {
223 [self debugWithFormat:@"splitting of domain in user: '%@'", user];
224 user = [user substringFromIndex:(rng.location + rng.length)];
227 if ([user length] == 0) {
228 [self logWithFormat:@"got malformed basic credentials!"];
229 [r setStatus:400 /* bad request */];
230 [r appendContentString:@"invalid login in credentials"];
233 if ([pwd length] == 0) {
234 [self logWithFormat:@"got empty password for user '%@'!", user];
236 auth = [NSString stringWithFormat:@"basic realm=\"%@\"",[self authRealm]];
237 [r setStatus:401 /* unauthorized */];
238 [r setHeader:auth forKey:@"www-authenticate"];
239 [r appendContentString:@"empty password in credentials"];
243 /* authenticate valid credentials */
245 if (![self checkLogin:user password:pwd]) {
246 [self logWithFormat:@"tried wrong password for user '%@'!", user];
247 auth = [NSString stringWithFormat:@"basic realm=\"%@\"",[self authRealm]];
248 [r setStatus:401 /* unauthorized */];
249 [r setHeader:auth forKey:@"www-authenticate"];
253 //[self debugWithFormat:@"authenticated user '%@'", user];
255 /* authentication succeeded */
257 static NSArray *auth = nil;
259 auth = [[NSArray alloc] initWithObjects:
260 SoRole_Authenticated, SoRole_Anonymous, nil];
262 [_ctx setObject:auth forKey:@"SoAuthenticatedRoles"];
267 /* render auth exceptions */
269 - (BOOL)renderException:(NSException *)_e inContext:(WOContext *)_ctx {
270 if ([_e httpStatus] == 401) {
275 auth = [NSString stringWithFormat:@"basic realm=\"%@\"", [self authRealm]];
276 [r setStatus:[_e httpStatus] /* unauthorized */];
277 [r setHeader:auth forKey:@"www-authenticate"];
283 @end /* SoHTTPAuthenticator */