switch from HTTP Auth to Cookie Auth
This commit is contained in:
parent
ba6b517e5b
commit
fd60839116
11 changed files with 335 additions and 390 deletions
536
index.pl
536
index.pl
|
@ -44,13 +44,10 @@ app->plugin(
|
||||||
authentication => {
|
authentication => {
|
||||||
autoload_user => 1,
|
autoload_user => 1,
|
||||||
session_key => 'foodor',
|
session_key => 'foodor',
|
||||||
|
fail_render => { template => 'login' },
|
||||||
load_user => sub {
|
load_user => sub {
|
||||||
my ( $self, $uid ) = @_;
|
my ( $self, $uid ) = @_;
|
||||||
my $data = $self->get_user_data($uid);
|
return $self->get_user_data($uid);
|
||||||
if ($data) {
|
|
||||||
return { name => $data->{name} };
|
|
||||||
}
|
|
||||||
return undef;
|
|
||||||
},
|
},
|
||||||
validate_user => sub {
|
validate_user => sub {
|
||||||
my ( $self, $username, $password, $extradata ) = @_;
|
my ( $self, $username, $password, $extradata ) = @_;
|
||||||
|
@ -68,6 +65,7 @@ app->plugin(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
app->sessions->default_expiration( 60 * 60 * 24 * 180 );
|
||||||
|
|
||||||
app->defaults( layout => 'default' );
|
app->defaults( layout => 'default' );
|
||||||
|
|
||||||
|
@ -427,7 +425,7 @@ helper 'checkin' => sub {
|
||||||
}
|
}
|
||||||
|
|
||||||
my $success = $self->app->checkin_query->execute(
|
my $success = $self->app->checkin_query->execute(
|
||||||
$self->get_user_id,
|
$self->current_user->{id},
|
||||||
$self->get_station_id(
|
$self->get_station_id(
|
||||||
ds100 => $status->{station_ds100},
|
ds100 => $status->{station_ds100},
|
||||||
name => $status->{station_name}
|
name => $status->{station_name}
|
||||||
|
@ -457,7 +455,7 @@ helper 'checkin' => sub {
|
||||||
helper 'undo' => sub {
|
helper 'undo' => sub {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
my $uid = $self->get_user_id;
|
my $uid = $self->current_user->{id};
|
||||||
$self->app->get_last_actions_query->execute($uid);
|
$self->app->get_last_actions_query->execute($uid);
|
||||||
my $rows = $self->app->get_last_actions_query->fetchall_arrayref;
|
my $rows = $self->app->get_last_actions_query->fetchall_arrayref;
|
||||||
|
|
||||||
|
@ -469,8 +467,8 @@ helper 'undo' => sub {
|
||||||
return 'Repeated undo is not supported';
|
return 'Repeated undo is not supported';
|
||||||
}
|
}
|
||||||
|
|
||||||
my $success
|
my $success = $self->app->undo_query->execute(
|
||||||
= $self->app->undo_query->execute( $self->get_user_id,
|
$self->current_user->{id},
|
||||||
DateTime->now( time_zone => 'Europe/Berlin' )->epoch,
|
DateTime->now( time_zone => 'Europe/Berlin' )->epoch,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -501,7 +499,7 @@ helper 'checkout' => sub {
|
||||||
if ( not defined $train ) {
|
if ( not defined $train ) {
|
||||||
if ($force) {
|
if ($force) {
|
||||||
my $success = $self->app->checkout_query->execute(
|
my $success = $self->app->checkout_query->execute(
|
||||||
$self->get_user_id,
|
$self->current_user->{id},
|
||||||
$self->get_station_id(
|
$self->get_station_id(
|
||||||
ds100 => $status->{station_ds100},
|
ds100 => $status->{station_ds100},
|
||||||
name => $status->{station_name}
|
name => $status->{station_name}
|
||||||
|
@ -523,7 +521,7 @@ helper 'checkout' => sub {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
my $success = $self->app->checkout_query->execute(
|
my $success = $self->app->checkout_query->execute(
|
||||||
$self->get_user_id,
|
$self->current_user->{id},
|
||||||
$self->get_station_id(
|
$self->get_station_id(
|
||||||
ds100 => $status->{station_ds100},
|
ds100 => $status->{station_ds100},
|
||||||
name => $status->{station_name}
|
name => $status->{station_name}
|
||||||
|
@ -580,7 +578,7 @@ helper 'get_user_token' => sub {
|
||||||
helper 'get_user_data' => sub {
|
helper 'get_user_data' => sub {
|
||||||
my ( $self, $uid ) = @_;
|
my ( $self, $uid ) = @_;
|
||||||
|
|
||||||
$uid //= $self->get_user_id;
|
$uid //= $self->current_user->{id};
|
||||||
my $query = $self->app->get_user_query;
|
my $query = $self->app->get_user_query;
|
||||||
$query->execute($uid);
|
$query->execute($uid);
|
||||||
my $rows = $query->fetchall_arrayref;
|
my $rows = $query->fetchall_arrayref;
|
||||||
|
@ -603,7 +601,7 @@ helper 'get_user_data' => sub {
|
||||||
deletion_requested => $row[7]
|
deletion_requested => $row[7]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return;
|
return undef;
|
||||||
};
|
};
|
||||||
|
|
||||||
helper 'get_user_password' => sub {
|
helper 'get_user_password' => sub {
|
||||||
|
@ -623,80 +621,9 @@ helper 'get_user_password' => sub {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
helper 'get_user_name' => sub {
|
helper 'add_user' => sub {
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
my $user = $self->req->headers->header('X-Remote-User') // 'dev';
|
|
||||||
|
|
||||||
return $user;
|
|
||||||
};
|
|
||||||
|
|
||||||
helper 'get_user_id' => sub {
|
|
||||||
my ( $self, $user_name, $email, $token, $password ) = @_;
|
my ( $self, $user_name, $email, $token, $password ) = @_;
|
||||||
|
|
||||||
$user_name //= $self->get_user_name;
|
|
||||||
|
|
||||||
if ( not -e $dbname ) {
|
|
||||||
$self->app->dbh->begin_work;
|
|
||||||
$self->app->dbh->do(
|
|
||||||
qq{
|
|
||||||
create table schema_version (
|
|
||||||
version integer primary key
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
$self->app->dbh->do(
|
|
||||||
qq{
|
|
||||||
create table users (
|
|
||||||
id integer primary key,
|
|
||||||
name char(64) not null unique,
|
|
||||||
status int not null,
|
|
||||||
public_level bool not null,
|
|
||||||
email char(256),
|
|
||||||
token char(80),
|
|
||||||
password text,
|
|
||||||
registered_at datetime not null,
|
|
||||||
last_login datetime not null,
|
|
||||||
deletion_requested datetime
|
|
||||||
)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
$self->app->dbh->do(
|
|
||||||
qq{
|
|
||||||
create table stations (
|
|
||||||
id integer primary key,
|
|
||||||
ds100 char(16) not null unique,
|
|
||||||
name char(64) not null unique
|
|
||||||
)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
$self->app->dbh->do(
|
|
||||||
qq{
|
|
||||||
create table user_actions (
|
|
||||||
user_id int not null,
|
|
||||||
action_id int not null,
|
|
||||||
station_id int,
|
|
||||||
action_time int not null,
|
|
||||||
train_type char(16),
|
|
||||||
train_line char(16),
|
|
||||||
train_no char(16),
|
|
||||||
train_id char(128),
|
|
||||||
sched_time int,
|
|
||||||
real_time int,
|
|
||||||
route text,
|
|
||||||
messages text,
|
|
||||||
primary key (user_id, action_time)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
$self->app->dbh->do(
|
|
||||||
qq{
|
|
||||||
insert into schema_version (version) values (1);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
$self->app->dbh->commit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$self->app->get_userid_query->execute($user_name);
|
$self->app->get_userid_query->execute($user_name);
|
||||||
my $rows = $self->app->get_userid_query->fetchall_arrayref;
|
my $rows = $self->app->get_userid_query->fetchall_arrayref;
|
||||||
|
|
||||||
|
@ -737,7 +664,7 @@ helper 'check_if_user_name_exists' => sub {
|
||||||
helper 'get_user_travels' => sub {
|
helper 'get_user_travels' => sub {
|
||||||
my ( $self, $limit ) = @_;
|
my ( $self, $limit ) = @_;
|
||||||
|
|
||||||
my $uid = $self->get_user_id;
|
my $uid = $self->current_user->{id};
|
||||||
my $query = $self->app->get_all_actions_query;
|
my $query = $self->app->get_all_actions_query;
|
||||||
if ($limit) {
|
if ($limit) {
|
||||||
$query = $self->app->get_last_actions_query;
|
$query = $self->app->get_last_actions_query;
|
||||||
|
@ -800,7 +727,7 @@ helper 'get_user_travels' => sub {
|
||||||
helper 'get_user_status' => sub {
|
helper 'get_user_status' => sub {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
my $uid = $self->get_user_id;
|
my $uid = $self->current_user->{id};
|
||||||
$self->app->get_last_actions_query->execute($uid);
|
$self->app->get_last_actions_query->execute($uid);
|
||||||
my $rows = $self->app->get_last_actions_query->fetchall_arrayref;
|
my $rows = $self->app->get_last_actions_query->fetchall_arrayref;
|
||||||
|
|
||||||
|
@ -886,7 +813,217 @@ helper 'navbar_class' => sub {
|
||||||
|
|
||||||
get '/' => sub {
|
get '/' => sub {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
if ( $self->is_user_authenticated ) {
|
||||||
$self->render( 'landingpage', with_geolocation => 1 );
|
$self->render( 'landingpage', with_geolocation => 1 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$self->render( 'landingpage', intro => 1 );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
get '/about' => sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
$self->render( 'about', version => $VERSION );
|
||||||
|
};
|
||||||
|
|
||||||
|
get '/impressum' => sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
$self->render('imprint');
|
||||||
|
};
|
||||||
|
|
||||||
|
get '/imprint' => sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
$self->render('imprint');
|
||||||
|
};
|
||||||
|
|
||||||
|
post '/geolocation' => sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
my $lon = $self->param('lon');
|
||||||
|
my $lat = $self->param('lat');
|
||||||
|
|
||||||
|
if ( not $lon or not $lat ) {
|
||||||
|
$self->render( json => { error => 'Invalid lon/lat received' } );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
my @candidates = map {
|
||||||
|
{
|
||||||
|
ds100 => $_->[0][0],
|
||||||
|
name => $_->[0][1],
|
||||||
|
eva => $_->[0][2],
|
||||||
|
lon => $_->[0][3],
|
||||||
|
lat => $_->[0][4],
|
||||||
|
distance => $_->[1],
|
||||||
|
}
|
||||||
|
} Travel::Status::DE::IRIS::Stations::get_station_by_location( $lon,
|
||||||
|
$lat, 5 );
|
||||||
|
$self->render(
|
||||||
|
json => {
|
||||||
|
candidates => [@candidates],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
get '/login' => sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
$self->render('login');
|
||||||
|
};
|
||||||
|
|
||||||
|
post '/login' => sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
my $user = $self->req->param('user');
|
||||||
|
my $password = $self->req->param('password');
|
||||||
|
|
||||||
|
# Keep cookies for 6 months
|
||||||
|
$self->session( expiration => 60 * 60 * 24 * 180 );
|
||||||
|
|
||||||
|
if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
|
||||||
|
$self->render(
|
||||||
|
'login',
|
||||||
|
invalid => 'csrf',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( $self->authenticate( $user, $password ) ) {
|
||||||
|
$self->redirect_to('/');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$self->render( 'login', invalid => 'credentials' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
get '/register' => sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
$self->render('register');
|
||||||
|
};
|
||||||
|
|
||||||
|
post '/register' => sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
my $user = $self->req->param('user');
|
||||||
|
my $email = $self->req->param('email');
|
||||||
|
my $password = $self->req->param('password');
|
||||||
|
my $password2 = $self->req->param('password2');
|
||||||
|
my $ip = $self->req->headers->header('X-Forwarded-For');
|
||||||
|
my $ua = $self->req->headers->user_agent;
|
||||||
|
my $date = DateTime->now( time_zone => 'Europe/Berlin' )
|
||||||
|
->strftime('%d.%m.%Y %H:%M:%S %z');
|
||||||
|
|
||||||
|
# In case Mojolicious is not running behind a reverse proxy
|
||||||
|
$ip
|
||||||
|
//= sprintf( '%s:%s', $self->tx->remote_address, $self->tx->remote_port );
|
||||||
|
|
||||||
|
if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
|
||||||
|
$self->render(
|
||||||
|
'register',
|
||||||
|
invalid => 'csrf',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( not length($user) ) {
|
||||||
|
$self->render( 'register', invalid => 'user_empty' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( not length($email) ) {
|
||||||
|
$self->render( 'register', invalid => 'mail_empty' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $user !~ m{ ^ [0-9a-zA-Z_-]+ $ }x ) {
|
||||||
|
$self->render( 'register', invalid => 'user_format' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $self->check_if_user_name_exists($user) ) {
|
||||||
|
$self->render( 'register', invalid => 'user_collision' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $password ne $password2 ) {
|
||||||
|
$self->render( 'register', invalid => 'password_notequal' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( length($password) < 8 ) {
|
||||||
|
$self->render( 'register', invalid => 'password_short' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $token = make_token();
|
||||||
|
my $pw_hash = hash_password($password);
|
||||||
|
my $user_id = $self->add_user( $user, $email, $token, $pw_hash );
|
||||||
|
|
||||||
|
my $body = "Hallo, ${user}!\n\n";
|
||||||
|
$body .= "Mit deiner E-Mail-Adresse (${email}) wurde ein Account auf\n";
|
||||||
|
$body .= "travelynx.finalrewind.org angelegt.\n\n";
|
||||||
|
$body
|
||||||
|
.= "Falls die Registrierung von dir ausging, kannst du den Account unter\n";
|
||||||
|
$body .= "https://travelynx.finalrewind.org/reg/${user_id}/${token}\n";
|
||||||
|
$body .= "freischalten.\n\n";
|
||||||
|
$body
|
||||||
|
.= "Falls nicht, ignoriere diese Mail bitte. Nach 48 Stunden wird deine\n";
|
||||||
|
$body
|
||||||
|
.= "Mail-Adresse erneut zur Registrierung freigeschaltet. Falls auch diese fehlschlägt,\n";
|
||||||
|
$body
|
||||||
|
.= "werden wir sie dauerhaft sperren und keine Mails mehr dorthin schicken.\n\n";
|
||||||
|
$body .= "Daten zur Registrierung:\n";
|
||||||
|
$body .= " * Datum: ${date}\n";
|
||||||
|
$body .= " * Verwendete IP: ${ip}\n";
|
||||||
|
$body .= " * Verwendeter Browser gemäß User Agent: ${ua}\n\n\n";
|
||||||
|
$body .= "Impressum: https://travelynx.finalrewind.org/impressum\n";
|
||||||
|
|
||||||
|
my $reg_mail = Email::Simple->create(
|
||||||
|
header => [
|
||||||
|
To => $email,
|
||||||
|
From => 'Travelynx <travelynx@finalrewind.org>',
|
||||||
|
Subject => 'Registrierung auf travelynx.finalrewind.org',
|
||||||
|
'Content-Type' => 'text/plain; charset=UTF-8',
|
||||||
|
],
|
||||||
|
body => encode( 'utf-8', $body ),
|
||||||
|
);
|
||||||
|
|
||||||
|
my $success = try_to_sendmail($reg_mail);
|
||||||
|
if ($success) {
|
||||||
|
$self->render( 'login', from => 'register' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$self->render( 'register', invalid => 'sendmail' );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
get '/reg/:id/:token' => sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
my $id = $self->stash('id');
|
||||||
|
my $token = $self->stash('token');
|
||||||
|
|
||||||
|
my @db_user = $self->get_user_token($id);
|
||||||
|
|
||||||
|
if ( not @db_user ) {
|
||||||
|
$self->render( 'register', invalid => 'token' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my ( $db_name, $db_status, $db_token ) = @db_user;
|
||||||
|
|
||||||
|
if ( not $db_name or $token ne $db_token or $db_status != 0 ) {
|
||||||
|
$self->render( 'register', invalid => 'token' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$self->app->set_status_query->execute( 1, $id );
|
||||||
|
$self->render( 'login', from => 'verification' );
|
||||||
|
};
|
||||||
|
|
||||||
|
under sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
return $self->is_user_authenticated;
|
||||||
};
|
};
|
||||||
|
|
||||||
post '/action' => sub {
|
post '/action' => sub {
|
||||||
|
@ -979,15 +1116,21 @@ post '/action' => sub {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
get '/a/account' => sub {
|
get '/account' => sub {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
$self->render('account');
|
$self->render('account');
|
||||||
};
|
};
|
||||||
|
|
||||||
get '/a/export.json' => sub {
|
get '/history' => sub {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
my $uid = $self->get_user_id;
|
|
||||||
|
$self->render('history');
|
||||||
|
};
|
||||||
|
|
||||||
|
get '/export.json' => sub {
|
||||||
|
my ($self) = @_;
|
||||||
|
my $uid = $self->current_user->{id};
|
||||||
my $query = $self->app->get_all_actions_query;
|
my $query = $self->app->get_all_actions_query;
|
||||||
|
|
||||||
$query->execute($uid);
|
$query->execute($uid);
|
||||||
|
@ -1031,220 +1174,13 @@ get '/a/export.json' => sub {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
get '/a/history' => sub {
|
post '/logout' => sub {
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
$self->render('history');
|
|
||||||
};
|
|
||||||
|
|
||||||
get '/x/about' => sub {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
$self->render( 'about', version => $VERSION );
|
|
||||||
};
|
|
||||||
|
|
||||||
get '/x/impressum' => sub {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
$self->render('imprint');
|
|
||||||
};
|
|
||||||
|
|
||||||
get '/x/imprint' => sub {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
$self->render('imprint');
|
|
||||||
};
|
|
||||||
|
|
||||||
post '/x/geolocation' => sub {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
my $lon = $self->param('lon');
|
|
||||||
my $lat = $self->param('lat');
|
|
||||||
|
|
||||||
if ( not $lon or not $lat ) {
|
|
||||||
$self->render( json => { error => 'Invalid lon/lat received' } );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
my @candidates = map {
|
|
||||||
{
|
|
||||||
ds100 => $_->[0][0],
|
|
||||||
name => $_->[0][1],
|
|
||||||
eva => $_->[0][2],
|
|
||||||
lon => $_->[0][3],
|
|
||||||
lat => $_->[0][4],
|
|
||||||
distance => $_->[1],
|
|
||||||
}
|
|
||||||
} Travel::Status::DE::IRIS::Stations::get_station_by_location( $lon,
|
|
||||||
$lat, 5 );
|
|
||||||
$self->render(
|
|
||||||
json => {
|
|
||||||
candidates => [@candidates],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
get '/x/login' => sub {
|
|
||||||
my ($self) = @_;
|
|
||||||
$self->render('login');
|
|
||||||
};
|
|
||||||
|
|
||||||
post '/x/login' => sub {
|
|
||||||
my ($self) = @_;
|
|
||||||
my $user = $self->req->param('user');
|
|
||||||
my $password = $self->req->param('password');
|
|
||||||
|
|
||||||
# Keep cookies for 6 months
|
|
||||||
$self->session( expiration => 60 * 60 * 24 * 180 );
|
|
||||||
|
|
||||||
if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
|
|
||||||
$self->render(
|
|
||||||
'login',
|
|
||||||
invalid => 'csrf',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ( $self->authenticate( $user, $password ) ) {
|
|
||||||
$self->redirect_to('/');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$self->render( 'login', invalid => 'credentials' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
get '/x/logout' => sub {
|
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
$self->logout;
|
$self->logout;
|
||||||
$self->redirect_to('/x/login');
|
$self->redirect_to('/login');
|
||||||
};
|
};
|
||||||
|
|
||||||
get '/x/register' => sub {
|
get '/s/*station' => sub {
|
||||||
my ($self) = @_;
|
|
||||||
$self->render('register');
|
|
||||||
};
|
|
||||||
|
|
||||||
post '/x/register' => sub {
|
|
||||||
my ($self) = @_;
|
|
||||||
my $user = $self->req->param('user');
|
|
||||||
my $email = $self->req->param('email');
|
|
||||||
my $password = $self->req->param('password');
|
|
||||||
my $password2 = $self->req->param('password2');
|
|
||||||
my $ip = $self->req->headers->header('X-Forwarded-For');
|
|
||||||
my $ua = $self->req->headers->user_agent;
|
|
||||||
my $date = DateTime->now( time_zone => 'Europe/Berlin' )
|
|
||||||
->strftime('%d.%m.%Y %H:%M:%S %z');
|
|
||||||
|
|
||||||
# In case Mojolicious is not running behind a reverse proxy
|
|
||||||
$ip
|
|
||||||
//= sprintf( '%s:%s', $self->tx->remote_address, $self->tx->remote_port );
|
|
||||||
|
|
||||||
if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
|
|
||||||
$self->render(
|
|
||||||
'register',
|
|
||||||
invalid => 'csrf',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( not length($user) ) {
|
|
||||||
$self->render( 'register', invalid => 'user_empty' );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( not length($email) ) {
|
|
||||||
$self->render( 'register', invalid => 'mail_empty' );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $user !~ m{ ^ [0-9a-zA-Z_-]+ $ }x ) {
|
|
||||||
$self->render( 'register', invalid => 'user_format' );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ( $self->check_if_user_name_exists($user) or $user eq 'dev' ) {
|
|
||||||
if ( $user ne $self->get_user_name ) {
|
|
||||||
$self->render( 'register', invalid => 'user_collision' );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $password ne $password2 ) {
|
|
||||||
$self->render( 'register', invalid => 'password_notequal' );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( length($password) < 8 ) {
|
|
||||||
$self->render( 'register', invalid => 'password_short' );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $token = make_token();
|
|
||||||
my $pw_hash = hash_password($password);
|
|
||||||
my $user_id = $self->get_user_id( $user, $email, $token, $pw_hash );
|
|
||||||
|
|
||||||
my $body = "Hallo, ${user}!\n\n";
|
|
||||||
$body .= "Mit deiner E-Mail-Adresse (${email}) wurde ein Account auf\n";
|
|
||||||
$body .= "travelynx.finalrewind.org angelegt.\n\n";
|
|
||||||
$body
|
|
||||||
.= "Falls die Registrierung von dir ausging, kannst du den Account unter\n";
|
|
||||||
$body .= "https://travelynx.finalrewind.org/x/reg/${user_id}/${token}\n";
|
|
||||||
$body .= "freischalten.\n\n";
|
|
||||||
$body
|
|
||||||
.= "Falls nicht, ignoriere diese Mail bitte. Nach 48 Stunden wird deine\n";
|
|
||||||
$body
|
|
||||||
.= "Mail-Adresse erneut zur Registrierung freigeschaltet. Falls auch diese fehlschlägt,\n";
|
|
||||||
$body
|
|
||||||
.= "werden wir sie dauerhaft sperren und keine Mails mehr dorthin schicken.\n\n";
|
|
||||||
$body .= "Daten zur Registrierung:\n";
|
|
||||||
$body .= " * Datum: ${date}\n";
|
|
||||||
$body .= " * Verwendete IP: ${ip}\n";
|
|
||||||
$body .= " * Verwendeter Browser gemäß User Agent: ${ua}\n\n\n";
|
|
||||||
$body .= "Impressum: https://travelynx.finalrewind.org/x/impressum\n";
|
|
||||||
|
|
||||||
my $reg_mail = Email::Simple->create(
|
|
||||||
header => [
|
|
||||||
To => $email,
|
|
||||||
From => 'Travelynx <travelynx@finalrewind.org>',
|
|
||||||
Subject => 'Registrierung auf travelynx.finalrewind.org',
|
|
||||||
'Content-Type' => 'text/plain; charset=UTF-8',
|
|
||||||
],
|
|
||||||
body => encode( 'utf-8', $body ),
|
|
||||||
);
|
|
||||||
|
|
||||||
my $success = try_to_sendmail($reg_mail);
|
|
||||||
if ($success) {
|
|
||||||
$self->render( 'login', from => 'register' );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$self->render( 'register', invalid => 'sendmail' );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
get '/x/reg/:id/:token' => sub {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
my $id = $self->stash('id');
|
|
||||||
my $token = $self->stash('token');
|
|
||||||
|
|
||||||
my @db_user = $self->get_user_token($id);
|
|
||||||
|
|
||||||
if ( not @db_user ) {
|
|
||||||
$self->render( 'register', invalid => 'token' );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
my ( $db_name, $db_status, $db_token ) = @db_user;
|
|
||||||
|
|
||||||
if ( not $db_name or $token ne $db_token or $db_status != 0 ) {
|
|
||||||
$self->render( 'register', invalid => 'token' );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$self->app->set_status_query->execute( 1, $id );
|
|
||||||
$self->render( 'login', from => 'verification' );
|
|
||||||
};
|
|
||||||
|
|
||||||
get '/*station' => sub {
|
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
my $station = $self->stash('station');
|
my $station = $self->stash('station');
|
||||||
|
|
||||||
|
|
|
@ -25,14 +25,14 @@ $(document).ready(function() {
|
||||||
stationlink.attr('href', ds100);
|
stationlink.attr('href', ds100);
|
||||||
stationlink.text(name);
|
stationlink.text(name);
|
||||||
|
|
||||||
resultBody.append('<tr><td><a href="/' + ds100 + '">' + name + '</a></td></tr>');
|
resultBody.append('<tr><td><a href="/s/' + ds100 + '">' + name + '</a></td></tr>');
|
||||||
});
|
});
|
||||||
placeholder.replaceWith(resultTable);
|
placeholder.replaceWith(resultTable);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var processLocation = function(loc) {
|
var processLocation = function(loc) {
|
||||||
$.post('/x/geolocation', {lon: loc.coords.longitude, lat: loc.coords.latitude}, processResult);
|
$.post('/geolocation', {lon: loc.coords.longitude, lat: loc.coords.latitude}, processResult);
|
||||||
};
|
};
|
||||||
|
|
||||||
var processError = function(error) {
|
var processError = function(error) {
|
||||||
|
|
2
public/static/js/geolocation.min.js
vendored
2
public/static/js/geolocation.min.js
vendored
|
@ -1 +1 @@
|
||||||
$(document).ready(function(){var e=$("p.geolocationhint"),t=$("div.geolocation div.progress"),o=function(o,a,n){e.remove(),t.remove()},a=function(e){e.error?o(0,e.error):0==e.candidates.length?o():(resultTable=$("<table><tbody></tbody></table>"),resultBody=resultTable.children(),$.each(e.candidates,function(e,t){var o=t.ds100,a=t.name,n=t.distance;n=n.toFixed(1);var r=$(document.createElement("a"));r.attr("href",o),r.text(a),resultBody.append('<tr><td><a href="/'+o+'">'+a+"</a></td></tr>")}),t.replaceWith(resultTable))},n=function(e){$.post("/x/geolocation",{lon:e.coords.longitude,lat:e.coords.latitude},a)},r=function(e){e.code==e.PERMISSION_DENIED?o():e.code==e.POSITION_UNAVAILABLE?o():(e.code,e.TIMEOUT,o())};navigator.geolocation?navigator.geolocation.getCurrentPosition(n,r):o()});
|
$(document).ready(function(){var a=$("p.geolocationhint"),n=$("div.geolocation div.progress"),t=function(e,t,o){a.remove(),n.remove()},o=function(e){e.error?t(0,e.error):0==e.candidates.length?t():(resultTable=$("<table><tbody></tbody></table>"),resultBody=resultTable.children(),$.each(e.candidates,function(e,t){var o=t.ds100,a=t.name,n=t.distance;n=n.toFixed(1);var r=$(document.createElement("a"));r.attr("href",o),r.text(a),resultBody.append('<tr><td><a href="/s/'+o+'">'+a+"</a></td></tr>")}),n.replaceWith(resultTable))};navigator.geolocation?navigator.geolocation.getCurrentPosition(function(e){$.post("/geolocation",{lon:e.coords.longitude,lat:e.coords.latitude},o)},function(e){e.code==e.PERMISSION_DENIED||e.code==e.POSITION_UNAVAILABLE||(e.code,e.TIMEOUT),t()}):t()});
|
||||||
|
|
|
@ -34,7 +34,7 @@ $(document).ready(function() {
|
||||||
station: link.data('station'),
|
station: link.data('station'),
|
||||||
force: link.data('force'),
|
force: link.data('force'),
|
||||||
};
|
};
|
||||||
tvly_run(link, req, '/' + req.station, function() {
|
tvly_run(link, req, '/s/' + req.station, function() {
|
||||||
link.append(' – Ohne Echtzeitdaten auschecken?')
|
link.append(' – Ohne Echtzeitdaten auschecken?')
|
||||||
link.data('force', true);
|
link.data('force', true);
|
||||||
});
|
});
|
||||||
|
|
2
public/static/js/travelynx-actions.min.js
vendored
2
public/static/js/travelynx-actions.min.js
vendored
|
@ -1 +1 @@
|
||||||
function tvly_run(n,t,i,a){var c='<i class="material-icons">error</i>',o=$('<div class="progress"><div class="indeterminate"></div></div>');n.hide(),n.after(o),$.post("/action",t,function(t){t.success?$(location).attr("href",i):(M.toast({html:c+" "+t.error}),o.remove(),a&&a(),n.append(" "+c),n.show())})}$(document).ready(function(){$(".action-checkin").click(function(){var t=$(this);tvly_run(t,{action:"checkin",station:t.data("station"),train:t.data("train")},"/")}),$(".action-checkout").click(function(){var t=$(this),n={action:"checkout",station:t.data("station"),force:t.data("force")};tvly_run(t,n,"/"+n.station,function(){t.append(" – Ohne Echtzeitdaten auschecken?"),t.data("force",!0)})}),$(".action-undo").click(function(){tvly_run($(this),{action:"undo"},window.location.href)})});
|
function tvly_run(n,t,i,a){var c='<i class="material-icons">error</i>',o=$('<div class="progress"><div class="indeterminate"></div></div>');n.hide(),n.after(o),$.post("/action",t,function(t){t.success?$(location).attr("href",i):(M.toast({html:c+" "+t.error}),o.remove(),a&&a(),n.append(" "+c),n.show())})}$(document).ready(function(){$(".action-checkin").click(function(){var t=$(this);tvly_run(t,{action:"checkin",station:t.data("station"),train:t.data("train")},"/")}),$(".action-checkout").click(function(){var t=$(this),n={action:"checkout",station:t.data("station"),force:t.data("force")};tvly_run(t,n,"/s/"+n.station,function(){t.append(" – Ohne Echtzeitdaten auschecken?"),t.data("force",!0)})}),$(".action-undo").click(function(){tvly_run($(this),{action:"undo"},window.location.href)})});
|
||||||
|
|
1
public/static/v1
Symbolic link
1
public/static/v1
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
.
|
|
@ -22,7 +22,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/a/export.json">Rohdaten</a> (Kein API-Ersatz, das Format kann sich jederzeit ändern)</li>
|
<li><a href="/export.json">Rohdaten</a> (Kein API-Ersatz, das Format kann sich jederzeit ändern)</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
% if (is_user_authenticated()) {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
% my $status = $self->get_user_status;
|
% my $status = get_user_status();
|
||||||
% if ($status->{checked_in}) {
|
% if ($status->{checked_in}) {
|
||||||
<div class="card green darken-4">
|
<div class="card green darken-4">
|
||||||
<div class="card-content white-text">
|
<div class="card-content white-text">
|
||||||
<span class="card-title">Hallo, <%= $self->get_user_name %>!</span>
|
<span class="card-title">Hallo, <%= current_user()->{name} %>!</span>
|
||||||
<p>Du bist gerade eingecheckt in
|
<p>Du bist gerade eingecheckt in
|
||||||
<%= $status->{train_type} %> <%= $status->{train_no} %>
|
<%= $status->{train_type} %> <%= $status->{train_no} %>
|
||||||
ab <%= $status->{station_name} %>.
|
ab <%= $status->{station_name} %>.
|
||||||
|
@ -27,7 +28,7 @@
|
||||||
% else {
|
% else {
|
||||||
<div class="card grey darken-4">
|
<div class="card grey darken-4">
|
||||||
<div class="card-content white-text">
|
<div class="card-content white-text">
|
||||||
<span class="card-title">Hallo, <%= $self->get_user_name %>!</span>
|
<span class="card-title">Hallo, <%= current_user()->{name} %>!</span>
|
||||||
<p>Du bist gerade nicht eingecheckt.</p>
|
<p>Du bist gerade nicht eingecheckt.</p>
|
||||||
<p class="geolocationhint">Stationen in der Umgebung:</p>
|
<p class="geolocationhint">Stationen in der Umgebung:</p>
|
||||||
<div class="geolocation">
|
<div class="geolocation">
|
||||||
|
@ -70,3 +71,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</tabel>
|
</tabel>
|
||||||
</div>
|
</div>
|
||||||
|
% }
|
||||||
|
% else {
|
||||||
|
Huhu!
|
||||||
|
% }
|
||||||
|
|
|
@ -5,14 +5,15 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="theme-color" content="#673ab7">
|
<meta name="theme-color" content="#673ab7">
|
||||||
%= stylesheet '/static/css/materialize.min.css'
|
% my $av = 'v1'; # asset version
|
||||||
%= stylesheet '/static/css/material-icons.css'
|
%= stylesheet "/static/${av}/css/materialize.min.css"
|
||||||
%= stylesheet '/static/css/local.css'
|
%= stylesheet "/static/${av}/css/material-icons.css"
|
||||||
%= javascript '/static/js/jquery-2.2.4.min.js'
|
%= stylesheet "/static/${av}/css/local.css"
|
||||||
%= javascript '/static/js/materialize.min.js'
|
%= javascript "/static/${av}/js/jquery-2.2.4.min.js"
|
||||||
%= javascript '/static/js/travelynx-actions.min.js'
|
%= javascript "/static/${av}/js/materialize.min.js"
|
||||||
|
%= javascript "/static/${av}/js/travelynx-actions.min.js"
|
||||||
% if (stash('with_geolocation')) {
|
% if (stash('with_geolocation')) {
|
||||||
%= javascript '/static/js/geolocation.min.js'
|
%= javascript "/static/${av}/js/geolocation.min.js"
|
||||||
% }
|
% }
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -21,9 +22,11 @@
|
||||||
<div class="nav-wrapper container">
|
<div class="nav-wrapper container">
|
||||||
<a href="/" class="brand-logo left">travelynx</a>
|
<a href="/" class="brand-logo left">travelynx</a>
|
||||||
<ul id="nav-mobile" class="right">
|
<ul id="nav-mobile" class="right">
|
||||||
<li class="<%= navbar_class('/a/history') %>"><a href='/a/history' title="History"><i class="material-icons">history</i></a></li>
|
% if (is_user_authenticated()) {
|
||||||
<li class="<%= navbar_class('/a/account') %>"><a href="/a/account" title="Account"><i class="material-icons">account_circle</i></a></li>
|
<li class="<%= navbar_class('/history') %>"><a href='/history' title="History"><i class="material-icons">history</i></a></li>
|
||||||
<li class="<%= navbar_class('/x/about') %>"><a href='/x/about' title="About"><i class="material-icons">info_outline</i></a></li>
|
<li class="<%= navbar_class('/account') %>"><a href="/account" title="Account"><i class="material-icons">account_circle</i></a></li>
|
||||||
|
% }
|
||||||
|
<li class="<%= navbar_class('/about') %>"><a href='/about' title="About"><i class="material-icons">info_outline</i></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<p>
|
<p>
|
||||||
Du bist bereits angemeldet. Falls du mehrere Accounts hast
|
Du bist bereits angemeldet. Falls du mehrere Accounts hast
|
||||||
und auf einen anderen wechseln möchtest, musst du dich
|
und auf einen anderen wechseln möchtest, musst du dich
|
||||||
vorher <a href="/x/logout">abmelden</a>.
|
vorher <a href="/logout">abmelden</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
</div>
|
</div>
|
||||||
% }
|
% }
|
||||||
<div class="row">
|
<div class="row">
|
||||||
%= form_for '/x/login' => (class => 'col s12', method => 'POST') => begin
|
%= form_for '/login' => (class => 'col s12', method => 'POST') => begin
|
||||||
%= csrf_field
|
%= csrf_field
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="input-field col s12">
|
<div class="input-field col s12">
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
</div>
|
</div>
|
||||||
% }
|
% }
|
||||||
<div class="row">
|
<div class="row">
|
||||||
%= form_for '/x/register' => (class => 'col s12', method => 'POST') => begin
|
%= form_for '/register' => (class => 'col s12', method => 'POST') => begin
|
||||||
%= csrf_field
|
%= csrf_field
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="input-field col l6 m12 s12">
|
<div class="input-field col l6 m12 s12">
|
||||||
|
@ -103,7 +103,7 @@
|
||||||
Die Mail-Adresse wird ausschließlich zur Bestätigung der Anmeldung
|
Die Mail-Adresse wird ausschließlich zur Bestätigung der Anmeldung
|
||||||
und für die "Passwort vergessen"-Funktionalität verwendet und nicht
|
und für die "Passwort vergessen"-Funktionalität verwendet und nicht
|
||||||
an Dritte weitergegeben. Die <a
|
an Dritte weitergegeben. Die <a
|
||||||
href="/x/impressum">Datenschutzerklärung</a> beschreibt weitere
|
href="/impressum">Datenschutzerklärung</a> beschreibt weitere
|
||||||
erhobene Daten sowie deren Zweck und Speicherfristen.
|
erhobene Daten sowie deren Zweck und Speicherfristen.
|
||||||
Accounts werden nach einem Jahr ohne Nutzung automatisch gelöscht.
|
Accounts werden nach einem Jahr ohne Nutzung automatisch gelöscht.
|
||||||
</p>
|
</p>
|
||||||
|
|
Loading…
Reference in a new issue