| 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 |