1 package Text::Tradition::UserStore;
7 use KiokuX::User::Util qw(crypt_password);
8 use Text::Tradition::Error;
10 extends 'Text::Tradition::Directory';
11 # extends 'KiokuX::Model';
13 use Text::Tradition::User;
17 Text::Tradition::UserStore - KiokuDB storage management for Users
21 my $userstore = Text::Tradition::UserStore->new(dsn => 'dbi:SQLite:foo.db');
22 my $newuser = $userstore->add_user({ username => 'fred',
23 password => 'somepassword' });
25 my $fetchuser = $userstore->find_user({ username => 'fred' });
26 if($fetchuser->check_password('somepassword')) {
27 ## login user or .. whatever
30 my $user = $userstore->deactivate_user({ username => 'fred' });
32 ## shouldnt be able to login etc
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.
45 Inherited from KiokuX::Model - dsn for the data store we are using.
49 Constant for the minimum password length when validating passwords,
54 has MIN_PASS_LEN => ( is => 'ro', isa => 'Num', default => sub { 8 } );
56 # has 'directory' => (
58 # isa => 'KiokuX::Model',
62 ## TODO: Some of these methods should probably optionally take $user objects
63 ## instead of hashrefs.
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.
73 Takes a hashref of C<username>, C<password>.
75 Create a new user object, store in the KiokuDB backend, and return it.
80 my ($self, $userinfo) = @_;
81 my $username = $userinfo->{url} || $userinfo->{username};
82 my $password = $userinfo->{password};
84 throw( "No username given" ) unless $username;
85 throw( "Invalid password - must be at least " . $self->MIN_PASS_LEN
86 . " characters long" )
87 unless ( $self->validate_password($password) || $username =~ /^https?:/ );
89 my $user = Text::Tradition::User->new(
91 password => ($password ? crypt_password($password) : ''),
94 my $scope = $self->new_scope;
95 $self->store($user->kiokudb_object_id, $user);
102 return $self->add_user(@_);
107 Takes a hashref of C<username>, optionally C<openid_identifier>.
109 Fetches the user object for the given username and returns it.
114 my ($self, $userinfo) = @_;
116 # 'display' => 'castaway.myopenid.com',
117 # 'url' => 'http://castaway.myopenid.com/',
118 my $username = $userinfo->{url} || $userinfo->{username};
120 my $scope = $self->new_scope;
121 return $self->lookup(Text::Tradition::User->id_for_user($username));
127 Takes a hashref of C<username> and C<password> (same as add_user).
129 Retrieves the user, and updates it with the new information. Username
130 changing is not currently supported. Returns the updated user object.
131 Throws an error if user not found.
136 my ($self, $userinfo) = @_;
137 my $username = $userinfo->{username};
138 my $password = $userinfo->{password};
140 throw( "Missing username or bad password" )
141 unless $username && $self->validate_password($password);
143 my $user = $self->find_user({ username => $username });
144 throw( "Could not find user $username" ) unless $user;
146 my $scope = $self->new_scope;
147 $user->password(crypt_password($password));
149 $self->update($user);
154 =head3 deactivate_user
156 Takes a hashref of C<username>.
158 Sets the users C<active> flag to false (0), and sets all traditions
159 assigned to them to non-public, updates the storage and returns the
162 Throws an error if user not found.
166 sub deactivate_user {
167 my ($self, $userinfo) = @_;
168 my $username = $userinfo->{username};
170 throw( "Need to specify a username for deactivation" ) unless $username;
172 my $user = $self->find_user({ username => $username });
173 throw( "User $username not found" ) unless $user;
176 foreach my $tradition (@{ $user->traditions }) {
177 ## Not implemented yet
178 # $tradition->public(0);
180 my $scope = $self->new_scope;
182 ## Should we be using Text::Tradition::Directory also?
183 $self->update(@{ $user->traditions });
185 $self->update($user);
190 =head3 reactivate_user
192 Takes a hashref of C<username>.
194 Returns the user object if already activated. Activates (sets the
195 active flag to true (1)), updates the storage and returns the user.
197 Throws an error if the user is not found.
201 sub reactivate_user {
202 my ($self, $userinfo) = @_;
203 my $username = $userinfo->{username};
205 throw( "Need to specify a username for reactivation" ) unless $username;
207 my $user = $self->find_user({ username => $username });
208 throw( "User $username not found" ) unless $user;
210 return $user if $user->active;
213 my $scope = $self->new_scope;
214 $self->update($user);
221 CAUTION: Delets actual data!
223 Takes a hashref of C<username>.
225 Throws an error if the user doesn't exist.
227 Removes the user from the store and returns 1.
232 my ($self, $userinfo) = @_;
233 my $username = $userinfo->{username};
235 throw( "Need to specify a username for deletion" ) unless $username;
237 my $user = $self->find_user({ username => $username });
238 throw( "User $username not found" ) unless $user;
240 my $scope = $self->new_scope;
242 ## Should we be using Text::Tradition::Directory for this bit?
243 $self->delete( @{ $user->traditions });
246 $self->delete($user);
251 =head3 validate_password
253 Takes a password string. Returns true if it is longer than
254 L</MIN_PASS_LEN>, false otherwise.
256 Used internally by L</add_user>.
260 sub validate_password {
261 my ($self, $password) = @_;
263 return if !$password;
264 return if length($password) < $self->MIN_PASS_LEN;
270 Text::Tradition::Error->throw(
271 'ident' => 'UserStore error',