line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CAS; |
2
|
|
|
|
|
|
|
|
3
|
5
|
|
|
5
|
|
122409
|
use strict; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
595
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
CAS - Central Authorization Server |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 VERSION |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
Version 0.89 |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=cut |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our $VERSION = '0.89'; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 SYNOPSIS |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
CAS is intended to provide cross project (client) and cross platform |
20
|
|
|
|
|
|
|
authentication and authorization services. CAS allows a user to have a single |
21
|
|
|
|
|
|
|
username and password, which can be granted access to 0 or more different |
22
|
|
|
|
|
|
|
clients. Even fine grained access controls can be granted differently |
23
|
|
|
|
|
|
|
for any and all of the different clients that use CAS. The central object to |
24
|
|
|
|
|
|
|
CAS is client based, and can be used to manage multiple users. |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
use CAS; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
my $client = CAS->new({CLIENT_ID => $id}); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
or |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
my $client = new CAS({CLIENT_NAME => 'Project Foo'}); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my $session = $client->authenticate({USERNAME => 'foo', |
35
|
|
|
|
|
|
|
PASSWORD => 'foobar'}); |
36
|
|
|
|
|
|
|
my $can_do = $client->authorize({USER => $session, |
37
|
|
|
|
|
|
|
RESOURCE => 'resource1', MASK => 'create'}); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
Code problems and mis-configurations should cause the call to die. Otherwise |
40
|
|
|
|
|
|
|
methods return undef on failure. Processing statements are stored in the |
41
|
|
|
|
|
|
|
calling objects message stack, which is reset with every method call. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
unless (defined $session) { die($client->messages) } |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head1 DESCRIPTION |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
CAS provides a set of tools for accessing a central user database, allowing |
48
|
|
|
|
|
|
|
a single username and password to be used by multiple applications & sites |
49
|
|
|
|
|
|
|
(clients). Permissions can be granted however finely or loosely the developer |
50
|
|
|
|
|
|
|
finds useful. The system also stores some very basic session information, |
51
|
|
|
|
|
|
|
providing some very minimal usage auditing. A separate distribution, |
52
|
|
|
|
|
|
|
CAS-Apache2, provides a mod_perl 2 application for protecting web sites from |
53
|
|
|
|
|
|
|
CAS. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head2 USAGE OVERVIEW |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
You first must create a CAS client object. Clients are defined in the database |
59
|
|
|
|
|
|
|
in advance by the CAS administrator. You will need to know the client ID, name |
60
|
|
|
|
|
|
|
or domain, all of which need to be unique to each client. |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
Examples: |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
my $client = CAS->new({CLIENT_ID => 2}); |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
or |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
my $client = CAS->new({CLIENT_NAME => 'Project Foo'}); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
You can fetch information about the client from this object if needed. But its |
71
|
|
|
|
|
|
|
main purpose is to authenticate users and check their authorizations. As the |
72
|
|
|
|
|
|
|
users can be granted access to any client, the specific client used to create |
73
|
|
|
|
|
|
|
this object doesn't matter if you just want to authenticate the user. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
my $session = $client->authenticate({}); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
The session token is a unique identifier for the particular session. It can be |
78
|
|
|
|
|
|
|
returned to the application as a key for session tracking, allowing for |
79
|
|
|
|
|
|
|
persistent login sessions and such. It is also used to identify the user when |
80
|
|
|
|
|
|
|
checking authorization. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
my $is_authorized = $client->authorize({SESSION => $session, |
83
|
|
|
|
|
|
|
RESOURCE => $request, MASK => 8}); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
The session token can also be used to fetch a user object, which remembers the |
87
|
|
|
|
|
|
|
client under which it was created. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
my $user = $client->user($session); |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
This user object, L, can be used to get information about the |
92
|
|
|
|
|
|
|
user. Security of the session token and its use is left to the discretion of |
93
|
|
|
|
|
|
|
the caller. |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=head2 CLIENT OBJECT ATTRIBUTES |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=over 4 |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=item user_info_fields |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
Returns a hash reference containing the field names in the UserInfo table. |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=item supl_user_info_fields |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
Returns a hash reference containing the field names in |
106
|
|
|
|
|
|
|
the clients supplemental_user_table, if defined. |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=item supplemental_user_table |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
The name of the clients supplemental user table. |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=item admin_email |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
The email address for the user designated as the administrator of the client. |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=item debug |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
The debug level for the client object. The default level is determined by the |
119
|
|
|
|
|
|
|
CAS configuration file. This is the only CAS client object attribute which can |
120
|
|
|
|
|
|
|
be set. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
$client->debug(2); |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
=item id |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
The ID of the client. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=item name |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
The name of the client. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=item default_group |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
The default group assigned to new users registering through the client. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item domain |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
The domain of the client. This can be used to allow a local interface to |
139
|
|
|
|
|
|
|
determine what client to assign based on the IP or such of a remote connection. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item base_path |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
The base path for this clients application(s) or work space. Primarilly used |
144
|
|
|
|
|
|
|
for websites where the project area defined for the client is a subsection of |
145
|
|
|
|
|
|
|
a website. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=item description |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
A description of the client. |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=item cookie_name |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
Primarilly used by CAS-Apache2 for determining the name of the cookie in whcih |
154
|
|
|
|
|
|
|
to store or fetch the session token. |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=item timeout |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
The period of incativitiy after which a user is forced to re-authenticate. |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=back |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head2 MESSAGING |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
All methods produce some internal messages while processing. When a method is |
165
|
|
|
|
|
|
|
first invoked on a CAS object, any old messages are cleared out and its initial |
166
|
|
|
|
|
|
|
result code is set to ERROR (so that if anything unexpected happens it has the |
167
|
|
|
|
|
|
|
result we would want - ERROR). |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
There are a wide variety of possible result codes that a method could use. |
170
|
|
|
|
|
|
|
L The specific ones that a method might set are described in |
171
|
|
|
|
|
|
|
the methods specific documentation. However there are three that are the most |
172
|
|
|
|
|
|
|
common, ERROR, BAD_REQUEST and OK which we will use in the following examples. |
173
|
|
|
|
|
|
|
The status is set to ERROR both when a method first starts and on non-fatal but |
174
|
|
|
|
|
|
|
still critical problems. BAD_REQUEST is generally set when a method call was |
175
|
|
|
|
|
|
|
properly constructed, but required parameters were missing or in an invalid |
176
|
|
|
|
|
|
|
format. OK is usually the status set after it has completed its job |
177
|
|
|
|
|
|
|
sucsesfully, just before returning. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=head3 Messaging methods |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=over 4 |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=item response_is |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
Used to check the status set by the last method called on the object: |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
$client->response_is('STATUS_NAME'); |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=item response_code |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
Returns the status set by the last method called on the object (as text): |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
my $status = $client->response_code; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=item messages |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
Returns all the messages generated by the last method called on the object. If |
198
|
|
|
|
|
|
|
called in list context returns a list of the messages. If called in scalar |
199
|
|
|
|
|
|
|
context returns a string, starting with the class name of the object, followed |
200
|
|
|
|
|
|
|
by all the messages generated joined on "; ". |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
my $messages = $client->messages; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=back |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
Be sure to see L for more details. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=head3 Example |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
Calling authentication with the USERNAME missing: |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
%args = get_user_credentials(); |
213
|
|
|
|
|
|
|
my $session = $client->authenticate(\%args); |
214
|
|
|
|
|
|
|
unless (defined $session) { |
215
|
|
|
|
|
|
|
if ($client->response_is('BAD_REQUEST')) { |
216
|
|
|
|
|
|
|
warn "Can't authenticate - missing required arguments: " |
217
|
|
|
|
|
|
|
. $client->messages; |
218
|
|
|
|
|
|
|
# try get_user_credentials again? |
219
|
|
|
|
|
|
|
} # if bad request |
220
|
|
|
|
|
|
|
else { |
221
|
|
|
|
|
|
|
my $status = $client->response_code; |
222
|
|
|
|
|
|
|
die "Problem with authentication - Status: $status, Messages: " . |
223
|
|
|
|
|
|
|
. $client->messages; |
224
|
|
|
|
|
|
|
} # something else went wrong? |
225
|
|
|
|
|
|
|
} # unless session token returned |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head2 FUTURE PLANS |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Here is the BIG wish list for CAS. For more humble feature requests, see |
231
|
|
|
|
|
|
|
L |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=over 4 |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=item XML/YAML/SOAP/JSON |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
I'd like to have optional handlers for accepting and replying to requests |
239
|
|
|
|
|
|
|
through one or more data exchange formats. Most likely I'll do this not through |
240
|
|
|
|
|
|
|
this core distribution, but through special mod_perl handlers under the |
241
|
|
|
|
|
|
|
L distribution. This will be the way through which not only |
242
|
|
|
|
|
|
|
remote applications access CAS from a different system (other than browsers |
243
|
|
|
|
|
|
|
accessing local pages), but also how any other languages could potentially |
244
|
|
|
|
|
|
|
use CAS authentication and authorization from a central database. |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=item LDAP & Kerberos |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
It would be great to have optional plugins or such that extend CAS to work |
249
|
|
|
|
|
|
|
seemlessly along side both LDAP and Kerberos. An earlier incarnation of this |
250
|
|
|
|
|
|
|
system actually did interact with Kerberos. If a user regestered with their |
251
|
|
|
|
|
|
|
kerberos username and password, CAS verified authentication from then on |
252
|
|
|
|
|
|
|
against Kerberos. It even fetched some user info from the Kerberos server |
253
|
|
|
|
|
|
|
using ph. The schema still has fields for indicating if a user record relates |
254
|
|
|
|
|
|
|
to a kerberos or ldap system, but there is no functionality at this time for |
255
|
|
|
|
|
|
|
such. |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=back |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=cut |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
|
263
|
5
|
|
|
5
|
|
31
|
use Scalar::Util qw(blessed); |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
455
|
|
264
|
5
|
|
|
5
|
|
2453
|
use CAS::Config; |
|
5
|
|
|
|
|
16
|
|
|
5
|
|
|
|
|
135
|
|
265
|
5
|
|
|
5
|
|
3178
|
use CAS::User; |
|
5
|
|
|
|
|
16
|
|
|
5
|
|
|
|
|
329
|
|
266
|
5
|
|
|
5
|
|
56
|
use Digest::MD5 qw(md5_hex); |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
336
|
|
267
|
5
|
|
|
5
|
|
30
|
use Carp qw(cluck confess croak carp); |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
464
|
|
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# otherwise constants don't get exported |
270
|
|
|
|
|
|
|
#use base qw(CAS::Messaging); |
271
|
5
|
|
|
5
|
|
34
|
use CAS::Messaging; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
13250
|
|
272
|
|
|
|
|
|
|
our @ISA = qw(CAS::Messaging); |
273
|
|
|
|
|
|
|
our $AUTOLOAD = ''; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# Config fields that subclasses of core should be able to get and set |
277
|
|
|
|
|
|
|
# Bitmasked with get permission = 1, set = 2, both = 3 |
278
|
|
|
|
|
|
|
my %fields = ( |
279
|
|
|
|
|
|
|
client => 1, |
280
|
|
|
|
|
|
|
dbh => 1, |
281
|
|
|
|
|
|
|
user_info_fields => 1, |
282
|
|
|
|
|
|
|
supl_user_info_fields => 1, |
283
|
|
|
|
|
|
|
admin_email => 1, |
284
|
|
|
|
|
|
|
debug => 3, |
285
|
|
|
|
|
|
|
id => 1, |
286
|
|
|
|
|
|
|
name => 1, |
287
|
|
|
|
|
|
|
supplemental_user_table => 1, |
288
|
|
|
|
|
|
|
default_group => 1, |
289
|
|
|
|
|
|
|
domain => 1, |
290
|
|
|
|
|
|
|
base_path => 1, |
291
|
|
|
|
|
|
|
description => 1, |
292
|
|
|
|
|
|
|
cookie_name => 1, |
293
|
|
|
|
|
|
|
timeout => 1, |
294
|
|
|
|
|
|
|
); |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=head1 METHODS |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head2 new |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
Create a new client object. |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
PARAMETERS: |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
CLIENT_ID: The database ID of the client which is seeking to connect to |
306
|
|
|
|
|
|
|
CAS. |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
CLIENT_NAME: The name of the client which is seeking to connect to |
309
|
|
|
|
|
|
|
CAS. |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
CLIENT_DOMAIN: The domain of the client which is seeking to connect to |
312
|
|
|
|
|
|
|
CAS. |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
You can use any one. If more than one is defined they are checked in the order |
315
|
|
|
|
|
|
|
listed. |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
OPTIONS: |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
CONFIG: Alternate configuration file. Defaults to '/etc/CAS.yaml'. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
DEBUG: Set the DEBUG level for this object. |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=cut |
324
|
|
|
|
|
|
|
sub new { |
325
|
6
|
|
|
6
|
1
|
2216
|
my $proto = shift; |
326
|
6
|
|
33
|
|
|
48
|
my $class = ref($proto) || $proto; |
327
|
6
|
|
|
|
|
16
|
my $HR_params = shift; |
328
|
6
|
50
|
|
|
|
27
|
croak("Parameters not passed as a hashref") |
329
|
|
|
|
|
|
|
unless ref($HR_params) eq 'HASH'; |
330
|
|
|
|
|
|
|
|
331
|
6
|
50
|
100
|
|
|
51
|
croak("No client key provided") |
|
|
|
66
|
|
|
|
|
332
|
|
|
|
|
|
|
unless defined $HR_params->{CLIENT_ID} || $HR_params->{CLIENT_NAME} |
333
|
|
|
|
|
|
|
|| $HR_params->{CLIENT_DOMAIN}; |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# load config |
336
|
6
|
|
|
|
|
59
|
my $config = CAS::Config->load($HR_params); |
337
|
0
|
|
|
|
|
|
$config->{_permitted} = \%fields; |
338
|
0
|
|
|
|
|
|
$config->{_users} = {}; |
339
|
|
|
|
|
|
|
|
340
|
0
|
|
|
|
|
|
my $self = bless ($config,$class); |
341
|
0
|
|
|
|
|
|
$self->_set_result(CREATED,"CAS Client object sucesfully initiatied"); |
342
|
0
|
|
|
|
|
|
return $self; |
343
|
|
|
|
|
|
|
} # new |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=head2 authenticate |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
This function is called to verify the username and password provided by the |
349
|
|
|
|
|
|
|
user. It will imediatly return undef and set the response code to BAD_REQUEST |
350
|
|
|
|
|
|
|
unless both the username and password were provided (well, technically, |
351
|
|
|
|
|
|
|
evaluate to true). It then checks that the password provided matches the one |
352
|
|
|
|
|
|
|
stored for that user. |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
Perls crypt function is called using the suplied password as the word and the |
355
|
|
|
|
|
|
|
password from the db as the salt. If the result matches the stored password, |
356
|
|
|
|
|
|
|
access will be granted. A session key is generated using md5_hex and the user |
357
|
|
|
|
|
|
|
ID and time are stored in the db on that key. Also stored are either the users |
358
|
|
|
|
|
|
|
IP address (if supplied) or the root caller() otherwise. |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
If authentication fails, NOT_FOUND is returned. If authentication succedes |
361
|
|
|
|
|
|
|
the md5_hex key is returned. The key is intended |
362
|
|
|
|
|
|
|
to be used by CAS as a session token for L after first |
363
|
|
|
|
|
|
|
authenticated. Any error message can be found in $client->errstr. |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
PARAMETERS: |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
USERNAME: The username. |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
PASSWORD: The users password. |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
OPTIONS: |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
IP: The remote connection IP. If present at authentication, the IP will be |
374
|
|
|
|
|
|
|
required to be provided and match during any subsiquent authorization check. |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=cut |
377
|
|
|
|
|
|
|
sub authenticate { |
378
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
379
|
0
|
0
|
|
|
|
|
$self->error("Not a method call") unless blessed($self); |
380
|
0
|
|
|
|
|
|
$self->_clear_result; |
381
|
|
|
|
|
|
|
|
382
|
0
|
|
|
|
|
|
my $HR_params = shift; |
383
|
0
|
0
|
|
|
|
|
$self->error("Parameters not passed as a hashref") |
384
|
|
|
|
|
|
|
unless ref($HR_params) eq 'HASH'; |
385
|
0
|
|
0
|
|
|
|
my $debug = $HR_params->{DEBUG} || $self->{DEBUG} || 0; |
386
|
0
|
|
|
|
|
|
my $dbh = $self->dbh; |
387
|
|
|
|
|
|
|
|
388
|
0
|
0
|
|
|
|
|
warn("Checking authentication for $HR_params->{USERNAME}") if $debug; |
389
|
|
|
|
|
|
|
|
390
|
0
|
0
|
|
|
|
|
unless ($HR_params->{USERNAME}) { |
391
|
0
|
|
|
|
|
|
$self->_set_result(BAD_REQUEST,"No username provided."); |
392
|
0
|
|
|
|
|
|
return undef; |
393
|
|
|
|
|
|
|
} # resource to check authorization against required |
394
|
|
|
|
|
|
|
|
395
|
0
|
0
|
|
|
|
|
unless ($HR_params->{PASSWORD}) { |
396
|
0
|
|
|
|
|
|
$self->_set_result(BAD_REQUEST,"No password provided."); |
397
|
0
|
|
|
|
|
|
return undef; |
398
|
|
|
|
|
|
|
} # resource to check authorization against required |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
# OK, now we have a username, lets check the suplied password |
401
|
0
|
|
|
|
|
|
my $Quser = $dbh->quote($HR_params->{USERNAME}); |
402
|
|
|
|
|
|
|
# now get userID and password for username |
403
|
0
|
|
|
|
|
|
my $HR_user = $dbh->selectrow_hashref("SELECT * |
404
|
|
|
|
|
|
|
FROM Users WHERE Username = $Quser"); |
405
|
0
|
0
|
|
|
|
|
$self->error("Database error: " . $dbh->errstr) if $dbh->err; |
406
|
|
|
|
|
|
|
|
407
|
0
|
0
|
|
|
|
|
unless ($HR_user->{User}) { |
408
|
0
|
|
|
|
|
|
$self->_set_result(NOT_FOUND, |
409
|
|
|
|
|
|
|
"Invalid account, username $HR_params->{USERNAME} not found."); |
410
|
0
|
|
|
|
|
|
return undef; |
411
|
|
|
|
|
|
|
} # unless user id returned |
412
|
|
|
|
|
|
|
|
413
|
0
|
0
|
|
|
|
|
if ($HR_user->{Disabled} eq 'Yes') { |
414
|
0
|
|
|
|
|
|
$self->_set_result(FORBIDDEN,"User has been disabled."); |
415
|
0
|
|
|
|
|
|
return undef; |
416
|
|
|
|
|
|
|
} # if user diasabled |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# OK, the user exists and we should have all the information needed |
420
|
|
|
|
|
|
|
# to authenticate |
421
|
0
|
0
|
|
|
|
|
$self->gripe("Password valid?") if $debug > 1; |
422
|
0
|
0
|
|
|
|
|
unless ($HR_user->{Password} |
423
|
|
|
|
|
|
|
eq crypt($HR_params->{PASSWORD},$HR_user->{Password})) { |
424
|
0
|
|
|
|
|
|
$self->_set_result(AUTH_REQUIRED,"Incorrect password."); |
425
|
0
|
|
|
|
|
|
return undef; |
426
|
|
|
|
|
|
|
} # unless password suplied matches users in db |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
# OK, user authenticated, provide a session token |
429
|
0
|
0
|
|
|
|
|
$self->gripe("Issue session token") if $debug; |
430
|
0
|
|
|
|
|
|
my $now = localtime; |
431
|
0
|
|
|
|
|
|
my $Skey = md5_hex("$0$HR_user->{Password}$HR_params->{USERNAME}$now"); |
432
|
0
|
|
|
|
|
|
my $Qkey = $dbh->quote($Skey); |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
# now, stick seomthing into IP? |
435
|
0
|
|
|
|
|
|
my $ip = $dbh->quote($HR_params->{IP}); |
436
|
|
|
|
|
|
|
|
437
|
0
|
|
|
|
|
|
$dbh->do("INSERT INTO Session (ID, User, IP) |
438
|
|
|
|
|
|
|
VALUES ($Qkey,$HR_user->{User},$ip)"); |
439
|
0
|
0
|
|
|
|
|
$self->error("Can't log user in: " . $dbh->errstr) if $dbh->err; |
440
|
|
|
|
|
|
|
|
441
|
0
|
|
|
|
|
|
$self->_set_result(OK,"User authenticated."); |
442
|
0
|
|
|
|
|
|
return ($Skey); |
443
|
|
|
|
|
|
|
} # authenticate |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head2 authorize |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
This checks the database to see if the user is currently logged in and if they |
449
|
|
|
|
|
|
|
are allowed to use the specified resource. |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
PARAMETERS: |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
SESSION: The session token returned by CAS when the user was authenticated |
455
|
|
|
|
|
|
|
and logged in. This is used to get the user information required for checking |
456
|
|
|
|
|
|
|
that user is logged in and that their session has not timed out. ***SECURITY*** |
457
|
|
|
|
|
|
|
It is up to you to make sure that this value is kept private and secure during |
458
|
|
|
|
|
|
|
the session. |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
USER: Alias for SESSION. |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
RESOURCE: This is the resource definition that will be checked in the |
463
|
|
|
|
|
|
|
database. |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
PERMISSIONS: This is the type of action you want to check if the user has |
466
|
|
|
|
|
|
|
permission for relative to the RESOURCE. The allowed values are read, modify, |
467
|
|
|
|
|
|
|
create and delete. Create refers to permision to create a new record which |
468
|
|
|
|
|
|
|
uses the refered to resource as a foreign key, or is under the refered resource |
469
|
|
|
|
|
|
|
'tree'. |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
OPTIONS: |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
MASK: This is an integer mask of permissions to be checked for the specified |
474
|
|
|
|
|
|
|
RESOURCE. This can optionaly be used instead of PERMISSIONS, and is the only |
475
|
|
|
|
|
|
|
way to specify requests on more than one type of permission at the same time. |
476
|
|
|
|
|
|
|
The Values are 8 = read, 4 = modify, 2 = create, 1 = delete. To check for |
477
|
|
|
|
|
|
|
multiple permissions at the same time simply sum all the permissions you want |
478
|
|
|
|
|
|
|
to check. For example, to check for read and modify permision, provide 12 (8+4) |
479
|
|
|
|
|
|
|
as the value for MASK. MASK overides PERMISSIONS if both are specified. |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
MATCHKEY: A matchkey can be used to specify a specific element or key |
482
|
|
|
|
|
|
|
match required. For example, RESOURCE my specify a particular table in a |
483
|
|
|
|
|
|
|
database, with MATCHLEY specifying the primary key match required. Or if |
484
|
|
|
|
|
|
|
RESOURCE was a web page, MATCHKEY may indicate a specific form element. |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
IP: The remote IP of the user. If this was provided during authentication then |
487
|
|
|
|
|
|
|
it is REQUIRED for authorization and the IP's must match. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=cut |
490
|
|
|
|
|
|
|
sub authorize { |
491
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
492
|
0
|
0
|
|
|
|
|
$self->error("Not a method call") unless blessed($self); |
493
|
0
|
|
|
|
|
|
$self->_clear_result; |
494
|
|
|
|
|
|
|
|
495
|
0
|
|
|
|
|
|
my $HR_params = shift; |
496
|
0
|
0
|
|
|
|
|
$self->error("Parameters not passed as a hashref") |
497
|
|
|
|
|
|
|
unless ref($HR_params) eq 'HASH'; |
498
|
0
|
|
0
|
|
|
|
my $debug = $HR_params->{DEBUG} || $self->debug || 0; |
499
|
0
|
|
|
|
|
|
my $dbh = $self->dbh; |
500
|
|
|
|
|
|
|
|
501
|
0
|
0
|
|
|
|
|
warn("Checking authorization") if $debug; |
502
|
|
|
|
|
|
|
|
503
|
0
|
0
|
|
|
|
|
unless ($self->client->{ID}) { |
504
|
0
|
|
|
|
|
|
$self->_set_result(ERROR,"Client object doesn't know its own ID?!"); |
505
|
0
|
|
|
|
|
|
return undef; |
506
|
|
|
|
|
|
|
} # client required |
507
|
|
|
|
|
|
|
|
508
|
0
|
0
|
|
|
|
|
unless ($HR_params->{RESOURCE}) { |
509
|
0
|
|
|
|
|
|
$self->_set_result(BAD_REQUEST,"No resource to authorize against " |
510
|
|
|
|
|
|
|
. "provided."); |
511
|
0
|
|
|
|
|
|
return undef; |
512
|
|
|
|
|
|
|
} # resource to check authorization against required |
513
|
|
|
|
|
|
|
|
514
|
0
|
|
0
|
|
|
|
my $session = $HR_params->{SESSION} || $HR_params->{USER} || undef; |
515
|
0
|
0
|
0
|
|
|
|
unless (defined $session && $session =~ /^\S{32}$/) { |
516
|
0
|
|
|
|
|
|
$self->_set_result(BAD_REQUEST, "Missing or bad SESSION($session) " |
517
|
|
|
|
|
|
|
. "for authorization on request $HR_params->{RESOURCE}"); |
518
|
0
|
|
|
|
|
|
return undef; |
519
|
|
|
|
|
|
|
} # session token required |
520
|
|
|
|
|
|
|
|
521
|
0
|
|
|
|
|
|
my $qsession = $dbh->quote($session); |
522
|
|
|
|
|
|
|
|
523
|
0
|
|
|
|
|
|
my $logged_ip = $dbh->selectrow_array("SELECT IP |
524
|
|
|
|
|
|
|
FROM Session WHERE ID = $qsession"); |
525
|
0
|
0
|
|
|
|
|
$self->error('Problem cheking for logged IP: ' . $dbh->errstr) |
526
|
|
|
|
|
|
|
if $dbh->err; |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
# if an IP was logged when authenticated, the provided IP must match |
529
|
0
|
0
|
0
|
|
|
|
if ($logged_ip && $logged_ip ne $HR_params->{IP}) { |
530
|
0
|
|
|
|
|
|
$self->_set_result(FORBIDDEN, |
531
|
|
|
|
|
|
|
"Current IP ($HR_params->{IP}) does not match IP " |
532
|
|
|
|
|
|
|
. "when you logged on ($logged_ip). This may indicate a 'man in " |
533
|
|
|
|
|
|
|
. "the middle' security attack."); |
534
|
0
|
|
|
|
|
|
return undef; |
535
|
|
|
|
|
|
|
} # if IP & ip doesn't match |
536
|
|
|
|
|
|
|
|
537
|
0
|
|
|
|
|
|
my $timeout = $self->client->{Timeout}; |
538
|
0
|
0
|
|
|
|
|
unless ($timeout) { |
539
|
0
|
|
|
|
|
|
$self->_set_result(ERROR,"Client object does not have a timeout?!"); |
540
|
0
|
|
|
|
|
|
return undef; |
541
|
|
|
|
|
|
|
} # client required |
542
|
|
|
|
|
|
|
|
543
|
0
|
|
|
|
|
|
my $get_timediff = $dbh->prepare("SELECT unix_timestamp() |
544
|
|
|
|
|
|
|
- unix_timestamp(TS) FROM Session WHERE ID = $qsession", |
545
|
|
|
|
|
|
|
{RaiseError => 1}); |
546
|
0
|
0
|
|
|
|
|
$self->error("Problem preparing timediff statement: " . $dbh->errstr) |
547
|
|
|
|
|
|
|
if $dbh->err; |
548
|
|
|
|
|
|
|
|
549
|
0
|
|
|
|
|
|
$get_timediff->execute(); |
550
|
0
|
0
|
|
|
|
|
$self->error("Problem executing timediff statement: " . $dbh->errstr) |
551
|
|
|
|
|
|
|
if $dbh->err; |
552
|
|
|
|
|
|
|
|
553
|
0
|
|
|
|
|
|
my $timediff = $get_timediff->fetchrow_array(); |
554
|
0
|
0
|
|
|
|
|
$self->error("Problem fetching timediff: " . $dbh->errstr) |
555
|
|
|
|
|
|
|
if $dbh->err; |
556
|
|
|
|
|
|
|
|
557
|
0
|
0
|
|
|
|
|
$self->gripe("Params appear in place, checking timeout: " |
558
|
|
|
|
|
|
|
. "$timediff > $timeout") if $debug; |
559
|
0
|
|
|
|
|
|
my $try = 2; |
560
|
0
|
0
|
|
|
|
|
unless (defined $timediff) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
561
|
0
|
|
|
|
|
|
$self->_set_result(ERROR, |
562
|
|
|
|
|
|
|
"Session ID $qsession not in database."); |
563
|
0
|
|
|
|
|
|
return undef; |
564
|
|
|
|
|
|
|
} # session token not found in db |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
elsif ($timediff == 0) { |
567
|
0
|
|
|
|
|
|
while ($timediff == 0) { |
568
|
0
|
|
|
|
|
|
sleep(1); |
569
|
0
|
|
|
|
|
|
$get_timediff->execute(); |
570
|
0
|
0
|
|
|
|
|
$self->error("Problem executing timediff statement: " |
571
|
|
|
|
|
|
|
. $dbh->errstr) if $dbh->err; |
572
|
|
|
|
|
|
|
|
573
|
0
|
|
|
|
|
|
$timediff = $get_timediff->fetchrow_array(); |
574
|
0
|
0
|
|
|
|
|
$self->error("Problem fetching timediff: " . $dbh->errstr) |
575
|
|
|
|
|
|
|
if $dbh->err; |
576
|
|
|
|
|
|
|
|
577
|
0
|
0
|
|
|
|
|
last if $try++ == 8; |
578
|
|
|
|
|
|
|
} # while timediff not true |
579
|
|
|
|
|
|
|
|
580
|
0
|
0
|
|
|
|
|
unless ($timediff) { |
581
|
0
|
|
|
|
|
|
$self->_set_result(FORBIDDEN, |
582
|
|
|
|
|
|
|
"Problem resolving timeout for $qsession."); |
583
|
0
|
|
|
|
|
|
return undef; |
584
|
|
|
|
|
|
|
} # unless second query suceeded |
585
|
|
|
|
|
|
|
} # session token not found |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
elsif ($timediff > $timeout) { |
588
|
0
|
|
|
|
|
|
$self->_set_result(AUTH_REQUIRED,"Session has timed out."); |
589
|
0
|
|
|
|
|
|
return undef; |
590
|
|
|
|
|
|
|
} # if session timed out |
591
|
|
|
|
|
|
|
|
592
|
0
|
|
|
|
|
|
$HR_params->{CLIENT} = $self->client->{ID}; |
593
|
0
|
|
|
|
|
|
$HR_params->{USER} = $self->user($session)->{ID}; |
594
|
0
|
|
0
|
|
|
|
$HR_params->{MATCHKEY} ||= ''; |
595
|
0
|
0
|
|
|
|
|
unless ($dbh->allowed($HR_params)) { |
596
|
0
|
|
|
|
|
|
$self->_set_result(FORBIDDEN, |
597
|
|
|
|
|
|
|
"User for session $qsession not authorized to access " |
598
|
|
|
|
|
|
|
. "$HR_params->{RESOURCE},$HR_params->{MATCHKEY}:\n\t" |
599
|
|
|
|
|
|
|
. $dbh->errstr); |
600
|
0
|
|
|
|
|
|
return undef; |
601
|
|
|
|
|
|
|
} # unless user has permision |
602
|
|
|
|
|
|
|
|
603
|
0
|
|
|
|
|
|
$dbh->do("UPDATE Session SET TS=NULL WHERE ID = $qsession"); |
604
|
0
|
0
|
|
|
|
|
$self->error("Problem updating timestamp for $qsession: " . |
605
|
|
|
|
|
|
|
$dbh->errstr) if $dbh->err; |
606
|
|
|
|
|
|
|
|
607
|
0
|
|
|
|
|
|
$self->_set_result(OK,"User authenticated."); |
608
|
0
|
|
|
|
|
|
return OK; |
609
|
|
|
|
|
|
|
} # authorize |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=head2 user |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
Access the user object (L) for authenticated users. Method takes |
615
|
|
|
|
|
|
|
a single argument, the authenticated users session token. |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
=cut |
618
|
|
|
|
|
|
|
sub user { |
619
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
620
|
0
|
0
|
|
|
|
|
$self->error("Not a method call") unless blessed($self); |
621
|
0
|
|
|
|
|
|
$self->_clear_result; |
622
|
0
|
|
|
|
|
|
my $session = shift; |
623
|
|
|
|
|
|
|
|
624
|
0
|
0
|
0
|
|
|
|
unless (defined $session && $session =~ /^\S{32}$/) { |
625
|
0
|
|
|
|
|
|
$self->_set_result(BAD_REQUEST, "Missing or bad SESSION($session) "); |
626
|
0
|
|
|
|
|
|
return undef; |
627
|
|
|
|
|
|
|
} # session token required |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
# if we've already loaded up the user object, just return it |
630
|
0
|
0
|
|
|
|
|
if ($self->{_users}{$session}) { |
631
|
0
|
|
|
|
|
|
$self->_set_result(OK,"Cached user object returned."); |
632
|
0
|
|
|
|
|
|
return $self->{_users}{$session}; |
633
|
|
|
|
|
|
|
} # if user already stored |
634
|
|
|
|
|
|
|
|
635
|
0
|
|
|
|
|
|
my $dbh = $self->dbh; |
636
|
0
|
|
|
|
|
|
my $qsession = $dbh->quote($session); |
637
|
0
|
|
|
|
|
|
my $id = $dbh->selectrow_array("SELECT User FROM Session |
638
|
|
|
|
|
|
|
WHERE ID = $qsession"); |
639
|
0
|
0
|
|
|
|
|
$self->error("Problem getting user ID from $qsession: " . |
640
|
|
|
|
|
|
|
$dbh->errstr) if $dbh->err; |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
# should we be checking for old instances of the same user to delete? |
643
|
|
|
|
|
|
|
|
644
|
0
|
|
|
|
|
|
my $user = CAS::User->load({ID => $id, CLIENT_ID => $self->client->{ID}, |
645
|
|
|
|
|
|
|
CONFIG => $self->{conf_file}}); |
646
|
0
|
0
|
0
|
|
|
|
unless (defined $user && $user->response_is(CREATED)) { |
647
|
0
|
|
|
|
|
|
$self->_set_result(ERROR,$user->messages); |
648
|
0
|
|
|
|
|
|
return undef; |
649
|
|
|
|
|
|
|
} # unless we were able to load user |
650
|
|
|
|
|
|
|
|
651
|
0
|
|
|
|
|
|
$self->{_users}{$session} = $user; |
652
|
0
|
|
|
|
|
|
$self->_set_result(OK,"User object created and returned."); |
653
|
0
|
|
|
|
|
|
return $user; |
654
|
|
|
|
|
|
|
} # user |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
# Allows fetching of certain CAS attributes |
659
|
|
|
|
|
|
|
sub AUTOLOAD { |
660
|
0
|
|
|
0
|
|
|
my $self = shift; |
661
|
0
|
0
|
|
|
|
|
return if ($AUTOLOAD =~ /DESTROY/); |
662
|
0
|
|
|
|
|
|
my $class = blessed($self); |
663
|
0
|
0
|
|
|
|
|
$self->error("Not a method call") unless $class; |
664
|
0
|
|
|
|
|
|
$self->_clear_result; |
665
|
|
|
|
|
|
|
|
666
|
0
|
|
|
|
|
|
my $name = $AUTOLOAD; |
667
|
0
|
|
|
|
|
|
$name =~ s/.*://; # strip fully-qualified portion |
668
|
|
|
|
|
|
|
|
669
|
0
|
0
|
|
|
|
|
unless (exists $self->{_permitted}->{$name} ) { |
670
|
0
|
|
|
|
|
|
$self->error("Can't access `$name' field in class $class"); |
671
|
|
|
|
|
|
|
} # unless access to the data feild is permitted |
672
|
|
|
|
|
|
|
|
673
|
0
|
0
|
|
|
|
|
if (@_) { |
674
|
0
|
0
|
|
|
|
|
$self->error("Not allowed to set $name") |
675
|
|
|
|
|
|
|
unless $self->{_permitted}{$name} & 2; |
676
|
|
|
|
|
|
|
# update database |
677
|
|
|
|
|
|
|
|
678
|
0
|
|
|
|
|
|
$self->{$name} = $_[0]; |
679
|
0
|
|
|
|
|
|
return $self->{$name}; |
680
|
|
|
|
|
|
|
} # if a new value supplied |
681
|
|
|
|
|
|
|
else { |
682
|
0
|
0
|
|
|
|
|
$self->error("Not allowed to fetch $name") |
683
|
|
|
|
|
|
|
unless $self->{_permitted}{$name} & 1; |
684
|
0
|
|
|
|
|
|
return $self->{$name}; |
685
|
|
|
|
|
|
|
} # else just return current value |
686
|
|
|
|
|
|
|
} # AUTOLOAD |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
=head1 INSTALLING |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
There are a few steps you will need to handle before you can proceed to the |
692
|
|
|
|
|
|
|
usual CPAN distribution make, make test, make install magic. Primarilly, you |
693
|
|
|
|
|
|
|
need to create the CAS database before any tests beyond syntax checking will |
694
|
|
|
|
|
|
|
pass. |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
% tar -xzf CAS-x.xx.tar.gz |
697
|
|
|
|
|
|
|
% cd CAS-x.xx |
698
|
|
|
|
|
|
|
% pwd |
699
|
|
|
|
|
|
|
/path/to/CAS-x.xx |
700
|
|
|
|
|
|
|
% mysql -u root -p |
701
|
|
|
|
|
|
|
password: |
702
|
|
|
|
|
|
|
mysql> CREATE DATABASE CAS; |
703
|
|
|
|
|
|
|
mysql> USE CAS; |
704
|
|
|
|
|
|
|
mysql> source /path/to/CAS-x.xx/CAS.sql |
705
|
|
|
|
|
|
|
mysql> GRANT ALL ON CAS.* TO CAS_query IDENTIFIED BY 'local_passwd' |
706
|
|
|
|
|
|
|
mysql> GRANT ALL ON CAS.* TO CAS_query@localhost IDENTIFIED BY 'local_passwd' |
707
|
|
|
|
|
|
|
mysql> exit |
708
|
|
|
|
|
|
|
% perl Makefile.PL |
709
|
|
|
|
|
|
|
% make |
710
|
|
|
|
|
|
|
% make test |
711
|
|
|
|
|
|
|
% make install |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
When running Makefile.PL for the first time you will be asked a bunch of |
714
|
|
|
|
|
|
|
questions. Answer them appropriately for your system. The DB_* items all |
715
|
|
|
|
|
|
|
relate to the information you provided mysql when setting up the database. If |
716
|
|
|
|
|
|
|
at any time you want to regenerate the configuration file, just delete it and |
717
|
|
|
|
|
|
|
rerun Makefile.PL. |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=head1 AUTHOR |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
Sean P. Quinlan, C<< >> |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
=head1 development notes |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=head2 groups |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
Groups are always associated with a client. However, groups from one |
728
|
|
|
|
|
|
|
client can be granted permissions on any other client. Generally all |
729
|
|
|
|
|
|
|
groups are owned by the CAS Admin client but it is possible to have admin |
730
|
|
|
|
|
|
|
tools on another client and allow them to manage their own group(s). The |
731
|
|
|
|
|
|
|
admin user for any client can alter/drop existing groups under that client. |
732
|
|
|
|
|
|
|
Additionally groups can have a 'Owner' specified. This is generally a user |
733
|
|
|
|
|
|
|
who also has rights to modify the group and add/remove members, but not to |
734
|
|
|
|
|
|
|
delete it. |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=head1 BUGS |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
739
|
|
|
|
|
|
|
C, or through the web interface at |
740
|
|
|
|
|
|
|
L. |
741
|
|
|
|
|
|
|
I will be notified, and then you'll automatically be notified of progress on |
742
|
|
|
|
|
|
|
your bug as I make changes. |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
=head1 HISTORY |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
=over 8 |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
=item 0.01 |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
Original version; created by module-starter |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
=item 0.1 |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
Initial code port from CAS. History below to .30_2 ported from CAS. |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
=item 0.2 |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
Basic required functionality for check auths in place. Apache auth handlers done |
759
|
|
|
|
|
|
|
as well as simple Login handler. Core tests written and passing, user tests of |
760
|
|
|
|
|
|
|
Apache handlers pass basic required functionality. |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
=item 0.21 |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
User module functional and all basic methods in place. No automated tests for it |
765
|
|
|
|
|
|
|
yet but that will be my next task before moving on to the Apache handlers for |
766
|
|
|
|
|
|
|
registering a new user and a user view edit account handler. Also started |
767
|
|
|
|
|
|
|
working on the docs. |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
=item 0.22 |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
Added tests for user object and disable/enable methods. Small additions to |
772
|
|
|
|
|
|
|
docs, like fixing my email address in this package! ;) |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
=item 0.23 |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
Most of the basic Apache stuff has been worked out. The CAS.yaml file was |
777
|
|
|
|
|
|
|
expanded and commented. I made a CAS.conf for all our Apache config stuff so |
778
|
|
|
|
|
|
|
admins can just Include it rather than edit the main conf. So far registering |
779
|
|
|
|
|
|
|
a new user & logging are functional if not quite complete or pretty. |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=item 0.3_1 |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
The internals of this module are pretty stable now. I added the |
784
|
|
|
|
|
|
|
krb5_authentication function and added code to check_authentication to check |
785
|
|
|
|
|
|
|
krb5 auth if required in conf or specified in user table. |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
=item 0.30_2 |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
Ported to stub distribution generated by module-starter. Split Apache and |
790
|
|
|
|
|
|
|
core CAS functionality into two dists. Started removing krb5 support from core |
791
|
|
|
|
|
|
|
modules. If I continue to support it, it will be as an optional extension. |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
=item 0.40 |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
Entered heavy development - many change entries were not made. Guessing from |
796
|
|
|
|
|
|
|
here to version .89 |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
=item 0.41 |
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
Finished post-port cleanup. Added some very simple tests. |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
=item 0.42 |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
Split out Messaging.pm and did a little more cleanup on CAS.pm |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
=item 0.43 |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
Reworked parts of Messaging.pm, updated everything to use messaging. |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=item 0.44 |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
Did some code cleanup on Users.pm, improved AUTOLOADS, adding %allowed with |
813
|
|
|
|
|
|
|
bitmasks. Added a few more tests, most of which fail. |
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
=item 0.50 |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
Started working on API and getting tests to pass. Small |
818
|
|
|
|
|
|
|
adjustments to schema. |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=item 0.52 |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
Debugging. Tests passing. |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
=item 0.60 |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
Completely changed object relations, making the CAS object all about the |
827
|
|
|
|
|
|
|
client and adding user caching. User.pm is no longer a subclass of CAS and |
828
|
|
|
|
|
|
|
authentication happens through the client object. |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
=item 0.61 |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
Updated tests and made some changes to API based on working out tests. |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
=item 0.80 |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
Wrote a slew more tests, got all the client and user tests passing. |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
=item 0.81 |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
Added generation of CAS.yaml to Makefile.PL and wrote post_install.prl. |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
=item 0.82 |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
Refined CAS.yaml generation some and tripled the number of tests. |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
=item 0.83 |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
Got auth tests passing! |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
=item 0.86 |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
All basic tests pass for existing funtionality. Can add, load, edit & |
853
|
|
|
|
|
|
|
disable users. Client object can handle multiple users, caching user |
854
|
|
|
|
|
|
|
objects by session token and authenticate and authorize against |
855
|
|
|
|
|
|
|
permissions in database. |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
=item 0.87 |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
Improved some error statements. Updated MANIFEST so the new modules were |
860
|
|
|
|
|
|
|
included in the distribution. (d'oh!) Allowed caller to supply ID to |
861
|
|
|
|
|
|
|
User->new to support installs where there is already a database of users |
862
|
|
|
|
|
|
|
(or employees) where use of pre-existing IDs is important. |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
=item 0.88 |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
Broke up Config.pm, mainly to separate database connection from load and to |
867
|
|
|
|
|
|
|
use a database connection routine that captured the db password in a closure. |
868
|
|
|
|
|
|
|
This was required to support CAS-Apache2, where storing the database |
869
|
|
|
|
|
|
|
connection in the global client object caused 'Command out of sync' errors |
870
|
|
|
|
|
|
|
on some otherwise valid setups. |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
=item 0.89 |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
Updated the documentation. Made the fields in the clients table attributes of |
875
|
|
|
|
|
|
|
the client object. Added some info on the caller to messages when debuging. |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
=back |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
=head1 SUPPORT |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
perldoc CAS |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
On most Unix systems you can probably also find the documentation under the |
887
|
|
|
|
|
|
|
man pages. |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
shell> man CAS |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
Please join the CAS mailing list and suggest a final release name for |
892
|
|
|
|
|
|
|
the package. |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
http://mail.grendels-den.org/mailman/listinfo/CAS_grendels-den.org |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
You can also look for information at: |
897
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
=over 4 |
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
L |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
=item * CPAN Ratings |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
L |
907
|
|
|
|
|
|
|
=item * Search CPAN |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
L |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
=back |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
=head2 BUGS |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
For bugs, bug reporting and feature requests, see CPAN's request tracker |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
L |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
The Bioinformatics Group at Massachusetts General Hospital during my |
923
|
|
|
|
|
|
|
tenure there for development assistance and advice, particularly the QA team |
924
|
|
|
|
|
|
|
for banging on the project code. |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
Copyright 2004-2007 Sean P. Quinlan, all rights reserved. |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
932
|
|
|
|
|
|
|
under the same terms as Perl itself. |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
=cut |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
1; # End of CAS |