File Coverage

blib/lib/WWW/Suffit/AuthDB/Role/CRUD.pm
Criterion Covered Total %
statement 369 479 77.0
branch 132 316 41.7
condition 52 206 25.2
subroutine 31 43 72.0
pod 38 38 100.0
total 622 1082 57.4


line stmt bran cond sub pod time code
1             package WWW::Suffit::AuthDB::Role::CRUD;
2 1     1   337506 use strict;
  1         3  
  1         46  
3 1     1   6 use utf8;
  1         3  
  1         8  
4              
5             =encoding utf8
6              
7             =head1 NAME
8              
9             WWW::Suffit::AuthDB::Role::CRUD - Suffit AuthDB methods for CRUD
10              
11             =head1 SYNOPSIS
12              
13             use WWW::Suffit::AuthDB;
14              
15             my $authdb = WWW::Suffit::AuthDB->with_roles('+CRUD')->new( ... );
16              
17             =head1 DESCRIPTION
18              
19             Suffit AuthDB methods for CRUD
20              
21             =head1 METHODS
22              
23             This class extends L and implements the following new ones methods
24              
25             =head2 export_data
26              
27             $authdb->export_data->save; # to `sourcefile`
28             $authdb->export_data("/tmp/authdb.json");
29              
30             Export all data to JSON file
31              
32             =head2 group_del
33              
34             $authdb->group_del( "wheel" ) or die $authdb->error;
35              
36             Delete group by groupname
37              
38             =head2 group_enroll
39              
40             $authdb->group_enroll(
41             groupname => "wheel",
42             username => "alice",
43             ) or die $authdb->error;
44              
45             Add user to group members
46              
47             =head2 group_get
48              
49             my %data = $authdb->group_get( "wheel" );
50             my @groups = $authdb->group_get;
51              
52             This method returns group's data or returns all groups as array of hashes
53              
54             =head2 group_members
55              
56             my @members = $authdb->group_members( "wheel" );
57              
58             This method returns group's members
59              
60             =head2 group_pset
61              
62             $authdb->group_pset(
63             groupname => "wheel",
64             description => "Admin group",
65             ) or die $authdb->error;
66              
67             This method adds new group or doing update data of existing group in pure mode
68              
69             =head2 group_pure_set
70              
71             This method is deprecated! See L
72              
73             =head2 group_set
74              
75             $authdb->group_set(
76             groupname => "wheel",
77             description => "Admin group",
78             ) or die $authdb->error;
79              
80             This method adds new group or doing update data of existing group
81              
82             =head2 import_data
83              
84             $authdb->load->import_data; # from `sourcefile` preloaded data
85             $authdb->import_data("/tmp/authdb.json");
86              
87             Import all data from JSON file
88              
89             =head2 meta
90              
91             $authdb->meta("my.key", "my value") or die $authdb->error;
92              
93             Sets meta-value by key
94              
95             my $val = $authdb->meta("my.key"); # my value
96             die $authdb->error if $authdb->error;
97              
98             Gets meta-value by key
99              
100             $authdb->meta("my.key", undef) or die $authdb->error;
101              
102             Deletes meta-value by key
103              
104             =head2 realm_del
105              
106             $authdb->realm_del( "default" ) or die $authdb->error;
107              
108             Delete realm by realmname
109              
110             =head2 realm_get
111              
112             my %data = $authdb->realm_get( "default" );
113             my @realms = $authdb->realm_get;
114              
115             This method returns realm's data or returns all realms as array of hashes
116              
117             =head2 realm_pset
118              
119             $authdb->realm_pset(
120             realmname => "default",
121             realm => "Strict Zone",
122             description => "Default realm",
123             ) or die $authdb->error;
124              
125             This method adds new realm or doing update data of existing realm in pure mode
126              
127             =head2 realm_pure_set
128              
129             This method is deprecated! See L
130              
131             =head2 realm_requirements
132              
133             my @requirements = $authdb->realm_requirements( "default" );
134              
135             This method returns list of realm's requirements
136              
137             =head2 realm_routes
138              
139             my @routes = $authdb->realm_routes( "default" );
140              
141             This method returns list of realm's routes
142              
143             =head2 realm_set
144              
145             $authdb->realm_set(
146             realmname => "default",
147             realm => "Strict Zone",
148             description => "Default realm",
149             ) or die $authdb->error;
150              
151             This method adds new realm or doing update data of existing realm
152              
153             =head2 route_del
154              
155             $authdb->route_del( "root" ) or die $authdb->error;
156              
157             Delete route by routename
158              
159             =head2 route_get
160              
161             my %data = $authdb->route_get( "root" );
162             my @routes = $authdb->route_get;
163              
164             This method returns route's data or returns all routes as array of hashes
165              
166             =head2 route_pset
167              
168             $authdb->route_pset(
169             realmname => "default",
170             routename => "root",
171             method => "GET",
172             url => "https://localhost:8695/",
173             base => "https://localhost:8695/",
174             path => "/",
175             ) or die $authdb->error;
176              
177             This method adds new route or doing update data of existing route in pure mode
178              
179             =head2 route_pure_set
180              
181             This method is deprecated! See L
182              
183             =head2 route_search
184              
185             my @routes = $authdb->route_search( $text );
186              
187             This method performs search route by name fragment
188              
189             =head2 route_set
190              
191             $authdb->route_set(
192             realmname => "default",
193             routename => "root",
194             method => "GET",
195             url => "https://localhost:8695/",
196             base => "https://localhost:8695/",
197             path => "/",
198             ) or die $authdb->error;
199              
200             This method adds new route or doing update data of existing route
201              
202             =head2 token_check
203              
204             $authdb->token_check($username, $jti)
205             or die "The token is revoked";
206              
207             This method checks status of the token in database
208              
209             =head2 token_del
210              
211             $authdb->token_del($username, $jti)
212             or die $authdb->error;
213              
214             This method deletes token from database by username and token ID (jti)
215              
216             =head2 token_get
217              
218             my @tokens = $authdb->token_get();
219             my %data = $authdb->token_get( 123 );
220             my %issued = $authdb->token_get($username, $jti);
221              
222             Returns the token's metadata by id or pair - username and jti
223             By default (without specified arguments) this method returns list of all tokens
224              
225             =head2 token_set
226              
227             $authdb->token_set(
228             type => 'api',
229             jti => $jti,
230             username => $username,
231             clientid => 'qwertyuiqwertyui',
232             iat => time,
233             exp => time + 3600,
234             address => '127.0.0.1',
235             ) or die($authdb->error);
236              
237             Adds new token to database
238              
239             $authdb->token_set(
240             id => 123,
241             type => 'api',
242             jti => $jti,
243             username => $username,
244             clientid => 'qwertyuiqwertyui',
245             iat => time,
246             exp => time + 3600,
247             address => '127.0.0.1',
248             ) or die($authdb->error);
249              
250             Updates token's data by id
251              
252             =head2 user_del
253              
254             $authdb->user_del( "admin" ) or die $authdb->error;
255              
256             Delete user by username
257              
258             =head2 user_edit
259              
260             $authdb->user_edit(
261             username => $username,
262             comment => $comment,
263             email => $email,
264             name => $name,
265             role => $role,
266             ) or die($authdb->error);
267              
268             Edit general user data only
269              
270             =head2 user_get
271              
272             my %data = $authdb->user_get( "admin" );
273             my @users = $authdb->user_get;
274              
275             This method returns user's data or returns all users as array of hashes
276              
277             =head2 user_groups
278              
279             my @groups = $authdb->user_groups( "admin" );
280              
281             This method returns all groups of the user
282              
283             =head2 user_passwd
284              
285             $authdb->user_passwd(
286             username => "admin",
287             password => "password",
288             ) or die $authdb->error;
289              
290             This method sets password for user
291              
292             =head2 user_pset
293              
294             $authdb->user_pset(
295             username => "foo",
296             name => "Test User",
297             email => 'test@localhost',
298             password => "098f6bcd4621d373cade4e832627b4f6",
299             algorithm => "MD5",
300             role => "Test user",
301             flags => 0,
302             not_before => time(),
303             not_after => undef,
304             public_key => "",
305             private_key => "",
306             attributes => qq/{"disabled": 0}/,
307             comment => "This user added for test",
308             ) or die $authdb->error;
309              
310             This method adds new user or doing update data of existing user in pure mode
311              
312             =head2 user_pure_set
313              
314             This method is deprecated! See L
315              
316             =head2 user_search
317              
318             my @users = $authdb->user_search( $text );
319              
320             This method performs search user by name fragment
321              
322             =head2 user_set
323              
324             $authdb->user_set(
325             username => "foo",
326             name => "Test User",
327             email => 'test@localhost',
328             password => "MyPassword", # Unsafe password
329             algorithm => "SHA256",
330             role => "Test user",
331             flags => 0,
332             not_before => time(),
333             not_after => undef,
334             public_key => "",
335             private_key => "",
336             attributes => qq/{"disabled": 0}/,
337             comment => "This user added for test",
338             ) or die $authdb->error;
339              
340             This method adds new user or doing update data of existing user
341              
342             =head2 user_setkeys
343              
344             $authdb->user_setkeys(
345             username => "foo",
346             public_key => $public_key,
347             private_key => $private_key,
348             ) or die $authdb->error;
349              
350             This method sets keys for user
351              
352             =head2 user_tokens
353              
354             my @tokens = $authdb->user_tokens( $username );
355              
356             This method returns all tokens of specified user
357              
358             =head2 ERROR CODES
359              
360             List of error codes describes in L
361              
362             =head1 HISTORY
363              
364             See C file
365              
366             =head1 TO DO
367              
368             See C file
369              
370             =head1 SEE ALSO
371              
372             L, L, L
373              
374             =head1 AUTHOR
375              
376             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
377              
378             =head1 COPYRIGHT
379              
380             Copyright (C) 1998-2026 D&D Corporation
381              
382             =head1 LICENSE
383              
384             This program is distributed under the terms of the Artistic License Version 2.0
385              
386             See the C file or L for details
387              
388             =cut
389              
390 1     1   174 use Mojo::Base -role;
  1         2  
  1         11  
