Commit | Line | Data |
2006bd3f |
1 | package Text::Tradition::UserStore; |
2 | |
3 | use strict; |
4 | use warnings; |
5 | |
6 | use Moose; |
7 | use KiokuX::User::Util qw(crypt_password); |
f7ff202c |
8 | use Text::Tradition::Error; |
2006bd3f |
9 | |
cf7e4e7b |
10 | extends 'Text::Tradition::Directory'; |
11 | # extends 'KiokuX::Model'; |
d1ba091f |
12 | |
2006bd3f |
13 | use Text::Tradition::User; |
2006bd3f |
14 | |
1384ec2d |
15 | =head1 NAME |
16 | |
17 | Text::Tradition::UserStore - KiokuDB storage management for Users |
18 | |
19 | =head1 SYNOPSIS |
20 | |
21 | my $userstore = Text::Tradition::UserStore->new(dsn => 'dbi:SQLite:foo.db'); |
22 | my $newuser = $userstore->add_user({ username => 'fred', |
23 | password => 'somepassword' }); |
24 | |
25 | my $fetchuser = $userstore->find_user({ username => 'fred' }); |
26 | if($fetchuser->check_password('somepassword')) { |
27 | ## login user or .. whatever |
28 | } |
29 | |
30 | my $user = $userstore->deactivate_user({ username => 'fred' }); |
31 | if(!$user->active) { |
32 | ## shouldnt be able to login etc |
33 | } |
34 | |
35 | =head1 DESCRIPTION |
36 | |
37 | A L<KiokuX::Model> for managing the storage and creation of |
38 | L<Text::Tradition::User> objects. Subclass or replace this module in |
39 | order to use a different source for stemmaweb users. |
40 | |
41 | =head2 ATTRIBUTES |
42 | |
43 | =head3 dsn |
44 | |
45 | Inherited from KiokuX::Model - dsn for the data store we are using. |
46 | |
47 | =head3 MIN_PASS_LEN |
48 | |
49 | Constant for the minimum password length when validating passwords, |
50 | defaults to "8". |
51 | |
52 | =cut |
53 | |
ef02228c |
54 | has MIN_PASS_LEN => ( is => 'ro', isa => 'Num', default => sub { 8 } ); |
55 | |
d1ba091f |
56 | # has 'directory' => ( |
57 | # is => 'rw', |
58 | # isa => 'KiokuX::Model', |
59 | # handles => [] |
60 | # ); |
2006bd3f |
61 | |
570cf8ba |
62 | ## TODO: Some of these methods should probably optionally take $user objects |
63 | ## instead of hashrefs. |
64 | |
65 | ## It also occurs to me that all these methods don't need to be named |
66 | ## XX_user, but leaving that way for now incase we merge this code |
67 | ## into ::Directory for one-store. |
68 | |
1384ec2d |
69 | =head2 METHODS |
70 | |
71 | =head3 add_user |
72 | |
73 | Takes a hashref of C<username>, C<password>. |
74 | |
75 | Create a new user object, store in the KiokuDB backend, and return it. |
76 | |
77 | =cut |
78 | |
2006bd3f |
79 | sub add_user { |
ef02228c |
80 | my ($self, $userinfo) = @_; |
63bedbf3 |
81 | my $username = $userinfo->{url} || $userinfo->{username}; |
ef02228c |
82 | my $password = $userinfo->{password}; |
83 | |
f7ff202c |
84 | throw( "No username given" ) unless $username; |
85 | throw( "Invalid password - too short?" ) |
86 | unless ( $self->validate_password($password) || $username =~ /^https?:/ ); |
87 | |
2006bd3f |
88 | my $user = Text::Tradition::User->new( |
89 | id => $username, |
63bedbf3 |
90 | password => ($password ? crypt_password($password) : ''), |
2006bd3f |
91 | ); |
92 | |
d1ba091f |
93 | my $scope = $self->new_scope; |
94 | $self->store($user->kiokudb_object_id, $user); |
2006bd3f |
95 | |
96 | return $user; |
97 | } |
98 | |
63bedbf3 |
99 | sub create_user { |
100 | my $self = shift; |
101 | return $self->add_user(@_); |
102 | } |
103 | |
1384ec2d |
104 | =head3 find_user |
105 | |
031a15f4 |
106 | Takes a hashref of C<username>, optionally C<openid_identifier>. |
1384ec2d |
107 | |
108 | Fetches the user object for the given username and returns it. |
109 | |
110 | =cut |
111 | |
2006bd3f |
112 | sub find_user { |
d1ba091f |
113 | my ($self, $userinfo) = @_; |
031a15f4 |
114 | ## url or display? |
115 | # 'display' => 'castaway.myopenid.com', |
116 | # 'url' => 'http://castaway.myopenid.com/', |
117 | my $username = $userinfo->{url} || $userinfo->{username}; |
2006bd3f |
118 | |
570cf8ba |
119 | my $scope = $self->new_scope; |
d1ba091f |
120 | return $self->lookup(Text::Tradition::User->id_for_user($username)); |
2006bd3f |
121 | |
122 | } |
123 | |
1384ec2d |
124 | =head3 modify_user |
125 | |
126 | Takes a hashref of C<username> and C<password> (same as add_user). |
127 | |
128 | Retrieves the user, and updates it with the new information. Username |
f7ff202c |
129 | changing is not currently supported. Returns the updated user object. |
130 | Throws an error if user not found. |
1384ec2d |
131 | |
132 | =cut |
133 | |
ef02228c |
134 | sub modify_user { |
135 | my ($self, $userinfo) = @_; |
136 | my $username = $userinfo->{username}; |
137 | my $password = $userinfo->{password}; |
138 | |
f7ff202c |
139 | throw( "Missing username or bad password" ) |
140 | unless $username && $self->validate_password($password); |
ef02228c |
141 | |
142 | my $user = $self->find_user({ username => $username }); |
f7ff202c |
143 | throw( "Could not find user $username" ) unless $user; |
ef02228c |
144 | |
145 | my $scope = $self->new_scope; |
146 | $user->password(crypt_password($password)); |
147 | |
148 | $self->update($user); |
149 | |
150 | return $user; |
151 | } |
152 | |
1384ec2d |
153 | =head3 deactivate_user |
154 | |
155 | Takes a hashref of C<username>. |
156 | |
157 | Sets the users C<active> flag to false (0), and sets all traditions |
158 | assigned to them to non-public, updates the storage and returns the |
159 | deactivated user. |
160 | |
f7ff202c |
161 | Throws an error if user not found. |
1384ec2d |
162 | |
163 | =cut |
164 | |
570cf8ba |
165 | sub deactivate_user { |
166 | my ($self, $userinfo) = @_; |
167 | my $username = $userinfo->{username}; |
168 | |
f7ff202c |
169 | throw( "Need to specify a username for deactivation" ) unless $username; |
570cf8ba |
170 | |
171 | my $user = $self->find_user({ username => $username }); |
f7ff202c |
172 | throw( "User $username not found" ) unless $user; |
570cf8ba |
173 | |
174 | $user->active(0); |
175 | foreach my $tradition (@{ $user->traditions }) { |
176 | ## Not implemented yet |
177 | # $tradition->public(0); |
178 | } |
179 | my $scope = $self->new_scope; |
180 | |
181 | ## Should we be using Text::Tradition::Directory also? |
182 | $self->update(@{ $user->traditions }); |
183 | |
184 | $self->update($user); |
185 | |
186 | return $user; |
ef02228c |
187 | } |
188 | |
1384ec2d |
189 | =head3 reactivate_user |
190 | |
191 | Takes a hashref of C<username>. |
192 | |
193 | Returns the user object if already activated. Activates (sets the |
194 | active flag to true (1)), updates the storage and returns the user. |
195 | |
f7ff202c |
196 | Throws an error if the user is not found. |
1384ec2d |
197 | |
198 | =cut |
199 | |
570cf8ba |
200 | sub reactivate_user { |
201 | my ($self, $userinfo) = @_; |
202 | my $username = $userinfo->{username}; |
203 | |
f7ff202c |
204 | throw( "Need to specify a username for reactivation" ) unless $username; |
570cf8ba |
205 | |
206 | my $user = $self->find_user({ username => $username }); |
f7ff202c |
207 | throw( "User $username not found" ) unless $user; |
570cf8ba |
208 | |
209 | return $user if $user->active; |
210 | |
211 | $user->active(1); |
212 | my $scope = $self->new_scope; |
213 | $self->update($user); |
214 | |
215 | return $user; |
216 | } |
217 | |
1384ec2d |
218 | =head3 delete_user |
219 | |
220 | CAUTION: Delets actual data! |
221 | |
222 | Takes a hashref of C<username>. |
223 | |
f7ff202c |
224 | Throws an error if the user doesn't exist. |
1384ec2d |
225 | |
226 | Removes the user from the store and returns 1. |
227 | |
228 | =cut |
229 | |
570cf8ba |
230 | sub delete_user { |
231 | my ($self, $userinfo) = @_; |
232 | my $username = $userinfo->{username}; |
233 | |
f7ff202c |
234 | throw( "Need to specify a username for deletion" ) unless $username; |
570cf8ba |
235 | |
236 | my $user = $self->find_user({ username => $username }); |
f7ff202c |
237 | throw( "User $username not found" ) unless $user; |
570cf8ba |
238 | |
239 | my $scope = $self->new_scope; |
240 | |
241 | ## Should we be using Text::Tradition::Directory for this bit? |
242 | $self->delete( @{ $user->traditions }); |
243 | |
244 | ## Poof, gone. |
245 | $self->delete($user); |
246 | |
247 | return 1; |
248 | } |
ef02228c |
249 | |
1384ec2d |
250 | =head3 validate_password |
251 | |
252 | Takes a password string. Returns true if it is longer than |
253 | L</MIN_PASS_LEN>, false otherwise. |
254 | |
255 | Used internally by L</add_user>. |
256 | |
257 | =cut |
258 | |
ef02228c |
259 | sub validate_password { |
260 | my ($self, $password) = @_; |
261 | |
262 | return if !$password; |
263 | return if length($password) < $self->MIN_PASS_LEN; |
264 | |
265 | return 1; |
266 | } |
267 | |
f7ff202c |
268 | sub throw { |
269 | Text::Tradition::Error->throw( |
270 | 'ident' => 'UserStore error', |
271 | 'message' => $_[0], |
272 | ); |
273 | } |
274 | |
2006bd3f |
275 | 1; |