]> err.no Git - sope/blob - sope-appserver/NGObjWeb/SoObjects/SoHTTPAuthenticator.m
fixed OGo bug #888
[sope] / sope-appserver / NGObjWeb / SoObjects / SoHTTPAuthenticator.m
1 /*
2   Copyright (C) 2002-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include "SoHTTPAuthenticator.h"
24 #include "SoUser.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>
32 #include "common.h"
33
34 #if APPLE_RUNTIME || NeXT_RUNTIME
35 @interface NSObject(Miss)
36 - (void)subclassResponsibility:(SEL)cmd;
37 @end
38 #endif
39
40 @implementation SoHTTPAuthenticator
41
42 + (int)version {
43   return 1;
44 }
45
46 /* HTTP basic authentication */
47
48 - (NSString *)authRealm {
49   return [(WOApplication *)[WOApplication application] name];
50 }
51
52 /* check for roles */
53
54 - (BOOL)checkLogin:(NSString *)_login password:(NSString *)_pwd {
55   [self subclassResponsibility:_cmd];
56   return NO;
57 }
58
59 + (NSArray *)parseCredentials:(NSString *)_creds {
60   NSRange  rng;
61   NSString *login, *pwd;
62   NSString *k;
63   
64   if ([_creds length] == 0) {
65     static NSArray *anon = nil;
66     if (anon == nil)
67       anon = [[NSArray alloc] initWithObjects:@"anonymous", @"", nil];
68     return anon;
69   }
70   if ([_creds length] < 6) {
71     [self logWithFormat:@"cannot handle authentication token: %@", _creds];
72     return nil;
73   }
74   
75   k = [[_creds substringToIndex:5] lowercaseString];
76   if (![k hasPrefix:@"basic"]) {
77     [self logWithFormat:@"tried unknown authentication method: %@", _creds];
78     return nil;
79   }
80   
81   k = [_creds substringFromIndex:6];
82   k = [k stringByDecodingBase64];
83   if (k == nil) return nil;
84
85   rng = [k rangeOfString:@":"];
86   if (rng.length <= 0) {
87     [self logWithFormat:@"got malformed basic credentials!"];
88     return nil;
89   }
90   login = [k substringToIndex:rng.location];
91   pwd   = [k substringFromIndex:(rng.location + rng.length)];
92   
93   rng = [login rangeOfString:@"\\"];
94   if (rng.length > 0) {
95     [self debugWithFormat:@"splitting of domain in login: '%@'", login];
96     login = [login substringFromIndex:(rng.location + rng.length)];
97   }
98   return [NSArray arrayWithObjects:login, pwd, nil];
99 }
100 - (NSArray *)parseCredentials:(NSString *)_creds {
101   return [[self class] parseCredentials:_creds];
102 }
103
104 - (NSString *)checkCredentials:(NSString *)_creds {
105   /* checks credentials, returnes login if successful */
106   NSString *login, *pwd;
107   NSArray  *creds;
108   
109   if ((creds = [self parseCredentials:_creds]) == nil)
110     return nil;
111
112   login = [creds objectAtIndex:0];
113   if ([login isEqualToString:@"anonymous"])
114     return @"anonymous";
115   
116   pwd = [creds objectAtIndex:1];
117   if (![self checkLogin:login password:pwd])
118     return nil;
119   
120   return login;
121 }
122
123 - (NSString *)checkCredentialsInContext:(WOContext *)_ctx {
124   WORequest *rq;
125   NSString  *auth;
126   
127   rq = [_ctx request];
128   if ((auth = [rq headerForKey:@"authorization"]) == nil) {
129     /* no auth supplied */
130     return @"anonymous";
131   }
132   return [self checkCredentials:auth];
133 }
134
135 - (NSArray *)rolesForLogin:(NSString *)_login {
136   NSArray *uroles = nil;
137   
138   // could add manager of login=root
139   
140   uroles = [NSArray arrayWithObjects:
141                       SoRole_Authenticated,
142                       SoRole_Anonymous,
143                       nil];
144   return uroles;
145 }
146
147 - (SoUser *)userInContext:(WOContext *)_ctx {
148   static SoUser *anonymous = nil;
149   NSString  *login;
150   NSArray   *uroles;
151   
152   if (anonymous == nil) {
153     NSArray *ar = [NSArray arrayWithObject:SoRole_Anonymous];
154     anonymous = [[SoUser alloc] initWithLogin:@"anonymous" roles:ar];
155   }
156   
157   if ((login = [self checkCredentialsInContext:_ctx]) == nil)
158     /* some error (otherwise result would have been anonymous */
159     return nil;
160   
161   if ([login isEqualToString:@"anonymous"])
162     return anonymous;
163   
164   uroles = [self rolesForLogin:login];
165   return [[[SoUser alloc] initWithLogin:login roles:uroles] autorelease];
166 }
167
168 - (WOResponse *)preprocessCredentialsInContext:(WOContext *)_ctx {
169   WOResponse *r;
170   NSString *auth;
171   NSString *k;
172   NSString *user, *pwd;
173   NSRange rng;
174
175   if ((auth = [[_ctx request] headerForKey:@"authorization"]) == nil) {
176     /* no authentication provided */
177     static NSArray *anon = nil;
178     if (anon == nil)
179       anon = [[NSArray alloc] initWithObjects:SoRole_Anonymous, nil];
180     
181     [_ctx setObject:anon forKey:@"SoAuthenticatedRoles"];
182     return nil;
183   }
184   
185   /* authentication provided, check whether it's valid */
186   
187   r = [_ctx response];
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"];
192     return r;
193   }
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"];
199     return r;
200   }
201   
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"];
207     return r;
208   }
209
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"];
215     return r;
216   }
217
218   user = [k substringToIndex:rng.location];
219   pwd  = [k substringFromIndex:(rng.location + rng.length)];
220
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)];
225   }
226   
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"];
231     return r;
232   }
233   if ([pwd length] == 0) {
234     [self logWithFormat:@"got empty password for user '%@'!", user];
235     
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"];
240     return r;
241   }
242   
243   /* authenticate valid credentials */
244
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"];
250     return r;
251   }
252   
253   //[self debugWithFormat:@"authenticated user '%@'", user];
254   
255   /* authentication succeeded */
256   {
257     static NSArray *auth = nil;
258     if (auth == nil) {
259       auth = [[NSArray alloc] initWithObjects:
260                                 SoRole_Authenticated, SoRole_Anonymous, nil];
261     }
262     [_ctx setObject:auth forKey:@"SoAuthenticatedRoles"];
263   }
264   return nil;
265 }
266
267 /* render auth exceptions */
268
269 - (BOOL)renderException:(NSException *)_e inContext:(WOContext *)_ctx {
270   if ([_e httpStatus] == 401) {
271     WOResponse *r;
272     NSString   *auth;
273     
274     r = [_ctx response];
275     auth = [NSString stringWithFormat:@"basic realm=\"%@\"", [self authRealm]];
276     [r setStatus:[_e httpStatus] /* unauthorized */];
277     [r setHeader:auth forKey:@"www-authenticate"];
278     return YES;
279   }
280   return NO;
281 }
282
283 @end /* SoHTTPAuthenticator */