| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package WWW::Suffit::Plugin::ConfigAuth; |
|
2
|
1
|
|
|
1
|
|
165761
|
use strict; |
|
|
1
|
|
|
|
|
8
|
|
|
|
1
|
|
|
|
|
46
|
|
|
3
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
82
|
|
|
4
|
1
|
|
|
1
|
|
805
|
use utf8; |
|
|
1
|
|
|
|
|
612
|
|
|
|
1
|
|
|
|
|
26
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=encoding utf8 |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
WWW::Suffit::Plugin::ConfigAuth - The Suffit plugin for authentication and authorization providing via configuration |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
sub startup { |
|
15
|
|
|
|
|
|
|
my $self = shift->SUPER::startup(); |
|
16
|
|
|
|
|
|
|
$self->plugin('ConfigAuth', { |
|
17
|
|
|
|
|
|
|
configsection => 'auth', |
|
18
|
|
|
|
|
|
|
}); |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# . . . |
|
21
|
|
|
|
|
|
|
} |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
... configuration: |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# ConfigAuth configuration |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
User test:5f4dcc3b5aa765d61d8327deb882cf99:role=Regular%20user |
|
28
|
|
|
|
|
|
|
User admin:5f4dcc3b5aa765d61d8327deb882cf99 |
|
29
|
|
|
|
|
|
|
User foo:bar:status=1&uid=2&name=user |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
The Suffit plugin for authentication and authorization providing via configuration |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head1 OPTIONS |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
This plugin supports the following options |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head2 configsection |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
configsection => 'auth' |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
This option sets a section name of the config file for define |
|
45
|
|
|
|
|
|
|
namespace of configuration directives for this plugin |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Default: none (without section) |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=head1 HELPERS |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
This plugin provides the following helpers |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=head2 configauth.init |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
my $init = $self->configauth->init; |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
This method returns the init object (L) |
|
58
|
|
|
|
|
|
|
that contains data of initialization: |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
{ |
|
61
|
|
|
|
|
|
|
error => '...', # Error message |
|
62
|
|
|
|
|
|
|
status => 500, # HTTP status code |
|
63
|
|
|
|
|
|
|
code => 'E7000', # The Suffit error code |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
For example (in your controller): |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# Check init status |
|
69
|
|
|
|
|
|
|
my $init = $self->configauth->init; |
|
70
|
|
|
|
|
|
|
if (my $err = $init->get('/error')) { |
|
71
|
|
|
|
|
|
|
$self->reply->error($init->get('/status'), |
|
72
|
|
|
|
|
|
|
$init->get('/code'), $err); |
|
73
|
|
|
|
|
|
|
return; |
|
74
|
|
|
|
|
|
|
} |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=head2 configauth.authenticate |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
my $auth = $self->configauth->authenticate({ |
|
79
|
|
|
|
|
|
|
username => $username, |
|
80
|
|
|
|
|
|
|
password => $password, |
|
81
|
|
|
|
|
|
|
loginpage => 'login', # -- To login-page!! |
|
82
|
|
|
|
|
|
|
expiration => $remember ? SESSION_EXPIRE_MAX : SESSION_EXPIRATION, |
|
83
|
|
|
|
|
|
|
realm => "Test zone", |
|
84
|
|
|
|
|
|
|
}); |
|
85
|
|
|
|
|
|
|
if (my $err = $auth->get('/error')) { |
|
86
|
|
|
|
|
|
|
if (my $location = $auth->get('/location')) { # Redirect |
|
87
|
|
|
|
|
|
|
$self->flash(message => $err); |
|
88
|
|
|
|
|
|
|
$self->redirect_to($location); # 'login' -- To login-page!! |
|
89
|
|
|
|
|
|
|
} elsif ($auth->get('/status') >= 500) { # Fatal server errors |
|
90
|
|
|
|
|
|
|
$self->reply->error($auth->get('/status'), $auth->get('/code'), $err); |
|
91
|
|
|
|
|
|
|
} else { # User errors (show on login page) |
|
92
|
|
|
|
|
|
|
$self->stash(error => $err); |
|
93
|
|
|
|
|
|
|
return $self->render; |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
return; |
|
96
|
|
|
|
|
|
|
} |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
This helper performs authentication backend subprocess and returns |
|
99
|
|
|
|
|
|
|
result object (L) that contains data structure: |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
{ |
|
102
|
|
|
|
|
|
|
error => '', # Error message |
|
103
|
|
|
|
|
|
|
status => 200, # HTTP status code |
|
104
|
|
|
|
|
|
|
code => 'E0000', # The Suffit error code |
|
105
|
|
|
|
|
|
|
username => $username, # User name |
|
106
|
|
|
|
|
|
|
referer => $referer, # Referer |
|
107
|
|
|
|
|
|
|
loginpage => $loginpage, # Login page for redirects (location) |
|
108
|
|
|
|
|
|
|
location => undef, # Location URL for redirects |
|
109
|
|
|
|
|
|
|
} |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=head2 configauth.authorize |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
my $auth = $self->configauth->authorize({ |
|
114
|
|
|
|
|
|
|
referer => $referer, |
|
115
|
|
|
|
|
|
|
username => $username, |
|
116
|
|
|
|
|
|
|
loginpage => 'login', # -- To login-page!! |
|
117
|
|
|
|
|
|
|
}); |
|
118
|
|
|
|
|
|
|
if (my $err = $auth->get('/error')) { |
|
119
|
|
|
|
|
|
|
if (my $location = $auth->get('/location')) { |
|
120
|
|
|
|
|
|
|
$self->flash(message => $err); |
|
121
|
|
|
|
|
|
|
$self->redirect_to($location); # 'login' -- To login-page!! |
|
122
|
|
|
|
|
|
|
} else { |
|
123
|
|
|
|
|
|
|
$self->reply->error($auth->get('/status'), $auth->get('/code'), $err); |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
return; |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
This helper performs authorization backend subprocess and returns |
|
129
|
|
|
|
|
|
|
result object (L) that contains data structure: |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
{ |
|
132
|
|
|
|
|
|
|
error => '', # Error message |
|
133
|
|
|
|
|
|
|
status => 200, # HTTP status code |
|
134
|
|
|
|
|
|
|
code => 'E0000', # The Suffit error code |
|
135
|
|
|
|
|
|
|
username => $username, # User name |
|
136
|
|
|
|
|
|
|
referer => $referer, # Referer |
|
137
|
|
|
|
|
|
|
loginpage => $loginpage, # Login page for redirects (location) |
|
138
|
|
|
|
|
|
|
location => undef, # Location URL for redirects |
|
139
|
|
|
|
|
|
|
user => { # User data |
|
140
|
|
|
|
|
|
|
address => "127.0.0.1", # User (client) IP address |
|
141
|
|
|
|
|
|
|
base => "http://localhost:8080", # Base URL of request |
|
142
|
|
|
|
|
|
|
comment => "No comments", # Comment |
|
143
|
|
|
|
|
|
|
email => 'test@example.com', # Email address |
|
144
|
|
|
|
|
|
|
email_md5 => "a84450...366", # MD5 hash of email address |
|
145
|
|
|
|
|
|
|
method => "ANY", # Current method of request |
|
146
|
|
|
|
|
|
|
name => "Bob Smith", # Full user name |
|
147
|
|
|
|
|
|
|
path => "/", # Current query-path of request |
|
148
|
|
|
|
|
|
|
role => "Regular user", # User role |
|
149
|
|
|
|
|
|
|
status => true, # User status in JSON::PP::Boolean notation |
|
150
|
|
|
|
|
|
|
uid => 1, # User ID |
|
151
|
|
|
|
|
|
|
username => $username, # User name |
|
152
|
|
|
|
|
|
|
}, |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=head1 METHODS |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Internal methods |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=head2 register |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
This method register the plugin and helpers in L application |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
L, L |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=head1 AUTHOR |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Serż Minus (Sergey Lepenkov) L Eabalama@cpan.orgE |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
Copyright (C) 1998-2026 D&D Corporation |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head1 LICENSE |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
This program is distributed under the terms of the Artistic License Version 2.0 |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
See the C file or L for details |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=cut |
|
182
|
|
|
|
|
|
|
|
|
183
|
1
|
|
|
1
|
|
690
|
use Mojo::Base 'Mojolicious::Plugin'; |
|
|
1
|
|
|
|
|
16139
|
|
|
|
1
|
|
|
|
|
9
|
|
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
our $VERSION = '1.01'; |
|
186
|
|
|
|
|
|
|
|
|
187
|
1
|
|
|
1
|
|
2950
|
use Digest::SHA qw/sha224_hex sha256_hex sha384_hex sha512_hex/; |
|
|
1
|
|
|
|
|
5153
|
|
|
|
1
|
|
|
|
|
170
|
|
|
188
|
1
|
|
|
1
|
|
750
|
use Mojo::File qw/path/; |
|
|
1
|
|
|
|
|
315871
|
|
|
|
1
|
|
|
|
|
107
|
|
|
189
|
1
|
|
|
1
|
|
14
|
use Mojo::Util qw/encode md5_sum sha1_sum hmac_sha1_sum secure_compare/; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
79
|
|
|
190
|
1
|
|
|
1
|
|
874
|
use Mojo::JSON::Pointer; |
|
|
1
|
|
|
|
|
1226
|
|
|
|
1
|
|
|
|
|
10
|
|
|
191
|
1
|
|
|
1
|
|
797
|
use Mojo::Parameters; |
|
|
1
|
|
|
|
|
3643
|
|
|
|
1
|
|
|
|
|
8
|
|
|
192
|
1
|
|
|
1
|
|
945
|
use WWW::Suffit::Const qw/ :session /; |
|
|
1
|
|
|
|
|
3642
|
|
|
|
1
|
|
|
|
|
297
|
|
|
193
|
1
|
|
|
1
|
|
782
|
use WWW::Suffit::Util qw/json_load json_save/; |
|
|
1
|
|
|
|
|
72192
|
|
|
|
1
|
|
|
|
|
3745
|
|
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub register { |
|
196
|
0
|
|
|
0
|
1
|
|
my ($plugin, $app, $opts) = @_; # $self = $plugin |
|
197
|
0
|
|
0
|
|
|
|
$opts //= {}; |
|
198
|
0
|
|
|
|
|
|
my $configsection = $opts->{configsection}; |
|
199
|
0
|
|
|
|
|
|
my %payload = ( # Ok by default |
|
200
|
|
|
|
|
|
|
error => '', # Error message |
|
201
|
|
|
|
|
|
|
status => 200, # HTTP status code |
|
202
|
|
|
|
|
|
|
code => 'E0000', # The Suffit error code |
|
203
|
|
|
|
|
|
|
); |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# List of users from config |
|
206
|
|
|
|
|
|
|
$app->helper('configauth.users' => sub { |
|
207
|
0
|
|
|
0
|
|
|
my $c = shift; |
|
208
|
0
|
0
|
|
|
|
|
state $users = $configsection |
|
209
|
|
|
|
|
|
|
? $c->conf->list("/$configsection/user") |
|
210
|
|
|
|
|
|
|
: $c->conf->list("/user"); |
|
211
|
0
|
|
|
|
|
|
}); |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# Auth helpers (methods) |
|
214
|
0
|
|
|
|
|
|
$app->helper('configauth.authenticate'=> \&_authenticate); |
|
215
|
0
|
|
|
|
|
|
$app->helper('configauth.authorize' => \&_authorize); |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# Check users |
|
218
|
0
|
|
|
|
|
|
my $users = $app->configauth->users; |
|
219
|
0
|
0
|
|
|
|
|
unless (scalar @$users) { |
|
220
|
0
|
0
|
|
|
|
|
my $err = $configsection |
|
221
|
|
|
|
|
|
|
? sprintf("No any users found in %s section of configuration", $configsection) |
|
222
|
|
|
|
|
|
|
: "No any users found in configuration"; |
|
223
|
0
|
|
|
|
|
|
$app->log->error(sprintf("[E7010] %s", $err)); |
|
224
|
0
|
|
|
|
|
|
$payload{error} = $err; |
|
225
|
0
|
|
|
|
|
|
$payload{status} = 500; |
|
226
|
0
|
|
|
|
|
|
$payload{code} = 'E7010'; |
|
227
|
0
|
|
|
0
|
|
|
return $app->helper('configauth.init' => sub { Mojo::JSON::Pointer->new({%payload}) }); |
|
|
0
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
#$app->log->error(Mojo::Util::dumper($users)); |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# Ok |
|
232
|
0
|
|
|
0
|
|
|
return $app->helper('configauth.init' => sub { Mojo::JSON::Pointer->new({%payload}) }); |
|
|
0
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
sub _authenticate { |
|
235
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
236
|
0
|
0
|
|
|
|
|
my %args = scalar(@_) ? scalar(@_) % 2 ? ref($_[0]) eq 'HASH' ? (%{$_[0]}) : () : (@_) : (); |
|
|
0
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
|
my $cache = $self->app->cache; |
|
238
|
0
|
|
|
|
|
|
my $now = time(); |
|
239
|
0
|
|
0
|
|
|
|
my $username = $args{username} || ''; |
|
240
|
0
|
|
0
|
|
|
|
my $password = $args{password} // ''; |
|
241
|
0
|
0
|
|
|
|
|
$password = encode('UTF-8', $password) if length $password; # chars to bytes |
|
242
|
0
|
|
0
|
|
|
|
my $referer = $args{referer} // $self->req->headers->header("Referer") // ''; |
|
|
|
|
0
|
|
|
|
|
|
243
|
0
|
|
0
|
|
|
|
my $loginpage = $args{loginpage} // ''; |
|
244
|
0
|
|
0
|
|
|
|
my $expiration = $args{expiration} || 0; |
|
245
|
0
|
|
|
|
|
|
my %payload = ( # Ok by default |
|
246
|
|
|
|
|
|
|
error => '', # Error message |
|
247
|
|
|
|
|
|
|
status => 200, # HTTP status code |
|
248
|
|
|
|
|
|
|
code => 'E0000', # The Suffit error code |
|
249
|
|
|
|
|
|
|
username => $username, # User name |
|
250
|
|
|
|
|
|
|
referer => $referer, # Referer |
|
251
|
|
|
|
|
|
|
loginpage => $loginpage, # Login page for redirects (location) |
|
252
|
|
|
|
|
|
|
location => undef, # Location URL for redirects |
|
253
|
|
|
|
|
|
|
); |
|
254
|
0
|
|
|
|
|
|
my $json_file = path($self->app->datadir, sprintf("u.%s.json", $username)); |
|
255
|
0
|
|
|
|
|
|
my $file = $json_file->to_string; |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# Check username |
|
258
|
0
|
0
|
|
|
|
|
unless (length $username) { |
|
259
|
0
|
|
|
|
|
|
$self->log->error("[E7001] Incorrect username"); |
|
260
|
0
|
|
|
|
|
|
$payload{error} = "Incorrect username"; |
|
261
|
0
|
|
|
|
|
|
$payload{status} = 400; |
|
262
|
0
|
|
|
|
|
|
$payload{code} = 'E7001'; |
|
263
|
0
|
|
|
|
|
|
return Mojo::JSON::Pointer->new({%payload}); |
|
264
|
|
|
|
|
|
|
} |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# Get user key and file |
|
267
|
0
|
|
|
|
|
|
my $ustat_key = sprintf("auth.ustat.%s", hmac_sha1_sum(sprintf("%s:%s", encode('UTF-8', $username), $password), $self->app->mysecret)); |
|
268
|
0
|
|
0
|
|
|
|
my $ustat_tm = $cache->get($ustat_key) || 0; |
|
269
|
0
|
0
|
0
|
|
|
|
if ($expiration && (-e $file) && ($ustat_tm + $expiration) > $now) { # Ok! |
|
|
|
|
0
|
|
|
|
|
|
270
|
0
|
|
|
|
|
|
$self->log->debug(sprintf("$$: User data is still valid. Expired at %s", scalar(localtime($ustat_tm + $expiration)))); |
|
271
|
0
|
|
|
|
|
|
return Mojo::JSON::Pointer->new({%payload}); |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
# Get password database from cache |
|
275
|
0
|
|
|
|
|
|
my $pwdb = $cache->get('auth.pwdb'); |
|
276
|
0
|
0
|
|
|
|
|
unless ($pwdb) { |
|
277
|
0
|
|
0
|
|
|
|
my $users = $self->configauth->users || []; |
|
278
|
0
|
|
|
|
|
|
$pwdb = { (_parse_pwdb_lines(@$users)) }; |
|
279
|
0
|
|
|
|
|
|
$cache->set('auth.pwdb' => $pwdb); # store whole password database to cache |
|
280
|
|
|
|
|
|
|
#$self->log->error(Mojo::Util::dumper( $pwdb )); |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# Authentication: Check by password database |
|
284
|
0
|
|
0
|
|
|
|
my $pw = encode('UTF-8', $pwdb->{$username}->{pwd} // ''); |
|
285
|
0
|
|
0
|
|
|
|
my $ar = Mojo::Parameters->new($pwdb->{$username}->{arg} // '')->charset('UTF-8'); |
|
286
|
0
|
0
|
|
|
|
|
unless (_check_pw($password, $pw)) { # Oops. Incorrect username/password |
|
287
|
0
|
|
|
|
|
|
$self->log->error(sprintf("[%s] %s: %s", 401, 'E7005', 'Incorrect username/password')); |
|
288
|
0
|
|
|
|
|
|
$payload{error} = 'Incorrect username/password'; |
|
289
|
0
|
|
|
|
|
|
$payload{status} = 401; |
|
290
|
0
|
|
|
|
|
|
$payload{code} = 'E7005'; |
|
291
|
0
|
|
|
|
|
|
return Mojo::JSON::Pointer->new({%payload}); |
|
292
|
|
|
|
|
|
|
} |
|
293
|
|
|
|
|
|
|
#$self->log->error(Mojo::Util::dumper( $ar )); |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# User data with required fields! |
|
296
|
0
|
|
0
|
|
|
|
my $data = $ar->to_hash || {}; |
|
297
|
0
|
|
|
|
|
|
$data->{address} = $self->remote_ip($self->app->trustedproxies); |
|
298
|
0
|
|
0
|
|
|
|
$data->{base} = $args{base_url} || $self->base_url; |
|
299
|
0
|
|
0
|
|
|
|
$data->{method} = $args{method} || $self->req->method || "ANY"; |
|
300
|
0
|
|
0
|
|
|
|
$data->{path} = $self->req->url->path->to_string || "/"; |
|
301
|
0
|
|
|
|
|
|
$data->{referer} = $referer; |
|
302
|
|
|
|
|
|
|
# required fields: |
|
303
|
0
|
0
|
|
|
|
|
$data->{status} = $data->{status} ? \1 : \0; |
|
304
|
0
|
|
0
|
|
|
|
$data->{uid} ||= 0; |
|
305
|
0
|
|
0
|
|
|
|
$data->{username} //= $username; |
|
306
|
0
|
|
0
|
|
|
|
$data->{name} //= $username; |
|
307
|
0
|
|
0
|
|
|
|
$data->{role} //= ''; |
|
308
|
0
|
|
0
|
|
|
|
$data->{email} //= ''; |
|
309
|
|
|
|
|
|
|
$data->{email_md5} //= $data->{email} ? md5_sum($data->{email}) : '', |
|
310
|
0
|
0
|
0
|
|
|
|
$data->{comment} //= ''; |
|
|
|
|
0
|
|
|
|
|
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
# Save json file with user data |
|
313
|
0
|
|
|
|
|
|
json_save($file, $data); |
|
314
|
0
|
0
|
|
|
|
|
unless (-e $file) { |
|
315
|
0
|
|
|
|
|
|
$self->log->error(sprintf("[E7007] Can't save file %s", $file)); |
|
316
|
0
|
|
|
|
|
|
$payload{error} = sprintf("Can't save file DATADIR/u.%s.json", $username); |
|
317
|
0
|
|
|
|
|
|
$payload{status} = 500; |
|
318
|
0
|
|
|
|
|
|
$payload{code} = 'E7007'; |
|
319
|
0
|
|
|
|
|
|
return Mojo::JSON::Pointer->new({%payload}); |
|
320
|
|
|
|
|
|
|
} |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
# Fixed to cache |
|
323
|
0
|
|
|
|
|
|
$cache->set($ustat_key, $now); |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# Ok |
|
326
|
0
|
|
|
|
|
|
return Mojo::JSON::Pointer->new({%payload}); |
|
327
|
|
|
|
|
|
|
} |
|
328
|
|
|
|
|
|
|
sub _authorize { |
|
329
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
330
|
0
|
0
|
|
|
|
|
my %args = scalar(@_) ? scalar(@_) % 2 ? ref($_[0]) eq 'HASH' ? (%{$_[0]}) : () : (@_) : (); |
|
|
0
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
331
|
0
|
|
0
|
|
|
|
my $username = $args{username} || ''; |
|
332
|
0
|
|
0
|
|
|
|
my $referer = $args{referer} // $self->req->headers->header("Referer") // ''; |
|
|
|
|
0
|
|
|
|
|
|
333
|
0
|
|
0
|
|
|
|
my $loginpage = $args{loginpage} // ''; |
|
334
|
0
|
|
|
|
|
|
my %payload = ( # Ok by default |
|
335
|
|
|
|
|
|
|
error => '', # Error message |
|
336
|
|
|
|
|
|
|
status => 200, # HTTP status code |
|
337
|
|
|
|
|
|
|
code => 'E0000', # The Suffit error code |
|
338
|
|
|
|
|
|
|
username => $username, # User name |
|
339
|
|
|
|
|
|
|
referer => $referer, # Referer |
|
340
|
|
|
|
|
|
|
loginpage => $loginpage, # Login page for redirects (location) |
|
341
|
|
|
|
|
|
|
location => undef, # Location URL for redirects |
|
342
|
|
|
|
|
|
|
user => { # User data with required fields (defaults) |
|
343
|
|
|
|
|
|
|
status => \0, # User status |
|
344
|
|
|
|
|
|
|
uid => 0, # User ID |
|
345
|
|
|
|
|
|
|
username => $username, # User name |
|
346
|
|
|
|
|
|
|
name => $username, # Full name |
|
347
|
|
|
|
|
|
|
role => "", # User role |
|
348
|
|
|
|
|
|
|
email => "", # Email address |
|
349
|
|
|
|
|
|
|
email_md5 => "", # MD5 of email address |
|
350
|
|
|
|
|
|
|
comment => "", # Comment |
|
351
|
|
|
|
|
|
|
}, |
|
352
|
|
|
|
|
|
|
); |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# Check username |
|
355
|
0
|
0
|
|
|
|
|
unless (length $username) { |
|
356
|
0
|
|
|
|
|
|
$self->log->error("[E7009] Incorrect username"); |
|
357
|
0
|
|
|
|
|
|
$payload{error} = "Incorrect username"; |
|
358
|
0
|
|
|
|
|
|
$payload{status} = 400; |
|
359
|
0
|
|
|
|
|
|
$payload{code} = 'E7009'; |
|
360
|
0
|
|
|
|
|
|
return Mojo::JSON::Pointer->new({%payload}); |
|
361
|
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
# Get user file name |
|
364
|
0
|
|
|
|
|
|
my $file = path($self->app->datadir, sprintf("u.%s.json", $username))->to_string; |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# Load user file with user data |
|
367
|
0
|
0
|
|
|
|
|
my $user = -e $file ? json_load($file) : {}; |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# Check user data |
|
370
|
0
|
0
|
|
|
|
|
unless ($user->{username}) { |
|
371
|
0
|
|
|
|
|
|
$self->log->error(sprintf("[E7008] File %s not found or incorrect", $file)); |
|
372
|
0
|
|
|
|
|
|
$payload{error} = sprintf("File DATADIR/u.%s.json not found or incorrect", $username); |
|
373
|
0
|
|
|
|
|
|
$payload{status} = 500; |
|
374
|
0
|
|
|
|
|
|
$payload{code} = 'E7008'; |
|
375
|
0
|
|
|
|
|
|
return Mojo::JSON::Pointer->new({%payload}); |
|
376
|
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# Ok |
|
379
|
0
|
|
|
|
|
|
$payload{user} = {%{$user}}; # Set user data to pyload hash |
|
|
0
|
|
|
|
|
|
|
|
380
|
0
|
|
|
|
|
|
return Mojo::JSON::Pointer->new({%payload}); |
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub _parse_pwdb_lines { |
|
384
|
0
|
|
|
0
|
|
|
my @lines = @_; |
|
385
|
0
|
|
|
|
|
|
my %r = (); |
|
386
|
0
|
|
|
|
|
|
for (@lines) { |
|
387
|
0
|
0
|
|
|
|
|
next unless $_; |
|
388
|
0
|
|
|
|
|
|
my @line = split ':', $_; |
|
389
|
0
|
|
0
|
|
|
|
my ($usr, $pwd, $arg) = ($line[0] // '', $line[1] // '', $line[2] // ''); |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
390
|
0
|
0
|
0
|
|
|
|
next unless length($usr) && length($pwd); |
|
391
|
0
|
0
|
|
|
|
|
if (@line == 3) { # username:password:params |
|
|
|
0
|
|
|
|
|
|
|
392
|
0
|
|
|
|
|
|
$r{$usr} = { |
|
393
|
|
|
|
|
|
|
pwd => $pwd, |
|
394
|
|
|
|
|
|
|
arg => $arg, |
|
395
|
|
|
|
|
|
|
}; |
|
396
|
|
|
|
|
|
|
} elsif (@line == 2) { # username:password |
|
397
|
0
|
|
|
|
|
|
$r{$usr} = { |
|
398
|
|
|
|
|
|
|
pwd => $pwd |
|
399
|
|
|
|
|
|
|
}; |
|
400
|
|
|
|
|
|
|
} |
|
401
|
|
|
|
|
|
|
} |
|
402
|
0
|
|
|
|
|
|
return %r; |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
sub _check_pw { |
|
405
|
0
|
|
0
|
0
|
|
|
my $pwd = shift // ''; |
|
406
|
0
|
|
0
|
|
|
|
my $sum = shift // ''; |
|
407
|
0
|
0
|
0
|
|
|
|
return 0 unless length($pwd) && length($sum); |
|
408
|
0
|
0
|
|
|
|
|
if ($sum =~ /^[0-9a-f]+$/i) { |
|
409
|
0
|
0
|
|
|
|
|
if (length($sum) == 32) { # md5: acbd18db4cc2f85cedef654fccc4a4d8 |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
410
|
0
|
|
|
|
|
|
return secure_compare(md5_sum($pwd), lc($sum)); |
|
411
|
|
|
|
|
|
|
} elsif(length($sum) == 40) { # sha1: 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33 |
|
412
|
0
|
|
|
|
|
|
return secure_compare(sha1_sum($pwd), lc($sum)); |
|
413
|
|
|
|
|
|
|
} elsif(length($sum) == 56) { # sha224: d63dc919e201d7bc4c825630d2cf25fdc93d4b2f0d46706d29038d01 |
|
414
|
0
|
|
|
|
|
|
return secure_compare(sha224_hex($pwd), lc($sum)); |
|
415
|
|
|
|
|
|
|
} elsif(length($sum) == 64) { # sha224: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 |
|
416
|
0
|
|
|
|
|
|
return secure_compare(sha256_hex($pwd), lc($sum)); |
|
417
|
|
|
|
|
|
|
} elsif(length($sum) == 96) { # sha384: a8b64babd0aca91a59bdbb7761b421d4f2bb38280d3a75ba0f21f2bebc45583d446c598660c94ce680c47d19c30783a7 |
|
418
|
0
|
|
|
|
|
|
return secure_compare(sha384_hex($pwd), lc($sum)); |
|
419
|
|
|
|
|
|
|
} elsif(length($sum) == 128) { # sha512: b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86 |
|
420
|
0
|
|
|
|
|
|
return secure_compare(sha512_hex($pwd), lc($sum)); |
|
421
|
|
|
|
|
|
|
} else { # Plain text (unsafe) |
|
422
|
0
|
|
|
|
|
|
return secure_compare($pwd, $sum); |
|
423
|
|
|
|
|
|
|
} |
|
424
|
|
|
|
|
|
|
} else { # Plain text (unsafe) |
|
425
|
0
|
|
|
|
|
|
return secure_compare($pwd, $sum); |
|
426
|
|
|
|
|
|
|
} |
|
427
|
0
|
|
|
|
|
|
return 0; |
|
428
|
|
|
|
|
|
|
} |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
1; |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
__END__ |