File Coverage

blib/lib/WWW/Suffit/AuthDB/Model.pm
Criterion Covered Total %
statement 500 678 73.7
branch 167 410 40.7
condition 49 133 36.8
subroutine 109 123 88.6
pod 55 55 100.0
total 880 1399 62.9


line stmt bran cond sub pod time code
1             package WWW::Suffit::AuthDB::Model;
2 4     4   92133 use strict;
  4         10  
  4         168  
3 4     4   373 use utf8;
  4         211  
  4         43  
4              
5             =encoding utf8
6              
7             =head1 NAME
8              
9             WWW::Suffit::AuthDB::Model - WWW::Suffit::AuthDB model class
10              
11             =head1 SYNOPSIS
12              
13             use WWW::Suffit::AuthDB::Model;
14              
15             # SQLite
16             my $model = WWW::Suffit::AuthDB::Model->new(
17             "sqlite:///tmp/test.db?RaiseError=0&PrintError=0&sqlite_unicode=1"
18             );
19              
20             # MySQL
21             my $model = WWW::Suffit::AuthDB::Model->new(
22             "mysql://user:pass@host/authdb?mysql_auto_reconnect=1&mysql_enable_utf8=1"
23             );
24              
25             die($model->error) if $model->error;
26              
27             =head1 DESCRIPTION
28              
29             This module provides model methods
30              
31             =head2 SQLITE DDL
32              
33             CREATE TABLE IF NOT EXISTS "users" (
34             "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
35             "username" CHAR(64) NOT NULL UNIQUE, -- User name
36             "name" CHAR(255) DEFAULT NULL, -- Full user name
37             "email" CHAR(255) DEFAULT NULL, -- Email address
38             "password" CHAR(255) NOT NULL, -- Password hash
39             "algorithm" CHAR(64) DEFAULT NULL, -- Password hash Algorithm (SHA256)
40             "role" CHAR(255) DEFAULT NULL, -- Role name
41             "flags" INTEGER DEFAULT 0, -- Flags
42             "created" INTEGER DEFAULT NULL, -- Created at
43             "not_before" INTEGER DEFAULT NULL, -- Not Before
44             "not_after" INTEGER DEFAULT NULL, -- Not After
45             "public_key" TEXT DEFAULT NULL, -- Public Key (RSA/X509)
46             "private_key" TEXT DEFAULT NULL, -- Private Key (RSA/X509)
47             "attributes" TEXT DEFAULT NULL, -- Attributes (JSON)
48             "comment" TEXT DEFAULT NULL -- Comment
49             );
50             CREATE TABLE IF NOT EXISTS "groups" (
51             "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
52             "groupname" CHAR(64) NOT NULL UNIQUE, -- Group name
53             "description" TEXT DEFAULT NULL -- Description
54             );
55             CREATE TABLE IF NOT EXISTS "realms" (
56             "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
57             "realmname" CHAR(64) NOT NULL UNIQUE, -- Realm name
58             "realm" CHAR(255) DEFAULT NULL, -- Realm string
59             "satisfy" CHAR(16) DEFAULT NULL, -- The satisfy policy (All, Any)
60             "description" TEXT DEFAULT NULL -- Description
61             );
62             CREATE TABLE IF NOT EXISTS "routes" (
63             "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
64             "realmname" CHAR(64) DEFAULT NULL, -- Realm name
65             "routename" CHAR(64) DEFAULT NULL, -- Route name
66             "method" CHAR(16) DEFAULT NULL, -- HTTP method (ANY, GET, POST, ...)
67             "url" CHAR(255) DEFAULT NULL, -- URL
68             "base" CHAR(255) DEFAULT NULL, -- Base URL
69             "path" CHAR(255) DEFAULT NULL -- Path of URL (pattern)
70             );
71             CREATE TABLE IF NOT EXISTS "requirements" (
72             "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
73             "realmname" CHAR(64) DEFAULT NULL, -- Realm name
74             "provider" CHAR(64) DEFAULT NULL, -- Provider name (user,group,ip and etc.)
75             "entity" CHAR(64) DEFAULT NULL, -- Entity (operand of expression)
76             "op" CHAR(2) DEFAULT NULL, -- Comparison Operator
77             "value" CHAR(255) DEFAULT NULL -- Test value
78             );
79             CREATE TABLE IF NOT EXISTS "grpsusrs" (
80             "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
81             "groupname" CHAR(64) DEFAULT NULL, -- Group name
82             "username" CHAR(64) DEFAULT NULL -- User name
83             );
84             CREATE TABLE IF NOT EXISTS "meta" (
85             "key" CHAR(255) NOT NULL UNIQUE PRIMARY KEY,
86             "value" TEXT DEFAULT NULL
87             );
88             CREATE TABLE IF NOT EXISTS "stats" (
89             "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
90             "address" CHAR(40) DEFAULT NULL, -- IPv4/IPv6 client address
91             "username" CHAR(64) DEFAULT NULL, -- User name
92             "dismiss" INTEGER DEFAULT 0, -- Dismissal count
93             "updated" INTEGER DEFAULT NULL -- Update date
94             );
95             CREATE TABLE IF NOT EXISTS "tokens" (
96             "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
97             "jti" CHAR(32) DEFAULT NULL, -- Request ID
98             "username" CHAR(64) DEFAULT NULL, -- User name
99             "type" CHAR(20) DEFAULT NULL, -- Token type (session, refresh, api)
100             "clientid" CAHR(32) DEFAULT NULL, -- Clientid as md5 (User-Agent . Remote-Address)
101             "iat" INTEGER DEFAULT NULL, -- Issue time
102             "exp" INTEGER DEFAULT NULL, -- Expiration time
103             "address" CAHR(40) DEFAULT NULL, -- IPv4/IPv6 client address
104             "description" TEXT DEFAULT NULL -- Description
105             );
106              
107             =head2 MYSQL DDL
108              
109             CREATE DATABASE `authdb` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */;
110             CREATE TABLE IF NOT EXISTS `users` (
111             `id` INT(11) NOT NULL AUTO_INCREMENT,
112             `username` VARCHAR(64) NOT NULL, -- User name
113             `name` VARCHAR(255) DEFAULT NULL, -- Full user name
114             `email` VARCHAR(255) DEFAULT NULL, -- Email address
115             `password` VARCHAR(255) NOT NULL, -- Password hash
116             `algorithm` VARCHAR(64) DEFAULT NULL, -- Password hash Algorithm (SHA256)
117             `role` VARCHAR(255) DEFAULT NULL, -- Role name
118             `flags` INT(11) DEFAULT 0, -- Flags
119             `created` INT(11) DEFAULT NULL, -- Created at
120             `not_before` INT(11) DEFAULT NULL, -- Not Before
121             `not_after` INT(11) DEFAULT NULL, -- Not After
122             `public_key` TEXT DEFAULT NULL, -- Public Key (RSA/X509)
123             `private_key` TEXT DEFAULT NULL, -- Private Key (RSA/X509)
124             `attributes` TEXT DEFAULT NULL, -- Attributes (JSON)
125             `comment` TEXT DEFAULT NULL, -- Comment
126             PRIMARY KEY (`id`),
127             UNIQUE KEY `username` (`username`)
128             ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
129             CREATE TABLE IF NOT EXISTS `groups` (
130             `id` INT(11) NOT NULL AUTO_INCREMENT,
131             `groupname` VARCHAR(64) NOT NULL, -- Group name
132             `description` TEXT DEFAULT NULL, -- Description
133             PRIMARY KEY (`id`),
134             UNIQUE KEY `groupname` (`groupname`)
135             ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
136             CREATE TABLE IF NOT EXISTS `realms` (
137             `id` INT(11) NOT NULL AUTO_INCREMENT,
138             `realmname` VARCHAR(64) NOT NULL, -- Realm name
139             `realm` VARCHAR(255) DEFAULT NULL, -- Realm string
140             `satisfy` VARCHAR(16) DEFAULT NULL, -- The satisfy policy (All, Any)
141             `description` TEXT DEFAULT NULL, -- Description
142             PRIMARY KEY (`id`),
143             UNIQUE KEY `realmname` (`realmname`)
144             ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
145             CREATE TABLE IF NOT EXISTS `routes` (
146             `id` INT NOT NULL AUTO_INCREMENT,
147             `realmname` VARCHAR(64) DEFAULT NULL, -- Realm name
148             `routename` VARCHAR(64) DEFAULT NULL, -- Route name
149             `method` VARCHAR(16) DEFAULT NULL, -- HTTP method (ANY, GET, POST, ...)
150             `url` VARCHAR(255) DEFAULT NULL, -- URL
151             `base` VARCHAR(255) DEFAULT NULL, -- Base URL
152             `path` VARCHAR(255) DEFAULT NULL, -- Path of URL (pattern)
153             PRIMARY KEY (`id`)
154             ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
155             CREATE TABLE IF NOT EXISTS `requirements` (
156             `id` INT(11) NOT NULL AUTO_INCREMENT,
157             `realmname` VARCHAR(64) DEFAULT NULL, -- Realm name
158             `provider` VARCHAR(64) DEFAULT NULL, -- Provider name (user,group,ip and etc.)
159             `entity` VARCHAR(64) DEFAULT NULL, -- Entity (operand of expression)
160             `op` VARCHAR(2) DEFAULT NULL, -- Comparison Operator
161             `value` VARCHAR(255) DEFAULT NULL, -- Test value
162             PRIMARY KEY (`id`)
163             ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
164             CREATE TABLE IF NOT EXISTS `grpsusrs` (
165             `id` INT(11) NOT NULL AUTO_INCREMENT,
166             `groupname` VARCHAR(64) DEFAULT NULL, -- Group name
167             `username` VARCHAR(64) DEFAULT NULL, -- User name
168             PRIMARY KEY (`id`)
169             ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
170             CREATE TABLE IF NOT EXISTS `meta` (
171             `key` VARCHAR(255) NOT NULL,
172             `value` TEXT DEFAULT NULL,
173             PRIMARY KEY (`key`)
174             ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
175             CREATE TABLE IF NOT EXISTS `stats` (
176             `id` INT(11) NOT NULL AUTO_INCREMENT,
177             `address` VARCHAR(40) DEFAULT NULL, -- IPv4/IPv6 client address
178             `username` VARCHAR(64) DEFAULT NULL, -- User name
179             `dismiss` INT(11) DEFAULT 0, -- Dismissal count
180             `updated` INT(11) DEFAULT NULL, -- Update date
181             PRIMARY KEY (`id`)
182             ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
183             CREATE TABLE IF NOT EXISTS `tokens` (
184             `id` INT(11) NOT NULL AUTO_INCREMENT,
185             `jti` VARCHAR(32) DEFAULT NULL, -- Request ID
186             `username` VARCHAR(64) DEFAULT NULL, -- User name
187             `type` VARCHAR(20) DEFAULT NULL, -- Token type (session, refresh, api)
188             `clientid` VARCHAR(32) DEFAULT NULL, -- Clientid as md5 (User-Agent . Remote-Address)
189             `iat` INT(11) DEFAULT NULL, -- Issue time
190             `exp` INT(11) DEFAULT NULL, -- Expiration time
191             `address` VARCHAR(40) DEFAULT NULL, -- IPv4/IPv6 client address
192             `description` TEXT DEFAULT NULL, -- Description
193             PRIMARY KEY (`id`)
194             ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
195              
196             =head1 METHODS
197              
198             This class inherits all methods from L and implements the following new ones
199              
200             =head2 new
201              
202             my $model = WWW::Suffit::AuthDB::Model->new(
203             "sqlite:///tmp/test.db?sqlite_unicode=1"
204             );
205              
206             Create DBI object. See also L
207              
208             =head2 group_add
209              
210             $model->group_add(
211             groupname => "wheel",
212             description => "This administrator group added by default",
213             ) or die($model->error);
214              
215             Add new group recored
216              
217             =head2 group_del
218              
219             $model->group_del("wheel") or die($model->error);
220              
221             Delete record by groupname
222              
223             =head2 group_get
224              
225             my %data = $model->group_get("wheel");
226              
227             Returns data from database by groupname
228              
229             =head2 group_getall
230              
231             my @table = $model->group_getall();
232              
233             Returns pure data from database
234              
235             =head2 group_members
236              
237             my @members = $model->group_members( "wheel" );
238              
239             Returns members of specified group
240              
241             =head2 group_set
242              
243             $model->group_set(
244             username => "wheel",
245             description => "This administrator group added by default",
246             ) or die($model->error);
247              
248             Update recored by groupname
249              
250             =head2 grpusr_add
251              
252             $model->grpusr_add(
253             groupname => "wheel",
254             username => "root",
255             ) or die($model->error);
256              
257             Add the user to the group
258              
259             =head2 grpusr_del
260              
261             $model->grpusr_del( id => 123 ) or die($model->error);
262             $model->grpusr_del( groupname => "wheel" ) or die($model->error);
263             $model->grpusr_del( username => "root" ) or die($model->error);
264              
265             Delete members from groups by id, groupname or username
266              
267             =head2 grpusr_get
268              
269             my %data = $model->grpusr_get( id => 123 );
270             my @table = $model->grpusr_get( groupname => "wheel");
271             my @table = $model->grpusr_get( username => "root" );
272              
273             Returns members of groups by id, groupname or username
274              
275             =head2 initialize
276              
277             $model = $model->initialize;
278              
279             This method initializes DB schema before start using
280              
281             =head2 is_initialized
282              
283             print "Database is inialized" if $model->is_initialized;
284              
285             This method checks of the schema initialization status
286              
287             =head2 is_mariadb
288              
289             print $model->is_mariadb ? "Is MariaDB" : "Is NOT MariaDB"
290              
291             Returns true if type of current database is MariaDB
292              
293             =head2 is_mysql
294              
295             print $model->is_mysql ? "Is MySQL" : "Is NOT MySQL"
296              
297             Returns true if type of current database is MySQL or MariaDB
298              
299             =head2 is_oracle
300              
301             print $model->is_oracle ? "Is Oracle" : "Is NOT Oracle"
302              
303             Returns true if type of current database is Oracle
304              
305             =head2 is_postgresql
306              
307             print $model->is_postgresql ? "Is PostgreSQL" : "Is NOT PostgreSQL"
308              
309             Returns true if type of current database is PostgreSQL
310              
311             =head2 is_sqlite
312              
313             print $model->is_sqlite ? "Is SQLite" : "Is NOT SQLite"
314              
315             Returns true if type of current database is SQLite
316              
317             =head2 meta_del
318              
319             $model->meta_del("key") or die($model->error);
320             $model->meta_set(key => "foo") or die($model->error);
321              
322             Delete record by key
323              
324             =head2 meta_get
325              
326             my %data = $model->meta_get("key");
327              
328             Returns pair - key and value
329              
330             my @table = $model->meta_get();
331              
332             Returns all data from meta table
333              
334             =head2 meta_set
335              
336             $model->meta_set(key => "foo", value => "test") or die($model->error);
337              
338             Set pair - key and value
339              
340             $model->meta_set(key => "foo") or die($model->error);
341              
342             Delete record by key
343              
344             =head2 realm_add
345              
346             $model->realm_add(
347             realmname => "root",
348             realm => "Root pages",
349             satisfy => "Any",
350             description => "Index page",
351             ) or die($model->error);
352              
353             Add new realm recored
354              
355             =head2 realm_del
356              
357             $model->realm_del("root") or die($model->error);
358              
359             Delete record by realmname
360              
361             =head2 realm_get
362              
363             my %data = $model->realm_get("root");
364              
365             Returns data from database by realmname
366              
367             =head2 realm_getall
368              
369             my @table = $model->realm_getall();
370              
371             Returns pure data from database
372              
373             =head2 realm_requirement_add
374              
375             $model->realm_requirement_add(
376             realmname => "root",
377             provider => "user",
378             entity => "username",
379             op => "eq",
380             value => "admin",
381             ) or die($model->error);
382              
383             Add the new requirement
384              
385             =head2 realm_requirement_del
386              
387             $model->realm_requirement_del("default") or die($model->error);
388              
389             Delete requirements by realmname
390              
391             =head2 realm_requirements
392              
393             my @table = $model->realm_requirements("default");
394              
395             Returns realm's requirements from database by realmname
396              
397             =head2 realm_routes
398              
399             my @table = $model->realm_routes( "realmname" );
400              
401             Returns realm's routes from database by realmname
402              
403             =head2 realm_set
404              
405             $model->realm_set(
406             realmname => "root",
407             realm => "Root pages",
408             satisfy => "Any",
409             description => "Index page (modified)",
410             ) or die($model->error);
411              
412             Update recored by realmname
413              
414             =head2 route_add
415              
416             $model->route_add(
417             realmname => "root",
418             routename => "root",
419             method => "GET",
420             url => "https://localhost:8695/foo/bar",
421             base => "https://localhost:8695/",
422             path => "/foo/bar",
423             ) or die($model->error);
424              
425             Add the new route to realm
426              
427             =head2 route_assign
428              
429             $model->route_add(
430             realmname => "default",
431             routename => "index",
432             ) or die($model->error);
433              
434             Assignees the realm for route by routename
435              
436             =head2 route_del
437              
438             $model->route_del(123) or die($model->error);
439              
440             Delete record by id
441              
442             $model->route_del("root") or die($model->error);
443              
444             Delete record by realmname
445              
446             =head2 route_get
447              
448             my %data = $model->route_get(123);
449              
450             Returns data from database by id
451              
452             my @table = $model->route_get("root");
453              
454             Returns data from database by realmname
455              
456             =head2 route_getall
457              
458             my @table = $model->route_getall();
459              
460             Returns pure data from database
461              
462             =head2 route_release
463              
464             $model->route_release("default") or die($model->error);
465              
466             Releases the route (removes relation with realm) by realmname
467              
468             =head2 route_search
469              
470             my @routes = $model->route_search( "ind" );
471              
472             Performs search route by specified fragment and returns list of found routes
473              
474             =head2 route_set
475              
476             $model->route_set(
477             id => 123,
478             realmname => "root",
479             routename => "root",
480             method => "POST",
481             url => "https://localhost:8695",
482             base => "https://localhost:8695/",
483             path => "/foo/bar",
484             ) or die($model->error);
485              
486             Update record by id
487              
488             =head2 stat_get
489              
490             my %st = $model->stat_get($address, $username);
491              
492             Returns the user statistic information by address and username
493              
494             =head2 stat_set
495              
496             $model->stat_set(
497             address => $address,
498             username => $username,
499             dismiss => 1,
500             updated => time,
501             ) or die($model->error);
502              
503             Sets the user statistic information by address and username
504              
505             =head2 token_add
506              
507             $model->token_add(
508             type => 'api',
509             jti => $jti,
510             username => $username,
511             clientid => 'qwertyuiqwertyui',
512             iat => time,
513             exp => time + 3600,
514             address => '127.0.0.1',
515             description => "My API token",
516             ) or die($model->error);
517              
518             Add new token for user
519              
520             =head2 token_del
521              
522             $model->token_del( 123 ) or die($model->error);
523              
524             Delete token by id
525              
526             $model->token_del() or die($model->error);
527              
528             Delete all expired tokens
529              
530             =head2 token_get
531              
532             my %data = $model->token_get( 123 );
533              
534             Returns data from database by id
535              
536             =head2 token_get_cond
537              
538             my %data = $model->token_get_cond('api', username => $username, jti => $jti);
539             my %data = $model->token_get_cond('session', username => $username, clientid => $clientid);
540              
541             Returns data from database by id jti or clientid
542              
543             =head2 token_getall
544              
545             my @table = $model->token_getall();
546              
547             Returns all tokens
548              
549             =head2 token_set
550              
551             $model->token_set(
552             id => 123,
553             type => 'api',
554             jti => $jti,
555             username => $username,
556             clientid => 'qwertyuiqwertyui',
557             iat => time,
558             exp => time + 3600,
559             address => '127.0.0.1',
560             description => "My API token",
561             ) or die($model->error);
562              
563             Update token by id
564              
565             =head2 user_add
566              
567             $model->user_add(
568             username => "admin",
569             name => "Administrator",
570             email => 'root@localhost',
571             password => "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
572             algorithm => "SHA256",
573             role => "System administrator",
574             flags => 0,
575             created => time(),
576             not_before => time(),
577             not_after => undef,
578             public_key => "",
579             private_key => "",
580             attributes => qq/{"disabled": 0}/,
581             comment => "This user added by default",
582             ) or die($model->error);
583              
584             Add new user recored
585              
586             =head2 user_del
587              
588             $model->user_del("admin") or die($model->error);
589              
590             Delete record by username
591              
592             =head2 user_edit
593              
594             $model->user_edit(
595             id => 123,
596             username => $username,
597             comment => $comment,
598             email => $email,
599             name => $name,
600             role => $role,
601             ) or die($model->error);
602              
603             Edit user data by id
604              
605             =head2 user_get
606              
607             my %data = $model->user_get("admin");
608              
609             Returns data from database by username
610              
611             =head2 user_getall
612              
613             my @table = $model->user_getall();
614              
615             Returns pure data from database (array of hashes)
616              
617             =head2 user_groups
618              
619             my @groups = $model->user_groups( "admin" );
620              
621             Returns groups of specified user
622              
623             =head2 user_passwd
624              
625             $model->user_passwd(
626             username => "admin",
627             password => "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
628             ) or die($model->error);
629              
630             Changes password for user
631              
632             =head2 user_search
633              
634             my @users = $model->user_search( "ad" );
635              
636             Performs search user by specified fragment and returns list of found users
637              
638             =head2 user_set
639              
640             $model->user_set(
641             username => "admin",
642             name => "Administrator",
643             email => 'root@localhost',
644             password => "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
645             algorithm => "SHA256",
646             role => "System administrator",
647             flags => 0,
648             not_before => time(),
649             not_after => undef,
650             public_key => "",
651             private_key => "",
652             attributes => qq/{"disabled": 0}/,
653             comment => "This user added by default",
654             ) or die($model->error);
655              
656             Update recored by username
657              
658             =head2 user_setkeys
659              
660             $model->user_setkeys(
661             id => 123,
662             public_key => $public_key,
663             private_key => $private_key,
664             ) or die($model->error);
665              
666             Sets keys to user's data
667              
668             =head2 user_tokens
669              
670             my @table = $model->user_tokens($username);
671              
672             Returns all tokens for user
673              
674             =head1 HISTORY
675              
676             See C file
677              
678             =head1 TO DO
679              
680             See C file
681              
682             =head1 SEE ALSO
683              
684             L, L
685              
686             =head1 AUTHOR
687              
688             Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE
689              
690             =head1 COPYRIGHT
691              
692             Copyright (C) 1998-2026 D&D Corporation
693              
694             =head1 LICENSE
695              
696             This program is distributed under the terms of the Artistic License Version 2.0
697              
698             See the C file or L for details
699              
700             =cut
701              
702 4     4   1299 use parent 'Acrux::DBI';
  4         244  
  4         47  
703              
704 4     4   702880 use Acrux::Util qw/ touch /;
  4         12  
  4         421  
705 4     4   26 use Acrux::RefUtil qw/ is_integer is_array_ref is_hash_ref isnt_void /;
  4         9  
  4         592  
706              
707             our $VERSION = '1.01';
708              
709             our $DEBUG //= !!$ENV{WWW_SUFFIT_AUTHDB_MODEL_DEBUG};
710              
711             use constant {
712 4         591 DEFAULT_ALGORITHM => 'SHA256',
713             SCHEMA_NAME => 'authdb',
714             SCHEMA_SECTION_FORMAT => 'schema_%s',
715             SCHEMA_PATCHES => {
716             # version => label
717             '0.01' => 'initial', # Initial version
718             '1.00' => 'v100',
719             '1.01' => 'v101',
720             },
721 4     4   64 };
  4         8  
722              
723             # Meta DMLs
724 4     4   33 use constant DML_META_ADD => <<'DML';
  4         7  
  4         292  
725             INSERT INTO `meta`
726             (`key`,`value`)
727             VALUES
728             (?,?)
729             DML
730 4     4   25 use constant DML_META_GET => <<'DML';
  4         5  
  4         241  
731             SELECT `key`,`value`
732             FROM `meta`
733             WHERE `key` = ?
734             DML
735 4     4   21 use constant DML_META_GETALL => <<'DML';
  4         7  
  4         253  
736             SELECT `key`,`value`
737             FROM `meta`
738             ORDER BY `key` ASC
739             DML
740 4     4   22 use constant DML_META_SET => <<'DML';
  4         7  
  4         227  
741             UPDATE `meta`
742             SET `value` = ?
743             WHERE `key` = ?
744             DML
745 4     4   24 use constant DML_META_DEL => <
  4         8  
  4         255  
746             DELETE FROM `meta`
747             WHERE `key` = ?
748             DML
749              
750             # Stat DMLs
751 4     4   24 use constant DML_STAT_GET => <<'DML';
  4         7  
  4         222  
752             SELECT `id`,`address`,`username`,`dismiss`,`updated`
753             FROM `stats`
754             WHERE `address` = ? AND `username` = ?
755             DML
756 4     4   20 use constant DML_STAT_ADD => <<'DML';
  4         7  
  4         179  
757             INSERT INTO `stats` (`address`,`username`,`dismiss`,`updated`)
758             VALUES (?,?,?,?)
759             DML
760 4     4   19 use constant DML_STAT_SET => <<'DML';
  4         6  
  4         226  
761             UPDATE `stats`
762             SET `address` = ?, `username` =?, `dismiss` = ?, `updated` = ?
763             WHERE `id` = ?
764             DML
765              
766             # User DMLs
767 4     4   43 use constant DML_USER_ADD => <<'DML';
  4         7  
  4         214  
768             INSERT INTO `users`
769             (`username`,`name`,`email`,`password`,`algorithm`,`role`,`flags`,`created`,
770             `not_before`,`not_after`,`public_key`,`private_key`,`attributes`,`comment`
771             )
772             VALUES
773             (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
774             DML
775 4     4   42 use constant DML_USER_GET => <<'DML';
  4         7  
  4         261  
776             SELECT `id`,`username`,`name`,`email`,`password`,`algorithm`,`role`,`flags`,`created`,
777             `not_before`,`not_after`,`public_key`,`private_key`,`attributes`,`comment`
778             FROM `users`
779             WHERE `username` = ?
780             DML
781 4     4   31 use constant DML_USER_SET => <<'DML';
  4         8  
  4         269  
782             UPDATE `users`
783             SET `name` = ?, `email` = ?, `password` = ?, `algorithm` = ?, `role` = ?, `flags` = ?,
784             `not_before` = ?, `not_after` = ?, `public_key` = ?, `private_key` = ?,
785             `attributes` = ?, `comment` = ?
786             WHERE `username` = ?
787             DML
788 4     4   47 use constant DML_USER_DEL => <<'DML';
  4         8  
  4         234  
789             DELETE FROM `users` WHERE `username` = ?
790             DML
791 4     4   22 use constant DML_USER_GETALL => <<'DML';
  4         10  
  4         215  
792             SELECT `id`,`username`,`name`,`email`,`password`,`algorithm`,`role`,`flags`,`created`,
793             `not_before`,`not_after`,`public_key`,`private_key`,`attributes`,`comment`
794             FROM `users`
795             ORDER BY `username` ASC
796             DML
797 4     4   22 use constant DML_PASSWD => <<'DML';
  4         6  
  4         237  
798             UPDATE `users`
799             SET `password` = ?
800             WHERE `username` = ?
801             DML
802 4     4   23 use constant DML_USER_SEARCH => <<'DML';
  4         16  
  4         276  
803             SELECT `id`,`username`,`name`,`role`
804             FROM `users`
805             WHERE 1 = 1
806             %s
807             ORDER BY `username` ASC
808             LIMIT 10
809             DML
810 4     4   27 use constant DML_USER_GROUPS => <<'DML';
  4         7  
  4         242  
811             SELECT
812             groups.`id` AS `id`,
813             groups.`groupname` AS `groupname`,
814             groups.`description` AS `description`
815             FROM
816             grpsusrs
817             LEFT OUTER JOIN groups ON (groups.`groupname` = grpsusrs.`groupname`)
818             WHERE 1 = 1
819             AND grpsusrs.`username` = ?
820             ORDER BY
821             grpsusrs.`groupname` ASC
822             DML
823 4     4   22 use constant DML_USER_EDIT => <<'DML';
  4         7  
  4         209  
824             UPDATE `users`
825             SET `name` = ?, `email` = ?, `role` = ?, `comment` = ?
826             WHERE `id` = ?
827             DML
828 4     4   21 use constant DML_USER_SETKEYS => <<'DML';
  4         7  
  4         236  
829             UPDATE `users`
830             SET `public_key` = ?, `private_key` = ?
831             WHERE `id` = ?
832             DML
833              
834             # Group DMLs
835 4     4   24 use constant DML_GROUP_ADD => <<'DML';
  4         7  
  4         298  
836             INSERT INTO `groups` (`groupname`,`description`)
837             VALUES (?,?)
838             DML
839 4     4   24 use constant DML_GROUP_GET => <<'DML';
  4         21  
  4         209  
840             SELECT `id`,`groupname`,`description`
841             FROM `groups`
842             WHERE `groupname` = ?
843             DML
844 4     4   20 use constant DML_GROUP_SET => <<'DML';
  4         12  
  4         300  
845             UPDATE `groups`
846             SET `description` = ?
847             WHERE `groupname` = ?
848             DML
849 4     4   25 use constant DML_GROUP_DEL => <<'DML';
  4         6  
  4         276  
850             DELETE FROM `groups` WHERE `groupname` = ?
851             DML
852 4     4   29 use constant DML_GROUP_GETALL => <<'DML';
  4         11  
  4         265  
853             SELECT `id`,`groupname`,`description`
854             FROM `groups`
855             ORDER BY `groupname` ASC
856             DML
857 4     4   25 use constant DML_GROUP_MEMBERS => <<'DML';
  4         7  
  4         245  
858             SELECT
859             users.`id` AS `id`,
860             users.`username` AS `username`,
861             users.`name` AS `name`,
862             users.`role` AS `role`
863             FROM
864             users
865             LEFT OUTER JOIN grpsusrs ON (grpsusrs.`username` = users.`username`)
866             WHERE 1 = 1
867             AND grpsusrs.`groupname` = ?
868             ORDER BY
869             grpsusrs.`username` ASC
870             DML
871              
872             # Group-User DMLs
873 4     4   45 use constant DML_GRPUSR_ADD => <<'DML';
  4         9  
  4         242  
874             INSERT INTO `grpsusrs`
875             (`groupname`,`username`)
876             VALUES
877             (?,?)
878             DML
879 4     4   28 use constant DML_GRPUSR_GET_BY_ID => <<'DML';
  4         9  
  4         262  
880             SELECT `id`,`groupname`,`username`
881             FROM `grpsusrs`
882             WHERE `id` = ?
883             DML
884 4     4   24 use constant DML_GRPUSR_GET_BY_GROUP_USER => <<'DML';
  4         9  
  4         250  
885             SELECT `id`,`groupname`,`username`
886             FROM `grpsusrs`
887             WHERE `groupname` = ? AND `username` = ?
888             DML
889 4     4   41 use constant DML_GRPUSR_GET_BY_GROUP => <<'DML';
  4         28  
  4         359  
890             SELECT `id`,`groupname`,`username`
891             FROM `grpsusrs`
892             WHERE `groupname` = ?
893             DML
894 4     4   26 use constant DML_GRPUSR_GET_BY_USER => <<'DML';
  4         7  
  4         250  
895             SELECT `id`,`groupname`,`username`
896             FROM `grpsusrs`
897             WHERE `username` = ?
898             DML
899 4     4   40 use constant DML_GRPUSR_DEL_BY_ID => <
  4         8  
  4         267  
900             DELETE FROM `grpsusrs`
901             WHERE `id` = ?
902             DML
903 4     4   24 use constant DML_GRPUSR_DEL_BY_GROUP => <
  4         7  
  4         260  
904             DELETE FROM `grpsusrs`
905             WHERE `groupname` = ?
906             DML
907 4     4   21 use constant DML_GRPUSR_DEL_BY_USER => <
  4         25  
  4         235  
908             DELETE FROM `grpsusrs`
909             WHERE `username` = ?
910             DML
911              
912             # Realm DMLs
913 4     4   23 use constant DML_REALM_ADD => <<'DML';
  4         8  
  4         244  
914             INSERT INTO `realms` (`realmname`,`realm`,`satisfy`,`description`)
915             VALUES (?,?,?,?)
916             DML
917 4     4   38 use constant DML_REALM_GET => <<'DML';
  4         8  
  4         246  
918             SELECT `id`,`realmname`,`realm`,`satisfy`,`description`
919             FROM `realms`
920             WHERE `realmname` = ?
921             DML
922 4     4   25 use constant DML_REALM_SET => <<'DML';
  4         8  
  4         234  
923             UPDATE `realms`
924             SET `realm` = ?,`satisfy` =?, `description` = ?
925             WHERE `realmname` = ?
926             DML
927 4     4   21 use constant DML_REALM_DEL => <<'DML';
  4         7  
  4         218  
928             DELETE FROM `realms` WHERE `realmname` = ?
929             DML
930 4     4   23 use constant DML_REALM_GETALL => <<'DML';
  4         8  
  4         319  
931             SELECT `id`,`realmname`,`realm`,`satisfy`,`description`
932             FROM `realms`
933             ORDER BY `realmname` ASC
934             DML
935              
936             # Route DMLs
937 4     4   24 use constant DML_ROUTE_ADD => <<'DML';
  4         6  
  4         227  
938             INSERT INTO `routes`
939             (`realmname`,`routename`,`method`,`url`,`base`,`path`)
940             VALUES
941             (?,?,?,?,?,?)
942             DML
943 4     4   21 use constant DML_ROUTE_GET_BY_ROUTE => <<'DML';
  4         19  
  4         227  
944             SELECT `id`,`realmname`,`routename`,`method`,`url`,`base`,`path`
945             FROM `routes`
946             WHERE `routename` = ?
947             DML
948 4     4   23 use constant DML_ROUTE_GET_BY_REALM => <<'DML';
  4         29  
  4         271  
949             SELECT `id`,`realmname`,`routename`,`method`,`url`,`base`,`path`
950             FROM `routes`
951             WHERE `realmname` = ?
952             ORDER BY `routename` ASC
953             DML
954 4     4   25 use constant DML_ROUTE_SET => <<'DML';
  4         7  
  4         254  
955             UPDATE `routes`
956             SET `realmname` = ?, `method` = ?, `url` = ?, `base` = ?, `path` = ?
957             WHERE `routename` = ?
958             DML
959 4     4   26 use constant DML_ROUTE_DEL_BY_ROUTE => <
  4         7  
  4         234  
960             DELETE FROM `routes`
961             WHERE `routename` = ?
962             DML
963 4     4   25 use constant DML_ROUTE_DEL_BY_REALM => <
  4         8  
  4         280  
964             DELETE FROM `routes`
965             WHERE `realmname` = ?
966             DML
967 4     4   35 use constant DML_ROUTE_GETALL => <<'DML';
  4         8  
  4         286  
968             SELECT `id`,`realmname`,`routename`,`method`,`url`,`base`,`path`
969             FROM `routes`
970             ORDER BY `routename` ASC
971             DML
972 4     4   23 use constant DML_ROUTE_SEARCH => <<'DML';
  4         8  
  4         267  
973             SELECT `id`,`realmname`,`routename`,`method`,`url`,`base`,`path`
974             FROM `routes`
975             WHERE 1 = 1
976             %s
977             ORDER BY `routename` ASC
978             LIMIT 10
979             DML
980 4     4   27 use constant DML_ROUTE_RELEASE_BY_REALM => <
  4         7  
  4         224  
981             UPDATE `routes`
982             SET `realmname` = NULL
983             WHERE `realmname` = ?
984             DML
985 4     4   22 use constant DML_ROUTE_ASSIGN_BY_ROUTE => <
  4         7  
  4         245  
986             UPDATE `routes`
987             SET `realmname` = ?
988             WHERE `routename` = ?
989             DML
990              
991             # Requirement DMLs
992 4     4   26 use constant DML_REQUIREMENT_ADD => <<'DML';
  4         6  
  4         275  
993             INSERT INTO `requirements`
994             (`realmname`,`provider`,`entity`,`op`,`value`)
995             VALUES
996             (?,?,?,?,?)
997             DML
998 4     4   24 use constant DML_REQUIREMENT_GET_BY_ID => <<'DML';
  4         9  
  4         223  
999             SELECT `id`,`realmname`,`provider`,`entity`,`op`,`value`
1000             FROM `requirements`
1001             WHERE `id` = ?
1002             DML
1003 4     4   23 use constant DML_REQUIREMENT_GET_BY_REALM => <<'DML';
  4         7  
  4         325  
1004             SELECT `id`,`realmname`,`provider`,`entity`,`op`,`value`
1005             FROM `requirements`
1006             WHERE `realmname` = ?
1007             ORDER BY `provider` ASC, `entity` ASC, `op` ASC, `value` ASC
1008             DML
1009 4     4   25 use constant DML_REQUIREMENT_DEL_BY_ID => <
  4         7  
  4         273  
1010             DELETE FROM `requirements`
1011             WHERE `id` = ?
1012             DML
1013 4     4   25 use constant DML_REQUIREMENT_DEL_BY_REALM => <
  4         6  
  4         270  
1014             DELETE FROM `requirements`
1015             WHERE `realmname` = ?
1016             DML
1017              
1018             # Token DMLs
1019 4     4   50 use constant DML_TOKEN_GET => <<'DML';
  4         8  
  4         287  
1020             SELECT `id`,`jti`,`username`,`type`,`clientid`,`iat`,`exp`,`address`,`description`
1021             FROM `tokens`
1022             WHERE `id` =?
1023             DML
1024 4     4   25 use constant DML_TOKEN_GET_BY_USERNAME_AND_CLIENTID => <<'DML';
  4         19  
  4         233  
1025             SELECT `id`,`jti`,`username`,`type`,`clientid`,`iat`,`exp`,`address`,`description`
1026             FROM `tokens`
1027             WHERE `username` = ? AND `clientid` = ? AND `type` = "session"
1028             DML
1029 4     4   22 use constant DML_TOKEN_GET_BY_USERNAME_AND_JTI => <<'DML';
  4         8  
  4         224  
1030             SELECT `id`,`jti`,`username`,`type`,`clientid`,`iat`,`exp`,`address`,`description`
1031             FROM `tokens`
1032             WHERE `username` = ? AND `jti` = ?
1033             DML
1034 4     4   22 use constant DML_TOKEN_ADD => <<'DML';
  4         7  
  4         487  
1035             INSERT INTO `tokens` (`jti`,`username`,`type`,`clientid`,`iat`,`exp`,`address`,`description`)
1036             VALUES (?,?,?,?,?,?,?,?)
1037             DML
1038 4     4   24 use constant DML_TOKEN_SET => <<'DML';
  4         7  
  4         297  
1039             UPDATE `tokens`
1040             SET `jti` = ?, `username` =?, `type` = ?, `clientid` = ?, `iat` = ?, `exp` = ?, `address` = ?, `description` = ?
1041             WHERE `id` = ?
1042             DML
1043 4     4   25 use constant DML_TOKEN_GET_BY_USERNAME => <
  4         8  
  4         255  
1044             SELECT `id`,`jti`,`username`,`type`,`clientid`,`iat`,`exp`,`address`,`description`
1045             FROM `tokens`
1046             WHERE `username` = ?
1047             ORDER BY `iat` DESC
1048             DML
1049 4     4   25 use constant DML_TOKEN_GET_ALL => <<'DML';
  4         7  
  4         203  
1050             SELECT `id`,`jti`,`username`,`type`,`clientid`,`iat`,`exp`,`address`,`description`
1051             FROM `tokens`
1052             ORDER BY `username` ASC, `iat` DESC
1053             DML
1054 4     4   22 use constant DML_TOKEN_DEL => <<'DML';
  4         6  
  4         234  
1055             DELETE FROM `tokens`
1056             WHERE `id` = ?
1057             DML
1058 4     4   22 use constant DML_TOKEN_DEL_EXPIRED => <<'DML';
  4         21  
  4         28867  
1059             DELETE FROM `tokens`
1060             WHERE `exp` IS NOT NULL AND `exp` > 0 AND `exp` < ?
1061             DML
1062              
1063             sub initialize {
1064 2     2 1 229955 my $self = shift; # shift->connect_cached;
1065 2   50     29 my $schema = shift // SCHEMA_NAME;
1066 2         6 my $is_inited = 0; # Not inited
1067 2         19 my $dbh = $self->dbh;
1068 2         12 my $name = 'unknown';
1069              
1070             # Check DB handler
1071 2 50 0     9 return $self->error(sprintf("Can't connect to database \"%s\": %s",
1072             $self->dsn, $self->errstr || "unknown error")) unless $dbh;
1073              
1074             # Check SQLite
1075 2 50       8 if ($self->is_sqlite) {
    0          
    0          
    0          
1076 2         35 my $file = $dbh->sqlite_db_filename();
1077 2 50 33     120 unless ($file && (-e $file) && !(-z $file)) {
      33        
1078 2         20 touch($file);
1079 2         355 chmod(0666, $file);
1080             }
1081              
1082             # Get table info
1083 2 50       29 if (my $sth = $dbh->table_info(undef, undef, undef, 'TABLE')) {
1084 2 50       1588 $is_inited = isnt_void($sth->fetchall_arrayref) ? 1 : 0;
1085             }
1086              
1087             # Set name
1088 2         184 $name = sprintf(SCHEMA_SECTION_FORMAT, 'sqlite');
1089             }
1090              
1091             # Check MariaDB
1092             elsif ($self->is_mariadb) {
1093             # Get table info
1094 0 0       0 if (my $sth = $dbh->table_info('', $schema, '', 'TABLE')) {
1095 0 0       0 $is_inited = isnt_void($sth->fetchall_arrayref) ? 1 : 0;
1096             }
1097              
1098             # Set name
1099 0         0 $name = sprintf(SCHEMA_SECTION_FORMAT, 'mysql');
1100             }
1101              
1102             # Check MySQL
1103             elsif ($self->is_mysql) {
1104             # Get table info
1105 0 0       0 if (my $sth = $dbh->table_info('', $schema, '', 'TABLE')) {
1106 0 0       0 $is_inited = isnt_void($sth->fetchall_arrayref) ? 1 : 0;
1107             }
1108              
1109             # Set name
1110 0         0 $name = sprintf(SCHEMA_SECTION_FORMAT, 'mysql');
1111             }
1112              
1113             # Check PostgreSQL
1114             elsif ($self->is_postgresql) {
1115             # Get table info
1116 0 0       0 if (my $sth = $dbh->table_info('', $schema, undef, 'TABLE')) { # schema = 'public'
1117 0 0       0 $is_inited = isnt_void($sth->fetchall_arrayref) ? 1 : 0;
1118             }
1119              
1120             # Set name
1121 0         0 $name = sprintf(SCHEMA_SECTION_FORMAT, 'postgresql');
1122             }
1123              
1124             # Skip initialize otherwise
1125             else {
1126 0         0 return $self;
1127             }
1128              
1129             # Get dump instance
1130 2         34 my $dump = $self->dump(name => $name)->from_data(__PACKAGE__);
1131              
1132             # Import initial schema if is not inited
1133 2 50       35804 unless ($is_inited) {
1134 2         18 $dump->poke(); # main section (default)
1135 2 50       449552 return $self if $self->error;
1136             }
1137              
1138             # Check connect
1139 2 50 0     41 return $self->error(sprintf("Can't init database \"%s\". Ping failed: %s",
1140             $self->dsn, $self->errstr() || "unknown error")) unless $self->ping;
1141              
1142             # Import patches
1143 2         210 my %ver = $self->meta_get("schema.version");
1144 2 50       110 return $self if $self->error;
1145 2   50     71 my $patches = $self->_get_patches( $ver{value} || '0.00' ) || [];
1146 2         7 foreach my $p (@$patches) {
1147             #print "# $p\n";
1148 6         89 $dump->poke($p);
1149 6 50       182129 return $self if $self->error;
1150             }
1151              
1152 2         69 return $self;
1153             }
1154             sub is_initialized {
1155 3     3 1 560 my $self = shift;
1156 3   33     33 my $ver = shift // $VERSION;
1157 3         20 my %vd = $self->meta_get("schema.version");
1158 3 50       147 return 0 if $self->error;
1159 3   50     38 my $v = $vd{value} || '0.00';
1160 3 50       45 return 1 if ($v * 1) >= ($ver * 1);
1161 0         0 return 0;
1162             }
1163              
1164             sub is_sqlite {
1165 2     2 1 6 my $self = shift;
1166 2         12 my $dr = $self->driver;
1167 2 50 33     34 return ($dr eq 'sqlite' or $dr eq 'file') ? 1 : 0;
1168             }
1169             sub is_mysql {
1170 0     0 1 0 my $self = shift;
1171 0         0 my $dr = $self->driver;
1172 0 0 0     0 return ($dr eq 'mysql' or $dr eq 'mariadb' or $dr eq 'maria') ? 1 : 0;
1173             }
1174             sub is_mariadb {
1175 0     0 1 0 my $self = shift;
1176 0         0 my $dr = $self->driver;
1177 0 0 0     0 return ($dr eq 'maria' or $dr eq 'mariadb') ? 1 : 0;
1178             }
1179             sub is_postgresql {
1180 0     0 1 0 my $self = shift;
1181 0         0 my $dr = $self->driver;
1182 0 0 0     0 return ($dr eq 'pg' or $dr eq 'pgsql' or $dr eq 'postgres' or $dr eq 'postgresql') ? 1 : 0;
1183             }
1184             sub is_oracle {
1185 0     0 1 0 my $self = shift;
1186 0         0 my $dr = $self->driver;
1187 0 0       0 return ($dr eq 'oracle') ? 1 : 0;
1188             }
1189              
1190             # Meta CRUDs
1191             sub meta_set {
1192 8     8 1 2177 my $self = shift;
1193 8         39 my %data = @_;
1194 8 50       40 return 0 unless $self->ping;
1195 8 50       529 unless ($data{key}) {
1196 0         0 $self->error("No key specified");
1197 0         0 return 0;
1198             }
1199              
1200             # Get existed data
1201 8         36 my %pair = $self->meta_get($data{key});
1202 8 50       236 return 0 if $self->error;
1203              
1204             # Add/Update/Delete
1205 8 100       87 if ($pair{key}) {
1206 3 100       15 if (exists $data{value}) {
1207             # Set (update)
1208 1 50       6 $self->query(DML_META_SET, $data{value}, $data{key}) or return 0;
1209             } else {
1210             # Delete
1211 2 50       11 $self->query(DML_META_DEL, $data{key}) or return 0;
1212             }
1213             } else {
1214             # Add (insert)
1215 5 50       26 $self->query(DML_META_ADD, $data{key}, $data{value}) or return 0;
1216             }
1217              
1218             # Ok
1219 8         132241 return 1;
1220             }
1221             sub meta_get {
1222 19     19 1 3417 my $self = shift;
1223 19   100     79 my $key = shift // '';
1224 19 50       76 return () unless $self->ping;
1225              
1226 19 100       934 if (length $key) {
1227 17 50       81 if (my $res = $self->query(DML_META_GET, $key)) {
1228 17         11438 my $r = $res->hash;
1229 17 100       872 return (%$r) if is_hash_ref($r);
1230             }
1231             } else {
1232 2 50       8 if (my $res = $self->query(DML_META_GETALL)) {
1233 2         489 my $r = $res->hashes;
1234 2 50       196 return (@$r) if is_array_ref($r);
1235             }
1236             }
1237              
1238 8         119 return ();
1239             }
1240 2   50 2 1 1144 sub meta_del { shift->meta_set(key => shift // '') }
1241              
1242             # Stat CRUDs
1243             sub stat_set {
1244 3     3 1 1824 my $self = shift;
1245 3         18 my %data = @_;
1246 3 50       19 return 0 unless $self->ping;
1247 3 50 33     226 unless (defined($data{address}) && length($data{address})) {
1248 0         0 $self->error("No address specified");
1249 0         0 return 0;
1250             }
1251 3 50 33     48 unless (defined($data{username}) && length($data{username})) {
1252 0         0 $self->error("No username specified");
1253 0         0 return 0;
1254             }
1255              
1256             # Get existed data
1257 3         19 my %cur = $self->stat_get($data{address}, $data{username});
1258 3 50       118 return 0 if $self->error;
1259              
1260             # Add/Update
1261 3 100       33 if ($cur{id}) {
1262             # Set (update)
1263             $self->query(DML_STAT_SET, $data{address}, $data{username},
1264             $data{dismiss} || 0, $data{updated} || time, $cur{id}
1265 1 50 50     12 ) or return 0;
      33        
1266             } else {
1267             # Add (insert)
1268             $self->query(DML_STAT_ADD, $data{address}, $data{username},
1269             $data{dismiss} || 0, $data{updated} || time
1270 2 50 100     21 ) or return 0;
      66        
1271             }
1272              
1273             # Ok
1274 3         76329 return 1;
1275             }
1276             sub stat_get {
1277 6     6 1 720 my $self = shift;
1278 6   50     26 my $address = shift // '';
1279 6   50     20 my $username = shift // '';
1280 6 50       24 return () unless $self->ping;
1281              
1282 6 100       337 unless (length($address)) {
1283 1         9 $self->error("No address specified");
1284 1         13 return ();
1285             }
1286 5 50       18 unless (length($username)) {
1287 0         0 $self->error("No username specified");
1288 0         0 return ();
1289             }
1290              
1291             # Get data
1292 5 50       33 if (my $res = $self->query(DML_STAT_GET, $address, $username)) {
1293 5         1902 my $r = $res->hash;
1294 5 100       173 return (%$r) if is_hash_ref($r);
1295             }
1296              
1297 3         28 return ();
1298             }
1299              
1300             # User CRUDs
1301             sub user_add {
1302 8     8 1 1134 my $self = shift;
1303 8         91 my %data = @_;
1304 8 50       41 return 0 unless $self->ping;
1305              
1306             # Add
1307             $self->query(DML_USER_ADD,
1308             $data{username}, $data{name}, $data{email}, $data{password},
1309             uc($data{algorithm} || DEFAULT_ALGORITHM), $data{role}, $data{flags},
1310             $data{created} || time(), $data{not_before} || time(), $data{not_after},
1311             $data{public_key}, $data{private_key}, $data{attributes},
1312             $data{comment},
1313 8 50 50     597 ) or return 0;
      33        
      33        
1314              
1315             # Ok
1316 8         167071 return 1;
1317             }
1318             sub user_set { # set by username
1319 2     2 1 500 my $self = shift;
1320 2         17 my %data = @_;
1321 2 50       9 return 0 unless $self->ping;
1322 2 50 50     138 unless (length($data{username} // '')) {
1323 0         0 $self->error("No username specified");
1324 0         0 return 0;
1325             }
1326              
1327             # Set
1328             $self->query(DML_USER_SET,
1329             $data{name}, $data{email}, $data{password},
1330             uc($data{algorithm} || DEFAULT_ALGORITHM), $data{role}, $data{flags},
1331             $data{not_before} || time(), $data{not_after},
1332             $data{public_key}, $data{private_key}, $data{attributes},
1333             $data{comment},
1334             $data{username},
1335 2 50 50     37 ) or return 0;
      66        
1336              
1337             # Ok
1338 2         33331 return 1;
1339             }
1340             sub user_edit { # set by id
1341 0     0 1 0 my $self = shift;
1342 0         0 my %data = @_;
1343 0 0       0 return 0 unless $self->ping;
1344 0 0       0 unless ($data{id}) {
1345 0         0 $self->error("No id of user specified");
1346 0         0 return 0;
1347             }
1348              
1349             # Set
1350             $self->query(DML_USER_EDIT,
1351             $data{name}, $data{email}, $data{role}, $data{comment},
1352             $data{id},
1353 0 0       0 ) or return 0;
1354              
1355             # Ok
1356 0         0 return 1;
1357             }
1358             sub user_del {
1359 2     2 1 955 my $self = shift;
1360 2   50     10 my $username = shift // '';
1361 2 50       10 return 0 unless $self->ping;
1362 2 50       134 unless (length($username)) {
1363 0         0 $self->error("No username specified");
1364 0         0 return 0;
1365             }
1366              
1367             # Del
1368 2 50       27 $self->query(DML_USER_DEL, $username) or return 0;
1369              
1370             # Ok
1371 2         38520 return 1;
1372             }
1373             sub user_get {
1374 19     19 1 847 my $self = shift;
1375 19   50     75 my $username = shift // '';
1376 19 50       105 return () unless $self->ping;
1377 19 50       1557 unless (length $username) {
1378 0         0 $self->error("No username specified");
1379 0         0 return ();
1380             }
1381              
1382             # Get data
1383 19 50       96 if (my $res = $self->query(DML_USER_GET, $username)) {
1384 19         8435 my $r = $res->hash;
1385 19 100       990 return (%$r) if is_hash_ref($r);
1386             }
1387              
1388 10         120 return ();
1389             }
1390             sub user_getall {
1391 2     2 1 760 my $self = shift;
1392 2 50       11 return () unless $self->ping;
1393              
1394             # Get data
1395 2 50       119 if (my $res = $self->query(DML_USER_GETALL)) {
1396 2         719 my $r = $res->hashes;
1397 2 50       444 return (@$r) if is_array_ref($r);
1398             }
1399              
1400 0         0 return ();
1401             }
1402             sub user_search {
1403 0     0 1 0 my $self = shift;
1404 0   0     0 my $_search = shift // '';
1405 0 0       0 return () unless $self->ping;
1406              
1407             # Safe search string
1408 0         0 my $search = $self->dbh->quote(sprintf("%%%s%%", $_search));
1409 0         0 my @where;
1410 0 0       0 push @where, "AND UPPER(`username`) LIKE UPPER($search)" if $_search;
1411              
1412             # Get data
1413 0 0       0 if (my $res = $self->query(sprintf(DML_USER_SEARCH, join("\n", @where)))) {
1414 0         0 my $r = $res->hashes;
1415 0 0       0 return (@$r) if is_array_ref($r);
1416             }
1417              
1418 0         0 return ();
1419             }
1420             sub user_groups {
1421 0     0 1 0 my $self = shift;
1422 0   0     0 my $username = shift // '';
1423 0 0       0 return () unless $self->ping;
1424 0 0       0 unless (length $username) {
1425 0         0 $self->error("No username specified");
1426 0         0 return ();
1427             }
1428              
1429             # Get data
1430 0 0       0 if (my $res = $self->query(DML_USER_GROUPS, $username)) {
1431 0         0 my $r = $res->hashes;
1432 0 0       0 return (@$r) if is_array_ref($r);
1433             }
1434              
1435 0         0 return ();
1436             }
1437             sub user_tokens {
1438 0     0 1 0 my $self = shift;
1439 0   0     0 my $username = shift // '';
1440 0 0       0 return () unless $self->ping;
1441 0 0       0 unless (length $username) {
1442 0         0 $self->error("No username specified");
1443 0         0 return ();
1444             }
1445              
1446             # Get data
1447 0 0       0 if (my $res = $self->query(DML_TOKEN_GET_BY_USERNAME, $username)) {
1448 0         0 my $r = $res->hashes;
1449 0 0       0 return (@$r) if is_array_ref($r);
1450             }
1451              
1452 0         0 return ();
1453             }
1454             sub user_passwd {
1455 1     1 1 3 my $self = shift;
1456 1         6 my %data = @_;
1457 1 50       4 return 0 unless $self->ping;
1458 1 50 50     67 unless (length($data{username} // '')) {
1459 0         0 $self->error("No username specified");
1460 0         0 return 0;
1461             }
1462              
1463             # Passwd
1464 1 50       5 $self->query(DML_PASSWD, $data{password}, $data{username}) or return 0;
1465              
1466             # Ok
1467 1         14722 return 1;
1468             }
1469             sub user_setkeys {
1470 1     1 1 1 my $self = shift;
1471 1         4 my %data = @_;
1472 1 50       3 return 0 unless $self->ping;
1473 1 50       38 unless ($data{id}) {
1474 0         0 $self->error("No id of user specified");
1475 0         0 return 0;
1476             }
1477              
1478             # Set
1479 1 50       5 $self->query(DML_USER_SETKEYS, $data{public_key}, $data{private_key}, $data{id}) or return 0;
1480              
1481             # Ok
1482 1         16726 return 1;
1483             }
1484              
1485             # Group CRUDs
1486             sub group_add {
1487 7     7 1 1462 my $self = shift;
1488 7         40 my %data = @_;
1489 7 50       27 return 0 unless $self->ping;
1490              
1491             # Add
1492 7 50       434 $self->query(DML_GROUP_ADD, $data{groupname}, $data{description}) or return 0;
1493              
1494             # Ok
1495 7         147778 return 1;
1496             }
1497             sub group_set {
1498 2     2 1 487 my $self = shift;
1499 2         8 my %data = @_;
1500 2 50       9 return 0 unless $self->ping;
1501 2 50 50     95 unless (length($data{groupname} // '')) {
1502 0         0 $self->error("No groupname specified");
1503 0         0 return 0;
1504             }
1505              
1506             # Set
1507 2 50       10 $self->query(DML_GROUP_SET, $data{description}, $data{groupname}) or return 0;
1508              
1509             # Ok
1510 2         21845 return 1;
1511             }
1512             sub group_del {
1513 2     2 1 1009 my $self = shift;
1514 2   50     13 my $groupname = shift // '';
1515 2 50       13 return 0 unless $self->ping;
1516 2 50       151 unless (length($groupname)) {
1517 0         0 $self->error("No groupname specified");
1518 0         0 return 0;
1519             }
1520              
1521             # Del
1522 2 50       11 $self->query(DML_GROUP_DEL, $groupname) or return 0;
1523              
1524             # Ok
1525 2         44562 return 1;
1526             }
1527             sub group_get {
1528 9     9 1 748 my $self = shift;
1529 9   50     37 my $groupname = shift // '';
1530 9 50       42 return () unless $self->ping;
1531 9 50       621 unless (length $groupname) {
1532 0         0 $self->error("No groupname specified");
1533 0         0 return ();
1534             }
1535              
1536             # Get data
1537 9 50       43 if (my $res = $self->query(DML_GROUP_GET, $groupname)) {
1538 9         3578 my $r = $res->hash;
1539 9 100       333 return (%$r) if is_hash_ref($r);
1540             }
1541              
1542 6         56 return ();
1543             }
1544             sub group_getall {
1545 2     2 1 840 my $self = shift;
1546 2 50       11 return () unless $self->ping;
1547              
1548             # Get data
1549 2 50       162 if (my $res = $self->query(DML_GROUP_GETALL)) {
1550 2         2445 my $r = $res->hashes;
1551 2 50       344 return (@$r) if is_array_ref($r);
1552             }
1553              
1554 0         0 return ();
1555             }
1556             sub group_members {
1557 6     6 1 64 my $self = shift;
1558 6   50     19 my $groupname = shift // '';
1559 6 50       15 return () unless $self->ping;
1560 6 50       294 unless (length $groupname) {
1561 0         0 $self->error("No groupname specified");
1562 0         0 return ();
1563             }
1564              
1565             # Get data
1566 6 50       18 if (my $res = $self->query(DML_GROUP_MEMBERS, $groupname)) {
1567 6         1673 my $r = $res->hashes;
1568 6 50       702 return (@$r) if is_array_ref($r);
1569             }
1570              
1571 0         0 return ();
1572             }
1573              
1574             # GrpUsr CRUDs
1575             sub grpusr_add {
1576 15     15 1 419 my $self = shift;
1577 15         66 my %data = @_;
1578 15 50       73 return 0 unless $self->ping;
1579              
1580             # Add
1581 15 50       745 $self->query(DML_GRPUSR_ADD, $data{groupname}, $data{username}) or return 0;
1582              
1583             # Ok
1584 15         263398 return 1;
1585             }
1586             sub grpusr_del {
1587 4     4 1 940 my $self = shift;
1588 4         23 my %data = @_;
1589 4 50       25 return 0 unless $self->ping;
1590              
1591             # Del
1592 4 50 33     363 if ($data{id} && is_integer($data{id})) { # By ID
    100          
    50          
1593 0 0       0 $self->query(DML_GRPUSR_DEL_BY_ID, $data{id}) or return 0;
1594             } elsif ($data{groupname}) { # By Group
1595 3 50       18 $self->query(DML_GRPUSR_DEL_BY_GROUP, $data{groupname}) or return 0;
1596             } elsif ($data{username}) { # By User
1597 1 50       7 $self->query(DML_GRPUSR_DEL_BY_USER, $data{username}) or return 0;
1598             } else {
1599 0         0 $self->error("No any conditions specified");
1600 0         0 return 0;
1601             }
1602              
1603             # Ok
1604 4         28828 return 1;
1605             }
1606             sub grpusr_get {
1607 20     20 1 815 my $self = shift;
1608 20         95 my %data = @_;
1609 20 50       122 return () unless $self->ping;
1610              
1611             # Get data
1612 20 50 33     1757 if ($data{id} && is_integer($data{id})) { # By ID
    100 66        
    100          
    50          
1613 0 0       0 if (my $res = $self->query(DML_GRPUSR_GET_BY_ID, $data{id})) {
1614 0         0 my $r = $res->hash;
1615 0 0       0 return (%$r) if is_hash_ref($r);
1616             }
1617             } elsif ($data{groupname} and $data{username}) { # By Group and User
1618 14 50       72 if (my $res = $self->query(DML_GRPUSR_GET_BY_GROUP_USER, $data{groupname}, $data{username})) {
1619 14         6199 my $r = $res->hash;
1620 14 50       408 return (%$r) if is_hash_ref($r);
1621             }
1622             } elsif ($data{groupname}) { # By Group
1623 2 50       20 if (my $res = $self->query(DML_GRPUSR_GET_BY_GROUP, $data{groupname})) {
1624 2         611 my $r = $res->hashes;
1625 2 50       212 return (@$r) if is_array_ref($r);
1626             }
1627             } elsif ($data{username}) { # By User
1628 4 50       22 if (my $res = $self->query(DML_GRPUSR_GET_BY_USER, $data{username})) {
1629 4         1199 my $r = $res->hashes;
1630 4 50       5651 return (@$r) if is_array_ref($r);
1631             }
1632             } else {
1633 0         0 $self->error("No any conditions specified");
1634             }
1635              
1636 14         117 return ();
1637             }
1638              
1639             # Realm CRUDs
1640             sub realm_add {
1641 3     3 1 1116 my $self = shift;
1642 3         30 my %data = @_;
1643 3 50       23 return 0 unless $self->ping;
1644              
1645             # Add
1646             $self->query(DML_REALM_ADD,
1647             $data{realmname}, $data{realm}, $data{satisfy}, $data{description}
1648 3 50       212 ) or return 0;
1649              
1650             # Ok
1651 3         58060 return 1;
1652             }
1653             sub realm_set {
1654 1     1 1 529 my $self = shift;
1655 1         10 my %data = @_;
1656 1 50       8 return 0 unless $self->ping;
1657 1 50 50     106 unless (length($data{realmname} // '')) {
1658 0         0 $self->error("No realmname specified");
1659 0         0 return 0;
1660             }
1661              
1662             # Set
1663             $self->query(DML_REALM_SET,
1664             $data{realm}, $data{satisfy}, $data{description}, $data{realmname}
1665 1 50       11 ) or return 0;
1666              
1667             # Ok
1668 1         22326 return 1;
1669             }
1670             sub realm_del {
1671 2     2 1 1455 my $self = shift;
1672 2   50     12 my $realmname = shift // '';
1673 2 50       11 return 0 unless $self->ping;
1674 2 50       137 unless (length($realmname)) {
1675 0         0 $self->error("No realmname specified");
1676 0         0 return 0;
1677             }
1678              
1679             # Del
1680 2 50       11 $self->query(DML_REALM_DEL, $realmname) or return 0;
1681              
1682             # Ok
1683 2         36464 return 1;
1684             }
1685             sub realm_get {
1686 5     5 1 700 my $self = shift;
1687 5   50     23 my $realmname = shift // '';
1688 5 50       26 return () unless $self->ping;
1689 5 50       320 unless (length $realmname) {
1690 0         0 $self->error("No realmname specified");
1691 0         0 return ();
1692             }
1693              
1694             # Get data
1695 5 50       22 if (my $res = $self->query(DML_REALM_GET, $realmname)) {
1696 5         1779 my $r = $res->hash;
1697 5 100       258 return (%$r) if is_hash_ref($r);
1698             }
1699              
1700 2         17 return ();
1701             }
1702             sub realm_getall {
1703 2     2 1 820 my $self = shift;
1704 2 50       9 return () unless $self->ping;
1705              
1706             # Get data
1707 2 50       122 if (my $res = $self->query(DML_REALM_GETALL)) {
1708 2         576 my $r = $res->hashes;
1709 2 50       194 return (@$r) if is_array_ref($r);
1710             }
1711              
1712 0         0 return ();
1713             }
1714             sub realm_requirement_add {
1715 3     3 1 517 my $self = shift;
1716 3         22 my %data = @_;
1717 3 50       14 return 0 unless $self->ping;
1718              
1719             # Add
1720             $self->query(DML_REQUIREMENT_ADD,
1721             $data{realmname}, $data{provider}, $data{entity}, $data{op}, $data{value}
1722 3 50       148 ) or return 0;
1723              
1724             # Ok
1725 3         48841 return 1;
1726             }
1727             sub realm_requirement_del {
1728 4     4 1 2103 my $self = shift;
1729 4   50     23 my $realmname = shift // '';
1730 4 50       26 return 0 unless $self->ping;
1731 4 50       445 unless (length($realmname)) {
1732 0         0 $self->error("No realmname specified");
1733 0         0 return 0;
1734             }
1735              
1736             # Del
1737 4 50       29 $self->query(DML_REQUIREMENT_DEL_BY_REALM, $realmname) or return 0;
1738              
1739             # Ok
1740 4         23477 return 1;
1741             }
1742             sub realm_requirements {
1743 3     3 1 888 my $self = shift;
1744 3         8 my $realmname = shift;
1745 3 50       12 return () unless $self->ping;
1746 3 50       156 unless ($realmname) {
1747 0         0 $self->error("No realmname specified");
1748 0         0 return ();
1749             }
1750              
1751             # Get data
1752 3 50       12 if (my $res = $self->query(DML_REQUIREMENT_GET_BY_REALM, $realmname)) {
1753 3         1018 my $r = $res->hashes;
1754 3 50       377 return (@$r) if is_array_ref($r);
1755             }
1756              
1757 0         0 return ();
1758             }
1759             sub realm_routes {
1760 0     0 1 0 my $self = shift;
1761 0         0 my $realmname = shift;
1762 0 0       0 return () unless $self->ping;
1763 0 0       0 unless ($realmname) {
1764 0         0 $self->error("No realmname specified");
1765 0         0 return ();
1766             }
1767              
1768             # Get data
1769 0 0       0 if (my $res = $self->query(DML_ROUTE_GET_BY_REALM, $realmname)) {
1770 0         0 my $r = $res->hashes;
1771 0 0       0 return (@$r) if is_array_ref($r);
1772             }
1773              
1774 0         0 return ();
1775             }
1776              
1777             # Route CRUDs
1778             sub route_add {
1779 1     1 1 2 my $self = shift;
1780 1         7 my %data = @_;
1781 1 50       3 return 0 unless $self->ping;
1782              
1783             # Add
1784             $self->query(DML_ROUTE_ADD,
1785             $data{realmname}, $data{routename}, $data{method},
1786             $data{url}, $data{base}, $data{path}
1787 1 50       38 ) or return 0;
1788              
1789             # Ok
1790 1         19097 return 1;
1791             }
1792             sub route_set {
1793 0     0 1 0 my $self = shift;
1794 0         0 my %data = @_;
1795 0 0       0 return 0 unless $self->ping;
1796 0 0       0 unless ($data{id}) {
1797 0         0 $self->error("No route id specified");
1798 0         0 return 0;
1799             }
1800              
1801             # Set
1802             $self->query(DML_ROUTE_SET,
1803             $data{realmname}, $data{method},
1804             $data{url}, $data{base}, $data{path},
1805             $data{routename}
1806 0 0       0 ) or return 0;
1807              
1808             # Ok
1809 0         0 return 1;
1810             }
1811             sub route_del {
1812 1     1 1 2 my $self = shift;
1813 1   50     6 my $routename = shift // '';
1814 1 50       5 return 0 unless $self->ping;
1815 1 50       93 unless (length($routename)) {
1816 0         0 $self->error("No routename specified");
1817 0         0 return 0;
1818             }
1819              
1820             # Del
1821 1 50       6 $self->query(DML_ROUTE_DEL_BY_ROUTE, $routename) or return 0;
1822              
1823             # Ok
1824 1         18106 return 1;
1825             }
1826             sub route_get {
1827 2     2 1 5 my $self = shift;
1828 2   50     10 my $routename = shift // '';
1829 2 50       13 return () unless $self->ping;
1830 2 50       219 unless ($routename) {
1831 0         0 $self->error("No routename specified");
1832 0         0 return ();
1833             }
1834              
1835             # Get data
1836 2 50       12 if (my $res = $self->query(DML_ROUTE_GET_BY_ROUTE, $routename)) {
1837 2         946 my $r = $res->hash;
1838 2 100       102 return (%$r) if is_hash_ref($r);
1839             }
1840              
1841 1         8 return ();
1842             }
1843             sub route_getall {
1844 3     3 1 7 my $self = shift;
1845 3 50       12 return () unless $self->ping;
1846              
1847             # Get data
1848 3 50       174 if (my $res = $self->query(DML_ROUTE_GETALL)) {
1849 3         912 my $r = $res->hashes;
1850 3 50       301 return (@$r) if is_array_ref($r);
1851             }
1852              
1853 0         0 return ();
1854             }
1855             sub route_search {
1856 0     0 1 0 my $self = shift;
1857 0   0     0 my $_search = shift // '';
1858 0 0       0 return () unless $self->ping;
1859              
1860             # Safe search string
1861 0         0 my $search = $self->dbh->quote(sprintf("%%%s%%", $_search));
1862 0         0 my @where;
1863 0 0       0 push @where, "AND UPPER(`routename`) LIKE UPPER($search)" if $_search;
1864              
1865             # Get data
1866 0 0       0 if (my $res = $self->query(sprintf(DML_ROUTE_SEARCH, join("\n", @where)))) {
1867 0         0 my $r = $res->hashes;
1868 0 0       0 return (@$r) if is_array_ref($r);
1869             }
1870              
1871 0         0 return ();
1872             }
1873             sub route_release {
1874 3     3 1 11 my $self = shift;
1875 3   50     16 my $realmname = shift // '';
1876 3 50       15 return 0 unless $self->ping;
1877 3 50       173 unless (length($realmname)) {
1878 0         0 $self->error("No realmname specified");
1879 0         0 return 0;
1880             }
1881              
1882             # Set
1883 3 50       14 $self->query(DML_ROUTE_RELEASE_BY_REALM, $realmname) or return 0;
1884              
1885             # Ok
1886 3         1108 return 1;
1887             }
1888             sub route_assign {
1889 0     0 1 0 my $self = shift;
1890 0         0 my %data = @_;
1891 0 0       0 return 0 unless $self->ping;
1892 0 0 0     0 unless (defined($data{realmname}) && length($data{realmname})) {
1893 0         0 $self->error("No realmname specified");
1894 0         0 return 0;
1895             }
1896 0 0 0     0 unless (defined($data{routename}) && length($data{routename})) {
1897 0         0 $self->error("No routename specified");
1898 0         0 return 0;
1899             }
1900              
1901             # Set
1902 0 0       0 $self->query(DML_ROUTE_ASSIGN_BY_ROUTE, $data{realmname}, $data{routename}) or return 0;
1903              
1904             # Ok
1905 0         0 return 1;
1906             }
1907              
1908             # Token CRUDs
1909             sub token_add {
1910 1     1 1 3 my $self = shift;
1911 1         7 my %data = @_;
1912 1 50       19 return 0 unless $self->ping;
1913              
1914             # Add
1915             $self->query(DML_TOKEN_ADD,
1916             $data{jti}, $data{username}, $data{type}, $data{clientid},
1917             $data{iat}, $data{exp}, $data{address}, $data{description}
1918 1 50       45 ) or return 0;
1919              
1920             # Ok
1921 1         15734 return 1;
1922             }
1923             sub token_set {
1924 0     0 1 0 my $self = shift;
1925 0         0 my %data = @_;
1926 0 0       0 return 0 unless $self->ping;
1927 0 0       0 unless ($data{id}) {
1928 0         0 $self->error("No token id specified");
1929 0         0 return 0;
1930             }
1931              
1932             # Set
1933             $self->query(DML_TOKEN_SET,
1934             $data{jti}, $data{username}, $data{type}, $data{clientid},
1935             $data{iat}, $data{exp}, $data{address}, $data{description},
1936             $data{id}
1937 0 0       0 ) or return 0;
1938              
1939             # Ok
1940 0         0 return 1;
1941             }
1942             sub token_del {
1943 2     2 1 4 my $self = shift;
1944 2   100     15 my $id = shift || 0;
1945 2 50       11 return 0 unless $self->ping;
1946              
1947             # Del
1948 2 100       137 if ($id) {
1949             # Delete by ID
1950 1 50       4 $self->query(DML_TOKEN_DEL, $id) or return 0;
1951             } else {
1952             # Delete all expired tokens
1953 1 50       8 $self->query(DML_TOKEN_DEL_EXPIRED, time) or return 0;
1954             }
1955              
1956             # Ok
1957 2         19448 return 1;
1958             }
1959             sub token_get {
1960 0     0 1 0 my $self = shift;
1961 0   0     0 my $id = shift // 0;
1962 0 0       0 return () unless $self->ping;
1963 0 0 0     0 unless ($id && is_integer($id)) {
1964 0         0 $self->error("No token id specified");
1965 0         0 return ();
1966             }
1967              
1968             # Get data
1969 0 0       0 if (my $res = $self->query(DML_TOKEN_GET, $id)) {
1970 0         0 my $r = $res->hash;
1971 0 0       0 return (%$r) if is_hash_ref($r);
1972             }
1973              
1974 0         0 return ();
1975             }
1976             sub token_getall {
1977 1     1 1 3 my $self = shift;
1978 1 50       10 return () unless $self->ping;
1979              
1980             # Get data
1981 1 50       115 if (my $res = $self->query(DML_TOKEN_GET_ALL)) {
1982 1         544 my $r = $res->hashes;
1983 1 50       164 return (@$r) if is_array_ref($r);
1984             }
1985              
1986 0         0 return ();
1987             }
1988             sub token_get_cond {
1989 1     1 1 2 my $self = shift;
1990 1   50     4 my $cond = shift // '';
1991 1         3 my %data = @_;
1992 1 50       6 return () unless $self->ping;
1993              
1994 1         71 my $res;
1995              
1996             # Username and ClientID
1997 1 50       7 if ($cond eq 'session') { # username and clinetid
    50          
1998 0         0 $res = $self->query(DML_TOKEN_GET_BY_USERNAME_AND_CLIENTID, $data{username}, $data{clientid});
1999             } elsif ($cond eq 'api') { # username and jti
2000 1         7 $res = $self->query(DML_TOKEN_GET_BY_USERNAME_AND_JTI, $data{username}, $data{jti});
2001             } else {
2002 0         0 $self->error("No any conditions specified");
2003 0         0 return ();
2004             }
2005              
2006             # Result
2007 1 50       355 if ($res) {
2008 1         5 my $r = $res->hash;
2009 1 50       40 return (%$r) if is_hash_ref($r);
2010             }
2011              
2012 0         0 return ();
2013             }
2014              
2015             sub _get_patches {
2016 2     2   8 my $self = shift;
2017 2   33     12 my $from = shift // $VERSION; # start from version
2018 2         8 my $patches = SCHEMA_PATCHES;
2019 2         7 my @labels = ();
2020 2         27 foreach my $v (sort keys %$patches) {
2021 6 50       52 push @labels, $patches->{$v} if ($v * 1) > ($from * 1);
2022             }
2023 2         17 return [@labels];
2024             }
2025              
2026             1;
2027              
2028             # !! Not forget add any new patch label to SCHEMA_PATCHES !!
2029              
2030             __DATA__