391              
392 1     1   915 use Acrux::RefUtil qw/is_hash_ref is_array_ref is_true_flag/;
  1         3  
  1         118  
393              
394 1     1   9 use Mojo::Util qw/deprecated/;
  1         2  
  1         11318  
395              
396             # Meta CRUD
397             sub meta {
398 6     6 1 1564 my $self = shift;
399 6         14 my $i = scalar(@_);
400 6   50     25 my $key = shift // '';
401 6         16 my $val = shift;
402 6         61 $self->clean; # Flush error
403              
404             # No key specified
405 6 50       21 return $self->raise(400 => "E1330: No key specified") unless length($key);
406              
407             # Get model
408 6         23 my $model = $self->model;
409              
410             # get/set/del
411 6 100       30 if ($i == 1) { # get
    50          
412 2         14 my %kv = $model->meta_get($key);
413 2 50       79 return $self->raise(500 => "E1329: %s", $model->error) if $model->error;
414 2         37 return $kv{"value"};
415             } elsif ($i > 1) {
416 4 100       13 if (defined($val)) { # set
417 3 50 0     18 $model->meta_set(key => $key, value => $val)
418             or return $self->raise(500 => "E1331: %s", $model->error || 'Database request error (meta_set)');
419             } else { # del
420 1 50 0     8 $model->meta_del($key)
421             or return $self->raise(500 => "E1339: %s", $model->error || 'Database request error (meta_del)');
422             }
423             }
424              
425             # Ok
426 4         221 return 1;
427             }
428              
429             # User CRUD
430             sub user_set {
431 1     1 1 1552 my $self = shift;
432 1         17 my %data = @_;
433 1         8 $self->clean; # Flush error
434 1         3 my $now = time();
435              
436             # Get model
437 1         5 my $model = $self->model;
438              
439             # Get password
440 1 50       25 if (my $password = $data{password}) {
441 1   50     9 my $digest = $self->checksum($password, $data{algorithm} // '');
442 1 50       4 return $self->raise(400 => "E1332: Incorrect digest algorithm") unless $digest;
443 1         4 $data{password} = $digest;
444             }
445              
446             # Get old data from model
447 1         9 my %old = $model->user_get($data{username});
448 1 50       36 return $self->raise(500 => "E1333: %s", $model->error) if $model->error;
449              
450             # Set or add
451 1   50     21 $data{not_after} ||= 0;
452 1 50       5 if ($data{id}) { # Update (Set)
453 0   0     0 $data{password} ||= $old{password};
454 0 0 0     0 $model->user_set(%data)
455             or return $self->raise(500 => "E1351: %s", $model->error || 'Database request error (user_set)');
456             } else { # Insert (Add)
457 1 50       5 return $self->raise(400 => "E1334: User already exists") if $old{id};
458 1         5 $data{created} = $now;
459 1         4 $data{not_before} = $now;
460 1 50 0     10 $model->user_add(%data)
461             or return $self->raise(500 => "E1335: %s", $model->error || 'Database request error (user_add)');
462             }
463              
464             # Sets up the updated tag
465             #return $self->meta(sprintf("user.%s.updated", $data{username}), $now);
466              
467             # Ok
468 1         63 return 1;
469             }
470             sub user_pset {
471 7     7 1 361 my $self = shift;
472 7         121 my %data = @_;
473 7         73 $self->clean; # Flush error
474              
475             # Get model
476 7         34 my $model = $self->model;
477              
478             # Get current data from model
479 7         67 my %cur = $model->user_get($data{username});
480 7 50       264 return $self->raise(500 => "E1333: %s", $model->error) if $model->error;
481              
482             # Set or add
483 7 100       91 if ($cur{id}) { # Update (Set)
484 1 50 0     8 $model->user_set(%data)
485             or return $self->raise(500 => "E1351: %s", $model->error || 'Database request error (user_set)');
486             } else { # Insert (Add)
487 6 50 0     70 $model->user_add(%data)
488             or return $self->raise(500 => "E1335: %s", $model->error || 'Database request error (user_add)');
489             }
490              
491             # Sets up the updated tag
492             #return $self->meta(sprintf("user.%s.updated", $data{username}), time);
493              
494             # Ok
495 7         782 return 1;
496             }
497             sub user_edit {
498 0     0 1 0 my $self = shift;
499 0         0 my %data = @_;
500 0         0 $self->clean; # Flush error
501              
502             # Get model
503 0         0 my $model = $self->model;
504              
505             # Get old data from model
506 0         0 my %old = $model->user_get($data{username});
507 0 0       0 return $self->raise(500 => "E1333: %s", $model->error) if $model->error;
508 0 0       0 return $self->raise(400 => "E1336: User not found") unless $old{id};
509              
510             # Set new data
511             $model->user_edit(%data, id => $old{id})
512 0 0 0     0 or return $self->raise(500 => "E1337: %s", $model->error || 'Database request error (user_edit)');
513              
514             # Sets up the updated tag
515             #return $self->meta(sprintf("user.%s.updated", $data{username}), time);
516              
517             # Ok
518 0         0 return 1;
519             }
520             sub user_get {
521 2     2 1 4 my $self = shift;
522 2   100     10 my $username = shift // '';
523 2         10 $self->clean; # Flush error
524              
525             # Get model
526 2         6 my $model = $self->model;
527              
528             # Get all users
529 2 100       7 unless (length($username)) {
530 1         6 my @table = $model->user_getall;
531 1 50       74 $self->raise(500 => "E1338: %s", $model->error) if $model->error;
532 1         18 return @table;
533             }
534              
535             # Get user data
536 1         6 my %data = $model->user_get($username);
537 1 50       44 $self->raise(500 => "E1333: %s", $model->error) if $model->error;
538 1         18 return %data;
539             }
540             sub user_del {
541 1     1 1 366 my $self = shift;
542 1         2 my $username = shift;
543 1         5 $self->clean; # Flush error
544              
545             # Get model
546 1         2 my $model = $self->model;
547              
548             # Delete user
549 1 50 0     5 $model->user_del($username)
550             or return $self->raise(500 => "E1340: %s", $model->error || 'Database request error (user_del)');
551              
552             # Delete all group relations
553 1 50 0     53 $model->grpusr_del(username => $username)
554             or return $self->raise(500 => "E1341: %s", $model->error || 'Database request error (grpusr_del)');
555              
556             # Sets up the updated tag
557             #return $self->meta(sprintf("user.%s.updated", $username), time);
558              
559             # Ok
560 1         131 return 1;
561             }
562             sub user_search {
563 0     0 1 0 my $self = shift;
564 0         0 my $username = shift;
565 0         0 $self->clean; # Flush error
566              
567             # Get model
568 0         0 my $model = $self->model;
569              
570             # Get data from model
571 0         0 my @table = $model->user_search($username);
572 0 0       0 $self->raise(500 => "E1342: %s", $model->error) if $model->error;
573 0         0 return @table;
574             }
575             sub user_groups {
576 0     0 1 0 my $self = shift;
577 0         0 my $username = shift;
578 0         0 $self->clean; # Flush error
579              
580             # Get model
581 0         0 my $model = $self->model;
582              
583             # Get groups list of user
584 0         0 my @groups = $model->user_groups( $username );
585 0 0       0 $self->raise(500 => "E1343: %s", $model->error) if $model->error;
586              
587 0         0 return @groups;
588             }
589             sub user_passwd {
590 1     1 1 3 my $self = shift;
591 1         7 my %data = @_;
592 1         9 $self->clean; # Flush error
593              
594             # Get model
595 1         4 my $model = $self->model;
596              
597             # Get old data from model
598 1         8 my %old = $model->user_get($data{username});
599 1 50       72 return $self->raise(500 => "E1333: %s", $model->error) if $model->error;
600 1 50       15 return $self->raise(400 => "E1336: User not found") unless $old{id};
601              
602             # Get password
603 1 50       7 if (my $password = $data{password}) {
604 1         8 my $digest = $self->checksum($password, $old{algorithm});
605 1 50       5 return $self->raise(400 => "E1332: Incorrect digest algorithm") unless $digest;
606 1         4 $data{password} = $digest;
607             } else {
608 0         0 return $self->raise(400 => "E1344: No password specified");
609             }
610              
611             # Set new password
612 1 50 0     7 $model->user_passwd(%data)
613             or return $self->raise(500 => "E1345: %s", $model->error || 'Database request error (user_passwd)');
614              
615             # Sets up the updated tag
616             #return $self->meta(sprintf("user.%s.updated", $data{username}), time);
617              
618             # Ok
619 1         47 return 1;
620             }
621             sub user_setkeys {
622 1     1 1 5 my $self = shift;
623 1         8 my %data = @_;
624 1         11 $self->clean; # Flush error
625              
626             # Get model
627 1         5 my $model = $self->model;
628              
629             # Get old data from model
630 1         7 my %old = $model->user_get($data{username});
631 1 50       44 return $self->raise(500 => "E1333: %s", $model->error) if $model->error;
632 1 50       10 return $self->raise(400 => "E1336: User not found") unless $old{id};
633              
634             # Set new keys
635             $model->user_setkeys(%data, id => $old{id})
636 1 50 0     5 or return $self->raise(500 => "E1346: %s", $model->error || 'Database request error (user_setkeys)');
637              
638             # Sets up the updated tag
639             #return $self->meta(sprintf("user.%s.updated", $data{username}), time);
640              
641             # Ok
642 1         113 return 1;
643             }
644             sub user_tokens {
645 0     0 1 0 my $self = shift;
646 0   0     0 my $username = shift // '';
647 0         0 $self->clean; # Flush error
648              
649             # Get model
650 0         0 my $model = $self->model;
651              
652             # Get user tokens
653 0         0 my @table = $model->user_tokens($username);
654 0 0       0 $self->raise(500 => "E1347: %s", $model->error) if $model->error;
655              
656 0         0 return @table;
657             }
658              
659             # Group CRUD
660             sub group_set {
661 1     1 1 1573 my $self = shift;
662 1         7 my %data = @_;
663 1         10 $self->clean; # Flush error
664              
665             # Get model
666 1         5 my $model = $self->model;
667              
668             # Get old data from model
669 1         9 my %old = $model->group_get($data{groupname});
670 1 50       31 return $self->raise(500 => "E1348: %s", $model->error) if $model->error;
671              
672             # Set or add group data
673 1 50       13 if ($data{id}) { # Update (Set)
674 0 0 0     0 $model->group_set(%data)
675             or return $self->raise(500 => "E1353: %s", $model->error || 'Database request error (group_set)');
676             } else { # Insert (Add)
677 1 50       4 return $self->raise(400 => "E1349: Group already exists") if $old{id};
678 1 50 0     7 $model->group_add(%data)
679             or return $self->raise(500 => "E1350: %s", $model->error || 'Database request error (group_add)');
680             }
681              
682             # Set users
683 1   50     37 my $users = $data{users} || [];
684             $model->grpusr_del( groupname => $data{groupname} )
685 1 50 0     6 or return $self->raise(500 => "E1341: %s", $model->error || 'Database request error (grpusr_del)');
686 1         32 foreach my $username (@$users) {
687 0 0 0     0 $model->grpusr_add(groupname => $data{groupname}, username => $username)
688             or return $self->raise(500 => "E1352: %s", $model->error || 'Database request error (grpusr_add)');
689             #$self->meta(sprintf("user.%s.updated", $username), time);
690             }
691 1 50 0     6 $model->group_set(%data)
692             or return $self->raise(500 => "E1353: %s", $model->error || 'Database request error (group_set)');
693              
694             # Sets up the updated tag
695             #return $self->meta(sprintf("group.%s.updated", $data{groupname}), time);
696              
697             # Ok
698 1         22 return 1;
699             }
700             sub group_pset {
701 5     5 1 17 my $self = shift;
702 5         27 my %data = @_;
703 5         49 $self->clean; # Flush error
704              
705             # Get model
706 5         26 my $model = $self->model;
707              
708             # Get current data from model
709 5         34 my %cur = $model->group_get($data{groupname});
710 5 50       183 return $self->raise(500 => "E1348: %s", $model->error) if $model->error;
711              
712             # Set or add
713 5 50       62 if ($cur{id}) { # Update (Set)
714 0 0 0     0 $model->group_set(%data)
715             or return $self->raise(500 => "E1353: %s", $model->error || 'Database request error (group_set)');
716             } else { # Insert (Add)
717 5 50 0     61 $model->group_add(%data)
718             or return $self->raise(500 => "E1350: %s", $model->error || 'Database request error (group_add)');
719             }
720              
721             # Sets up the updated tag
722             #return $self->meta(sprintf("group.%s.updated", $data{groupname}), time);
723              
724             # Ok
725 5         341 return 1;
726             }
727             sub group_get {
728 2     2 1 3 my $self = shift;
729 2   100     8 my $groupname = shift // '';
730 2         11 $self->clean; # Flush error
731              
732             # Get model
733 2         5 my $model = $self->model;
734              
735             # Get all groups
736 2 100       8 unless (length($groupname)) {
737 1         6 my @table = $model->group_getall;
738 1 50       30 $self->raise(500 => "E1355: %s", $model->error) if $model->error;
739 1         10 return @table;
740             }
741              
742             # Get group data
743 1         4 my %data = $model->group_get($groupname);
744 1 50       30 $self->raise(500 => "E1348: %s", $model->error) if $model->error;
745 1         12 return %data;
746             }
747             sub group_del {
748 1     1 1 571 my $self = shift;
749 1         3 my $groupname = shift;
750 1         6 $self->clean; # Flush error
751              
752             # Get model
753 1         4 my $model = $self->model;
754              
755             # Delete group
756 1 50 0     26 $model->group_del($groupname)
757             or return $self->raise(500 => "E1356: %s", $model->error || 'Database request error (group_del)');
758              
759             # Delete all user relations
760 1 50 0     47 $model->grpusr_del(groupname => $groupname)
761             or return $self->raise(500 => "E1341: %s", $model->error || 'Database request error (grpusr_del)');
762              
763             # Sets up the updated tag
764             #return $self->meta(sprintf("group.%s.updated", $groupname), time);
765              
766             # Ok
767 1         35 return 1;
768             }
769             sub group_enroll {
770 14     14 1 38 my $self = shift;
771 14         113 my %data = @_;
772 14         152 $self->clean; # Flush error
773              
774             # Get model
775 14         74 my $model = $self->model;
776              
777             # Get existed relation
778 14         109 my %old = $model->grpusr_get(%data);
779 14 50       461 return $self->raise(500 => "E1357: %s", $model->error) if $model->error;
780 14 50       157 return 1 if $old{id};
781              
782             # Enroll
783 14 50 0     120 $model->grpusr_add(%data)
784             or return $self->raise(500 => "E1352: %s", $model->error || 'Database request error (grpusr_add)');
785              
786             # Sets up the updated tag
787             #$self->meta(sprintf("user.%s.updated", $data{username}), time);
788             #return 0 unless $model->status;
789             #$self->meta(sprintf("group.%s.updated", $data{groupname}), time);
790             #return 0 unless $model->status;
791              
792             # Ok
793 14         783 return 1;
794             }
795             sub group_members {
796 6     6 1 387 my $self = shift;
797 6         8 my $groupname = shift;
798 6         20 $self->clean; # Flush error
799              
800             # Get model
801 6         12 my $model = $self->model;
802              
803             # Get users list of group
804 6         38 my @members = $model->group_members( $groupname );
805 6 50       171 $self->raise(500 => "E1358: %s", $model->error) if $model->error;
806              
807 6         55 return @members;
808             }
809              
810             # Realm CRUD
811             sub realm_set {
812 1     1 1 1616 my $self = shift;
813 1         9 my %data = @_;
814 1         28 $self->clean; # Flush error
815              
816             # Get model
817 1         6 my $model = $self->model;
818              
819             # Get old data from model
820 1         10 my %old = $model->realm_get($data{realmname});
821 1 50       33 return $self->raise(500 => "E1359: %s", $model->error) if $model->error;
822              
823             # Set or add realm data
824 1 50       13 if ($data{id}) { # Update (Set)
825 0 0 0     0 $model->realm_set(%data)
826             or return $self->raise(500 => "E1366: %s", $model->error || 'Database request error (realm_set)');
827             } else { # Insert (Add)
828 1 50       4 return $self->raise(400 => "E1360: Realm already exists") if $old{id};
829 1 50 0     8 $model->realm_add(%data)
830             or return $self->raise(500 => "E1361: %s", $model->error || 'Database request error (realm_add)');
831             }
832              
833             # Set routes
834 1   50     72 my $routes = $data{routes} || [];
835             $model->route_release( $data{realmname} )
836 1 50 0     11 or return $self->raise(500 => "E1362: %s", $model->error || 'Database request error (route_release)');
837 1         35 foreach my $routename (@$routes) {
838             $model->route_assign(routename => $routename, realmname => $data{realmname})
839 0 0 0     0 or return $self->raise(500 => "E1363: %s", $model->error || 'Database request error (route_assign)');
840             }
841              
842             # Set requirements
843 1   50     13 my $requirements = $data{requirements} || [];
844             $model->realm_requirement_del( $data{realmname} )
845 1 50 0     10 or return $self->raise(500 => "E1364: %s", $model->error || 'Database request error (realm_requirement_del)');
846 1         32 foreach my $req (@$requirements) {
847 0 0       0 next unless is_hash_ref($req);
848             $model->realm_requirement_add(%$req, realmname => $data{realmname})
849 0 0 0     0 or return $self->raise(500 => "E1365: %s", $model->error || 'Database request error (realm_requirement_add)');
850             }
851              
852             # Sets up the updated tag
853             #return $self->meta(sprintf("realm.%s.updated", $data{realmname}), time);
854              
855             # Ok
856 1         14 return 1;
857             }
858             sub realm_pset {
859 1     1 1 3 my $self = shift;
860 1         9 my %data = @_;
861 1         8 $self->clean; # Flush error
862              
863             # Get model
864 1         5 my $model = $self->model;
865              
866             # Get current data from model
867 1         8 my %cur = $model->realm_get($data{realmname});
868 1 50       24 return $self->raise(500 => "E1359: %s", $model->error) if $model->error;
869              
870             # Set or add
871 1 50       12 if ($cur{id}) { # Update (Set)
872 0 0 0     0 $model->realm_set(%data)
873             or return $self->raise(500 => "E1366: %s", $model->error || 'Database request error (realm_set)');
874             } else { # Insert (Add)
875 1 50 0     7 $model->realm_add(%data)
876             or return $self->raise(500 => "E1361: %s", $model->error || 'Database request error (realm_add)');
877             }
878              
879             # Sets up the updated tag
880             #return $self->meta(sprintf("realm.%s.updated", $data{realmname}), time);
881              
882             # Ok
883 1         36 return 1;
884             }
885             sub realm_get {
886 2     2 1 7 my $self = shift;
887 2   100     11 my $realmname = shift // '';
888 2         14 $self->clean; # Flush error
889              
890             # Get model
891 2         9 my $model = $self->model;
892              
893             # Get all realms
894 2 100       9 unless (length($realmname)) {
895 1         5 my @table = $model->realm_getall;
896 1 50       25 $self->raise(500 => "E1367: %s", $model->error) if $model->error;
897 1         8 return @table;
898             }
899              
900             # Get realm data
901 1         8 my %data = $model->realm_get($realmname);
902 1 50       68 $self->raise(500 => "E1359: %s", $model->error) if $model->error;
903              
904 1         24 return %data;
905             }
906             sub realm_del {
907 1     1 1 564 my $self = shift;
908 1         4 my $realmname = shift;
909 1         7 $self->clean; # Flush error
910              
911             # Get model
912 1         5 my $model = $self->model;
913              
914             # Delete realm
915 1 50 0     9 $model->realm_del($realmname)
916             or return $self->raise(500 => "E1368: %s", $model->error || 'Database request error (realm_del)');
917              
918             # Delete realm's requirements
919 1 50 0     72 $model->realm_requirement_del($realmname)
920             or return $self->raise(500 => "E1364: %s", $model->error || 'Database request error (realm_requirement_del)');
921              
922             # Release all related routes
923 1 50 0     28 $model->route_release($realmname)
924             or return $self->raise(500 => "E1362: %s", $model->error || 'Database request error (route_release)');
925              
926             # Sets up the updated tag
927             #return $self->meta(sprintf("realm.%s.updated", $realmname), time);
928              
929             # Ok
930 1         95 return 1;
931             }
932             sub realm_requirements {
933 1     1 1 2 my $self = shift;
934 1   50     20 my $realmname = shift // '';
935 1         4 $self->clean; # Flush error
936              
937             # Get model
938 1         3 my $model = $self->model;
939              
940             # Get realm requirements
941 1         4 my @table = $model->realm_requirements($realmname);
942 1 50       28 $self->raise(500 => "E1371: %s", $model->error) if $model->error;
943              
944 1         8 return @table;
945             }
946             sub realm_routes {
947 0     0 1 0 my $self = shift;
948 0   0     0 my $realmname = shift // '';
949 0         0 $self->clean; # Flush error
950              
951             # Get model
952 0         0 my $model = $self->model;
953              
954             # Get realm routes
955 0         0 my @table = $model->realm_routes($realmname);
956 0 0       0 $self->raise(500 => "E1372: %s", $model->error) if $model->error;
957              
958 0         0 return @table;
959             }
960              
961             # Route CRUD
962             sub route_set {
963 1     1 1 1074 my $self = shift;
964 1         12 my %data = @_;
965 1         10 $self->clean; # Flush error
966              
967             # Get model
968 1         5 my $model = $self->model;
969              
970             # Get old data from model
971 1         11 my %old = $model->route_get($data{routename});
972 1 50       41 return $self->raise(500 => "E1373: %s", $model->error) if $model->error;
973              
974             # Set or add route data
975 1 50       12 if ($data{id}) { # Update (Set)
976 0 0 0     0 $model->route_set(%data)
977             or return $self->raise(500 => "E1375: %s", $model->error || 'Database request error (route_set)');
978             } else { # Insert (Add)
979 1 50       5 return $self->raise(400 => "E1374: Route already exists") if $old{id};
980 1 50 0     7 $model->route_add(%data)
981             or return $self->raise(500 => "E1370: %s", $model->error || 'Database request error (route_add)');
982             }
983              
984             # Sets up the updated tag
985             #return $self->meta(sprintf("routes.%s.updated", $data{base} // '__default'), time);
986              
987             # Ok
988 1         84 return 1;
989             }
990             sub route_pset {
991 0     0 1 0 my $self = shift;
992 0         0 my %data = @_;
993 0         0 $self->clean; # Flush error
994              
995             # Get model
996 0         0 my $model = $self->model;
997              
998             # Get current data from model
999 0         0 my %cur = $model->route_get($data{routename});
1000 0 0       0 return $self->raise(500 => "E1373: %s", $model->error) if $model->error;
1001              
1002             # Set or add
1003 0 0       0 if ($cur{id}) { # Update (Set)
1004 0         0 $data{id} = $cur{id};
1005 0 0 0     0 $model->route_set(%data)
1006             or return $self->raise(500 => "E1375: %s", $model->error || 'Database request error (route_set)');
1007             } else { # Insert (Add)
1008 0 0 0     0 $model->route_add(%data)
1009             or return $self->raise(500 => "E1370: %s", $model->error || 'Database request error (route_add)');
1010             }
1011              
1012             # Sets up the updated tag
1013             #return $self->meta(sprintf("routes.%s.updated", $data{base} // '__default'), time);
1014              
1015             # Ok
1016 0         0 return 1;
1017             }
1018             sub route_get {
1019 2     2 1 6 my $self = shift;
1020 2   100     10 my $routename = shift // '';
1021 2         15 $self->clean; # Flush error
1022              
1023             # Get model
1024 2         9 my $model = $self->model;
1025              
1026             # Get all routes
1027 2 100       9 unless (length($routename)) {
1028 1         5 my @table = $model->route_getall();
1029 1 50       39 $self->raise(500 => "E1376: %s", $model->error) if $model->error;
1030 1         8 return @table;
1031             }
1032              
1033             # Get route data
1034 1         9 my %data = $model->route_get($routename);
1035 1 50       63 $self->raise(500 => "E1373: %s", $model->error) if $model->error;
1036              
1037 1         21 return %data;
1038             }
1039             sub route_del {
1040 1     1 1 674 my $self = shift;
1041 1         4 my $routename = shift;
1042 1         8 $self->clean; # Flush error
1043              
1044             # Get model
1045 1         6 my $model = $self->model;
1046              
1047             # Delete route
1048 1 50 0     9 $model->route_del($routename)
1049             or return $self->raise(500 => "E1377: %s", $model->error || 'Database request error (route_del)');
1050              
1051             # Ok
1052 1         65 return 1;
1053             }
1054             sub route_search {
1055 0     0 1 0 my $self = shift;
1056 0         0 my $text = shift;
1057 0         0 $self->clean; # Flush error
1058              
1059             # Get model
1060 0         0 my $model = $self->model;
1061              
1062             # Get data from model
1063 0         0 my @table = $model->route_search($text);
1064 0 0       0 $self->raise(500 => "E1378: %s", $model->error) if $model->error;
1065              
1066 0         0 return @table;
1067             }
1068              
1069             # Token CRUD
1070             sub token_set {
1071 1     1 1 1122 my $self = shift;
1072 1         16 my %data = @_;
1073 1         12 $self->clean; # Flush error
1074 1   50     23 $data{type} //= 'session';
1075              
1076             # Get model
1077 1         8 my $model = $self->model;
1078              
1079             # Delete expired tokens
1080 1 50 0     13 $model->token_del
1081             or return $self->raise(500 => "E1379: %s", $model->error || 'Database request error (token_del)');
1082              
1083             # Get old data from model
1084 1         27 my %old;
1085 1 50       8 if ($data{id}) {
    50          
1086 0         0 %old = $model->token_get($data{id});
1087 0 0       0 return $self->raise(500 => "E1380: %s", $model->error) if $model->error;
1088             } elsif ($data{type} eq 'session') {
1089 0         0 %old = $model->token_get_cond('session', %data);
1090 0 0       0 return $self->raise(500 => "E1381: %s", $model->error) if $model->error;
1091             }
1092              
1093             # Set or add data
1094 1 50       5 if ($old{id}) { # Update (Set)
1095 0         0 $data{id} = $old{id};
1096 0 0 0     0 $model->token_set(%data)
1097             or return $self->raise(500 => "E1382: %s", $model->error || 'Database request error (token_set)');
1098             } else { # Insert (Add)
1099 1 50 0     7 $model->token_add(%data)
1100             or return $self->raise(500 => "E1369: %s", $model->error || 'Database request error (token_add)');
1101             }
1102              
1103             # Ok
1104 1         82 return 1;
1105             }
1106             sub token_get {
1107 1     1 1 2 my $self = shift;
1108 1         3 my ($id, $username, $jti);
1109 1 50       6 if (scalar(@_) == 1) { $id = shift }
  0 50       0  
1110 0         0 elsif (scalar(@_) == 2) {($username, $jti) = @_}
1111 1         11 $self->clean; # Flush error
1112              
1113             # Get model
1114 1         4 my $model = $self->model;
1115              
1116             # Get data from model
1117 1         3 my @data = ();
1118 1 50       5 if ($id) {
    50          
1119 0         0 @data = $model->token_get($id); # hash returs
1120 0 0       0 $self->raise(500 => "E1380: %s", $model->error) if $model->error;
1121             } elsif ($jti) {
1122 0         0 @data = $model->token_get_cond('api', username => $username, jti => $jti); # hash returs
1123 0 0       0 $self->raise(500 => "E1381: %s", $model->error) if $model->error;
1124             } else {
1125 1         6 @data = $model->token_getall(); # table returs
1126 1 50       35 $self->raise(500 => "E1383: %s", $model->error) if $model->error;
1127             }
1128              
1129 1         24 return @data;
1130             }
1131             sub token_del {
1132 1     1 1 479 my $self = shift;
1133 1   50     5 my $username = shift // '';
1134 1   50     6 my $jti = shift // '';
1135 1         5 $self->clean; # Flush error
1136              
1137             # Get model
1138 1         3 my $model = $self->model;
1139              
1140             # Get data from model
1141 1         7 my %data = $model->token_get_cond('api', username => $username, jti => $jti);
1142 1 50       36 return $self->raise(500 => "E1381: %s", $model->error) if $model->error;
1143 1 50       8 return 1 unless $data{id};
1144              
1145             # Delete token
1146             $model->token_del($data{id})
1147 1 50 0     7 or return $self->raise(500 => "E1379: %s", $model->error || 'Database request error (token_del)');
1148              
1149             # Ok
1150 1         74 return 1;
1151             }
1152             sub token_check {
1153 0     0 1 0 my $self = shift;
1154 0   0     0 my $username = shift // '';
1155 0   0     0 my $jti = shift // '';
1156 0         0 $self->clean; # Flush error
1157              
1158             # Get model
1159 0         0 my $model = $self->model;
1160              
1161             # Get data from model
1162 0         0 my %data = $model->token_get_cond('api', username => $username, jti => $jti);
1163 0 0 0     0 return $self->raise(500 => "E1381: %s", $model->error) // 0 if $model->error;
1164              
1165             # Check
1166 0 0       0 return 1 if $data{id};
1167 0         0 return 0;
1168             }
1169              
1170             # Working with dumps
1171             sub import_data {
1172 1     1 1 4 my $self = shift;
1173 1         5 my $file = shift;
1174 1         12 $self->clean; # Flush error
1175 1         7 my $model = $self->model;
1176 1         3 my $now = time();
1177              
1178             # Get data struct from file
1179 1 50       7 if ($file) {
1180 0         0 $self->load($file);
1181 0 0       0 if ($self->error) {
1182 0         0 $self->code(500);
1183 0         0 return;
1184             }
1185             }
1186 1         8 my $data = $self->data; # Perl struct expected!
1187              
1188             # Get users
1189 1   50     13 my $users_array = $data->{"users"} // [];
1190 1 50       28 $users_array = [] unless is_array_ref($users_array);
1191 1         12 my %grpsusrs = ();
1192 1         5 foreach my $user (@$users_array) {
1193 6 50       32 next unless is_hash_ref($user);
1194 6   50     62 my $username = $user->{"username"} // '';
1195 6 50       27 next unless length($username);
1196              
1197             # Add user
1198             $self->user_pset(
1199             username => $username,
1200             name => $user->{"name"} // '',
1201             email => $user->{"email"} // '',
1202             password => $user->{"password"} // '',
1203             algorithm => $user->{"algorithm"} // '',
1204             role => $user->{"role"} // '',
1205             flags => $user->{"flags"} || 0,
1206             created => $now,
1207             not_before => $now,
1208             not_after => is_true_flag($user->{"disabled"}) ? $now : undef,
1209             public_key => $user->{"public_key"} // '',
1210             private_key => $user->{"private_key"} // '',
1211             attributes => $user->{"attributes"} // '',
1212 6 100 50     206 comment => $user->{"comment"} // '',
    50 50        
      50        
      50        
      50        
      50        
      50        
      50        
      50        
      50        
1213             ) or return;
1214              
1215             # Add groups to grpsusrs
1216 6   100     58 my $groups = $user->{"groups"} || [];
1217 6 50       59 $groups = [] unless is_array_ref($groups);
1218 6         64 foreach my $g (@$groups) {
1219 8         64 $grpsusrs{"$g:$username"} = {
1220             groupname => $g,
1221             username => $username,
1222             };
1223             }
1224             }
1225              
1226             # Get groups
1227 1   50     7 my $groups_array = $data->{"groups"} // [];
1228 1 50       4 $groups_array = [] unless is_array_ref($groups_array);
1229 1         7 foreach my $group (@$groups_array) {
1230 5 50       19 next unless is_hash_ref($group);
1231 5   50     46 my $groupname = $group->{"groupname"} // '';
1232 5 50       18 next unless length($groupname);
1233              
1234             # Add group
1235             $self->group_pset(
1236             groupname => $groupname,
1237 5 50 50     45 description => $group->{"description"} // '',
1238             ) or return;
1239              
1240             # Add users to grpsusrs
1241 5   100     46 my $users = $group->{"users"} || [];
1242 5 50       33 $users = [] unless is_array_ref($users);
1243 5         51 foreach my $u (@$users) {
1244 8         67 $grpsusrs{"$groupname:$u"} = {
1245             groupname => $groupname,
1246             username => $u,
1247             };
1248             }
1249             }
1250              
1251             # Add members to group
1252 1         8 foreach my $member (values %grpsusrs) {
1253 14 50       140 $self->group_enroll(%$member) or return;
1254             }
1255              
1256             # Get realms
1257 1   50     8 my $realms_array = $data->{"realms"} // [];
1258 1 50       7 $realms_array = [] unless is_array_ref($realms_array);
1259 1         8 foreach my $realm (@$realms_array) {
1260 1 50       17 next unless is_hash_ref($realm);
1261 1   50     9 my $realmname = $realm->{"realmname"} // '';
1262 1 50       3 next unless length($realmname);
1263              
1264             # Add realm
1265             $self->realm_pset(
1266             realmname => $realmname,
1267             realm => $realm->{"realm"} // '',
1268             satisfy => $realm->{"satisfy"} // '',
1269 1 50 50     16 description => $realm->{"description"} // '',
      50        
      50        
1270             ) or return;
1271              
1272             # Delete all current requirements from realm
1273 1 50 0     6 $model->realm_requirement_del($realmname)
1274             or return $self->raise(500 => "E1364: %s", $model->error || 'Database request error (realm_requirement_del)');
1275              
1276             # Set requirements
1277 1   50     27 my $requirements = $realm->{"requirements"} || [];
1278 1 50       4 $requirements = [] unless is_array_ref($requirements);
1279 1         7 foreach my $req (@$requirements) {
1280 2 50       36 next unless is_hash_ref($req);
1281 2 50 0     21 $model->realm_requirement_add(%$req, realmname => $realmname)
1282             or return $self->raise(500 => "E1365: %s", $model->error || 'Database request error (realm_requirement_add)');
1283             }
1284              
1285             # Release all routes for realm
1286 1 50 0     63 $model->route_release($realmname)
1287             or return $self->raise(500 => "E1362: %s", $model->error || 'Database request error (route_release)');
1288             }
1289              
1290             # Get routes
1291 1   50     31 my $routes_array = $data->{"routes"} // [];
1292 1 50       4 $routes_array = [] unless is_array_ref($routes_array);
1293 1         7 foreach my $route (@$routes_array) {
1294 0 0       0 next unless is_hash_ref($route);
1295 0   0     0 my $routename = $route->{"routename"} // '';
1296 0 0       0 next unless length($routename);
1297              
1298             # Add route
1299             $self->route_pset(
1300             routename => $routename,
1301             realmname => $route->{"realmname"} // '',
1302             method => $route->{"method"} // '',
1303             url => $route->{"url"} // '',
1304             base => $route->{"base"} // '',
1305 0 0 0     0 path => $route->{"path"} // '',
      0        
      0        
      0        
      0        
1306             ) or return;
1307             }
1308              
1309             # Get meta
1310 1   50     7 my $meta_hash = $data->{"meta"} // {};
1311 1 50       5 $meta_hash = {} unless is_hash_ref($meta_hash);
1312 1         8 while (my ($k, $v) = each %$meta_hash) {
1313 0 0 0     0 last unless (defined($k) && length($k));
1314 0 0       0 $self->meta($k, $v) or return;
1315 0         0 delete $meta_hash->{$k}; # This is safe
1316             }
1317              
1318             # Save status data to meta
1319 1 50 33     28 $self->meta("data.file" => $file || $self->sourcefile) or return;
1320 1 50       7 $self->meta("data.inited" => $now) or return;
1321              
1322             # Ok
1323 1         31 return 1;
1324             }
1325             sub export_data {
1326 1     1 1 3 my $self = shift;
1327 1         2 my $file = shift;
1328 1         6 $self->clean; # Flush error
1329 1         3 my $model = $self->model;
1330 1         2 my $now = time();
1331              
1332             # Get users
1333 1         6 my @users = $self->user_get();
1334 1 50       118 return if $self->error;
1335 1         12 foreach my $u (@users) {
1336 6   100     14 my $not_after = $u->{not_after} || 0;
1337 6 100 66     17 $u->{disabled} = ($not_after && $not_after < $now) ? 'yes' : 'no';
1338 6         17 delete($u->{$_}) for qw/created id not_before not_after/;
1339             }
1340              
1341             # Get groups
1342 1         6 my @groups = $self->group_get();
1343 1 50       6 return if $self->error;
1344 1         7 foreach my $g (@groups) {
1345 5   50     13 my $groupname = $g->{groupname} // '';
1346 5 50       10 next unless length $groupname;
1347 5         10 delete($g->{id});
1348              
1349             # Get members
1350 5         11 my @members = $self->group_members($groupname);
1351 5 50       18 return if $self->error;
1352 5         27 my @usr = ();
1353 5         9 foreach my $m (@members) {
1354 13         25 push @usr, $m->{username};
1355             }
1356 5         25 $g->{users} = [@usr];
1357             }
1358              
1359             # Get realms
1360 1         5 my @realms = $self->realm_get();
1361 1 50       5 return if $self->error;
1362 1         5 foreach my $r (@realms) {
1363 1   50     4 my $realmname = $r->{realmname} // '';
1364 1 50       3 next unless length $realmname;
1365 1         3 delete($r->{id});
1366              
1367             # Get requirements
1368 1         4 my @requirements = $self->realm_requirements($realmname);
1369 1 50       6 return if $self->error;
1370 1         5 my @reqs = ();
1371 1         3 foreach my $q (@requirements) {
1372 2         4 delete($q->{id});
1373 2         3 delete($q->{realmname});
1374 2         4 push @reqs, $q;
1375             }
1376 1         3 $r->{requirements} = [@reqs];
1377             }
1378              
1379             # Get routes
1380 1         6 my @routes = $self->route_get();
1381 1 50       4 return if $self->error;
1382 1         5 foreach my $r (@routes) {
1383 0         0 delete($r->{id});
1384             }
1385              
1386             # Get meta
1387 1         4 my @metas = $model->meta_get();
1388 1 50       25 return $self->raise(500 => "E1329: %s", $model->error) if $model->error;
1389 1         6 my %meta = ();
1390 1         3 foreach my $m (@metas) {
1391 3         8 $meta{$m->{key}} = $m->{value};
1392             }
1393             #print Mojo::Util::dumper(\%meta);
1394              
1395             # Store data
1396             $self->data({
1397 1         9 users => \@users,
1398             groups => \@groups,
1399             realms => \@realms,
1400             routes => \@routes,
1401             meta => \%meta,
1402             });
1403 1 50       71 if ($file) {
1404 1         7 $self->save($file);
1405 1 50       6 if ($self->error) {
1406 0         0 $self->code(500);
1407 0         0 return;
1408             }
1409             }
1410              
1411             # Ok
1412 1         14 return 1;
1413             }
1414              
1415             # Deprecated methods
1416             sub user_pure_set {
1417 0     0 1   deprecated 'The "WWW::Suffit::AuthDB::user_pure_set" is deprecated in favor of "user_pset"';
1418 0           goto &user_pset;
1419             }
1420             sub group_pure_set {
1421 0     0 1   deprecated 'The "WWW::Suffit::AuthDB::group_pure_set" is deprecated in favor of "group_pset"';
1422 0           goto &group_pset;
1423             }
1424             sub realm_pure_set {
1425 0     0 1   deprecated 'The "WWW::Suffit::AuthDB::realm_pure_set" is deprecated in favor of "realm_pset"';
1426 0           goto &realm_pset;
1427             }
1428             sub route_pure_set {
1429 0     0 1   deprecated 'The "WWW::Suffit::AuthDB::route_pure_set" is deprecated in favor of "route_pset"';
1430 0           goto &route_pset;
1431             }
1432              
1433             1;
1434              
1435             __END__