1/* SOGoProxyAuthenticator.h - this file is part of SOGo
2 *
3 * Copyright (C) 2009-2013 Inverse inc.
4 *
5 * This file is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * This file is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; see the file COPYING.  If not, write to
17 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21#import <Foundation/NSArray.h>
22#import <Foundation/NSString.h>
23
24#import <NGObjWeb/NSException+HTTP.h>
25#import <NGObjWeb/WOContext.h>
26#import <NGObjWeb/WORequest.h>
27#import <NGObjWeb/WOResponse.h>
28
29#import <NGExtensions/NGBase64Coding.h>
30#import <NGExtensions/NSObject+Logs.h>
31
32#import "SOGoPermissions.h"
33#import "SOGoSystemDefaults.h"
34#import "SOGoUser.h"
35
36#import "SOGoProxyAuthenticator.h"
37
38@implementation SOGoProxyAuthenticator
39
40+ (id) sharedSOGoProxyAuthenticator
41{
42  static SOGoProxyAuthenticator *auth = nil;
43
44  if (!auth)
45    auth = [self new];
46
47  return auth;
48}
49
50- (BOOL) checkLogin: (NSString *) _login
51           password: (NSString *) _pwd
52{
53  return YES;
54}
55
56/* create SOGoUser */
57
58- (NSString *) checkCredentialsInContext: (WOContext *) context
59{
60  NSString *remoteUser;
61
62
63  /* If such a header is not provided by the proxy, SOPE will attempt to
64     deduce it from the "Authorization" header. */
65  remoteUser = [[context request] headerForKey: @"x-webobjects-remote-user"];
66
67  if ([remoteUser length] == 0 && [[SOGoSystemDefaults sharedSystemDefaults] trustProxyAuthentication])
68    {
69      remoteUser = @"anonymous";
70    }
71
72  return remoteUser;
73}
74
75- (WOResponse *) unauthorized: (NSString *) reason
76                    inContext: (WOContext *) context
77{
78  WOResponse *r;
79
80  if (![reason length])
81    reason = @"Unauthorized";
82
83  r = [context response];
84  [r setStatus: 403 /* unauthorized */];
85  [r setHeader: @"text/plain; charset=utf-8" forKey: @"content-type"];
86  [r appendContentString: reason];
87
88  return r;
89}
90
91- (SOGoUser *) userInContext: (WOContext *) context
92{
93  SOGoUser *user;
94  NSString *login;
95
96  login = [self checkCredentialsInContext: context];
97  if ([login length])
98    user = [SOGoUser userWithLogin: login
99                             roles: [NSArray arrayWithObject:
100                                               SoRole_Authenticated]];
101  else
102    user = nil;
103
104  return user;
105}
106
107#warning the DAV authenticator is pretty similar to this one. We should enable \
108  the use of the former for Basic auth type through some defaults.
109- (NSString *) passwordInContext: (WOContext *) context
110{
111  NSString *password, *authType, *authorization, *pair, *pairStart;
112  WORequest *rq;
113
114  password = @"";
115
116  rq = [context request];
117  authType = [rq headerForKey: @"x-webobjects-auth-type"];
118  if ([authType isEqualToString: @"Basic"])
119    {
120      authorization = [rq headerForKey: @"authorization"];
121      if ([authorization hasPrefix: @"Basic "])
122        {
123          pair = [[authorization substringFromIndex: 6]
124                   stringByDecodingBase64];
125          pairStart = [NSString stringWithFormat: @"%@:",
126                                [self checkCredentialsInContext: context]];
127          if ([pair hasPrefix: pairStart])
128            password = [pair substringFromIndex: [pairStart length]];
129          else
130            [self errorWithFormat: @"the username in the 'authorization'"
131                  @" header does not have the expected value"];
132        }
133      else
134        [self errorWithFormat:
135                @"'authorization' header does not have the expected value"];
136    }
137  else if (authType)
138    [self errorWithFormat: @"unrecognized authentication type: '%@'",
139          authType];
140  else
141    [self warnWithFormat: @"no authentication type found, skipped"];
142
143  return password;
144}
145
146- (NSString *) imapPasswordInContext: (WOContext *) context
147                              forURL: (NSURL *) server
148                          forceRenew: (BOOL) renew
149{
150  return (renew ? nil : [self passwordInContext: context]);
151}
152
153- (WOResponse *) preprocessCredentialsInContext: (WOContext *) context
154{
155  WOResponse *r;
156
157  if ([self userInContext: context])
158    {
159      [context setObject: [NSArray arrayWithObject: SoRole_Authenticated]
160                  forKey: @"SoAuthenticatedRoles"];
161      r = nil;
162    }
163  else
164    r = [self unauthorized: nil inContext: context];
165
166  return r;
167}
168
169- (BOOL) renderException: (NSException *) e
170               inContext: (WOContext *) context
171{
172  BOOL rc;
173
174  if ([e httpStatus] == 401)
175    {
176      [self unauthorized: [e reason] inContext: context];
177      rc = YES;
178    }
179  else
180    rc = NO;
181
182  return rc;
183}
184
185@end /* SOGoProxyAuthenticator */
186