ONO::ToolBox::Auth

package ONO::ToolBox::Auth;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2000-2025 Jos KIRPS [ www.kirps.com | jos_AT_kirps_DOT_com ]
# and The Joopita Project [ www.joopita.org | contact_AT_joopita_DOT_com ]
#
# This file, as well as other parts of the ONO Software Project or related
# elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################


use strict;

use ONO::IO;
use ONO::DB;

use ONO::ToolBox::Auth::Auth;
use ONO::ToolBox::Auth::Login;
use ONO::ToolBox::Auth::Logout;
use ONO::ToolBox::Auth::PIN;
use ONO::ToolBox::Auth::ToolBox;

use ONO::Lib::Web::Community;
use ONO::Lib::Web::Cookie;
use ONO::Lib::Web::Client;
use ONO::Lib::Data::Crypt;
use ONO::Lib::Data::Geo;

use ONO::FW::User::Init;

###############################################################################
# ONO
###############################################################################

sub sudo {

#: Replace the current username with the currently selected user the sudoer
#: wishes to impersonate.

my (
$self,
$community,
$vars_ref,
) = @_;

my %vars = %$vars_ref;

my $sudo = ONO::IO->load(ONO::FW::User::Init->getuserdir($vars{'username'},$community)."/sudo/username.txt");

if ($sudo && $vars{'community_screen'} !~ /^admin/) {
$vars{'username'} = $sudo;
$vars{'username_sudo_mode'} = 1;
}

return \%vars;

}

sub admin {

#: Administrator login, logout and authentication using the ono_auth_asid cookie.
#: Note that this feature seems to be DEPRECATED, it may be removed soon.

my $vars_ref = $_[1];
my %vars = %$vars_ref;

# my $status;
#
# if ($vars{'mode'} eq "login") {
#
# ($status,$vars{'sid'}) = ONO::ToolBox::Auth::Login->login($vars{'username'},$vars{'password'});
#
# if ($vars{'sid'}) {
# if ($vars{'keep_session'}) {
# $vars{'cookie_sid'} = ONO::Lib::Web::Cookie->make("ono_auth_asid",$vars{'sid'},"90d");
# } else {
# $vars{'cookie_sid'} = ONO::Lib::Web::Cookie->make("ono_auth_asid",$vars{'sid'});
# }
# }
# $vars{'mode'} = "";
#
# }
#
# if (!$vars{'sid'}) {
# $vars{'sid'} = ONO::Lib::Web::Cookie->get("ono_auth_asid");
# }
#
# if ($vars{'sid'}) {
# ($vars{'sid'},$vars{'username'}) = ONO::ToolBox::Auth::Auth->auth($vars{'sid'});
# }
#
# if ($vars{'mode'} eq "logout") {
#
# ONO::ToolBox::Auth::Logout->logout($vars{'username'});
# $vars{'cookie_sid'} = ONO::Lib::Web::Cookie->make("ono_auth_asid","","now");
# $vars{'sid'} = "";
# $vars{'mode'} = "";
#
# }

return \%vars;

}

sub ono {

#: ONO Admin and Desk authentication, also supporting IP based access
#: limitations via /etc/security/onoaccess.conf.

my $vars_ref = $_[1];
my %vars = %$vars_ref;

my $block_ip;

if (ONO::IO->exists("etc/security/onoaccess.conf")) {

my $mode = "User";
if ($_[2] =~ /admin/i) {
$mode = "Admin";
}

my $IP = ONO::Lib::Web::Client->ip();

foreach my $line (ONO::IO->list("etc/security/onoaccess.conf")) {
if ($line =~ /^${mode}IPDeny ALL/) {
$block_ip++;
}
if ($line =~ /^${mode}IPAllow "(.*?)"/) {
my $VALID_IP = $1;
if ($IP eq $VALID_IP || $IP =~ /^$VALID_IP\./) {
$block_ip = 0;
}
}
}

}

if ($block_ip) {

$vars{'ono_blocked_ip'}++;

} else {

$vars{'sid'} = ONO::Lib::Web::Cookie->get("ono_session");
if ($vars{'sid'} =~ /^(.*?)-(.*?)$/) {

if ($vars{'sid'} eq ONO::IO->load("var/ono/sessions/$1.txt")) {
$vars{'username'} = $1;
} else {
$vars{'sid'} = "";
}

}

}

return (\%vars,%vars);

}

sub auth {

#: The main ONO platform authentication logic, using the ono_auth_sid_v2107
#: cookie (latest version as of July 2021).
#:
#: This handles login, logout and authentication, it provides basic user
#: info and keeps statistics up to date.

my ($self,$db,$community,$vars_ref) = @_;
my %vars;
if ($vars_ref) {
%vars = %$vars_ref;
}

my $request = $ENV{'REQUEST_URI'};
my $REDIRECT;

# reset group variables

$vars{'groupname'} = "";
$vars{'groupid'} = "";

# if there's no sid then we'll check the cookies

if (!$vars{'sid'}) {
$vars{'sid'} = ONO::Lib::Web::Cookie->get("ono_auth_sid_v2107");
}

# the standard login procedure

if ($vars{'mode'} eq "login" && $db) {

foreach my $line (ONO::DB->select($db,"${community}_community_users","username = '$vars{'username'}'")) {
my @row = ONO::DB->readcols($line);

if ($row[2] == 1 && ONO::Lib::Data::Crypt->pwdchk($vars{'password'},$row[4])) {
$vars{'sid'} = &makesid($vars{'username'},$community);
if ($vars{'keep_session'}) {
$vars{'cookie_sid'} = ONO::Lib::Web::Cookie->make("ono_auth_sid_v2107",$vars{'sid'},"90d",$vars_ref);
} else {
$vars{'cookie_sid'} = ONO::Lib::Web::Cookie->make("ono_auth_sid_v2107",$vars{'sid'},"",$vars_ref);
}
}
}

$vars{'mode'} = "";
}

# mobile logic

my $MOBILE = &mobile_identifier;

# web logic

my $WEBSITE;
if ($ENV{'REQUEST_URI'} =~ m~/web/-/(.*?)/~) {
$WEBSITE = $1;
} else {
if ($vars{'auth_mode'} eq "website" && $vars{'website'}) {
$WEBSITE = $vars{'website'};
}
}

# devstation test code...

if (ONO::IO->devstation) {
$vars{'userdata_country'} = "LU";
$vars{'userdata_territory'} = "EU";
}


# authentication

if ($vars{'sid'}) {

($vars{'sid'},$vars{'username'}) = ONO::Lib::Web::Community->auth($vars{'sid'},$community,"",$WEBSITE);

if ($db) {

# check profile, as required by EU laws

foreach my $line (ONO::DB->select($db,"${community}_community_profiles","username = '$vars{'username'}'")) {
my @row = ONO::DB->readcols($line);

$vars{'userdata_realname'} = $row[1];
$vars{'userdata_sex'} = $row[2];
$vars{'userdata_birthday'} = $row[3];
$vars{'userdata_country'} = $row[4];
$vars{'userdata_language'} = $row[7];
$vars{'userdata_languages'} = $row[8];
$vars{'userdata_xp'} = $row[15];
$vars{'userdata_rank'} = $row[16];
$vars{'userdata_timezone'} = $row[18];
$vars{'userdata_flags'} = $row[21];

# this will tell us if the user is an EU citizen, and/or if data has to be censored

$vars{'userdata_territory'} = ONO::Lib::Data::Geo->country_to_territory($row[4]);

# we need to censor the EU because of the new copyright directive
# we automatically censor CHina, as they only spam and scam sites...

if ($vars{'userdata_territory'} eq "EU" || $vars{'userdata_territory'} eq "CH") {
$vars{'userdata_censored'} = 1;
if ($vars{'userdata_flags'} =~ /E/) {
$vars{'userdata_uncensored'} = 1;
}
}
}
}

# randomly update the last login info and the user agent...

if ($vars{'sid'} && !$WEBSITE && $db) {
my $rand = int(rand(20));
if ($rand == 10) {
my $timestamp = ONO::IO->timestamp("var/community/$community/accounts/sessions/$vars{'username'}$MOBILE.sid");
ONO::DB->command($db,"UPDATE ${community}_community_status SET lastlogin = '$timestamp', domain = '$ENV{'SERVER_NAME'}' WHERE username = '$vars{'username'}';");
}
if ($rand == 11) {
my $time = time();
ONO::DB->command($db,"UPDATE ${community}_community_status SET lastonline = '$time', domain = '$ENV{'SERVER_NAME'}' WHERE username = '$vars{'username'}';");
}
if ($rand == 12) {
my $dir = ONO::FW::User::Init->getuserdir($vars{'username'},$community);
ONO::IO->store("$dir/user_agent.txt","$ENV{'REMOTE_ADDR'} - $ENV{'HTTP_USER_AGENT'} - $ENV{'HTTP_HOST'}");
}

}

}

# logout

if ($vars{'mode'} eq "logout" || $vars{'menubar_option'} eq "user_logout") {

if ($WEBSITE) {

ONO::IO->rm("var/community/$community/accounts/web/$WEBSITE/$vars{'username'}$MOBILE.sid");

} else {

ONO::IO->mkpath("var/community/$community/accounts/sessions_expired");
ONO::IO->rm("var/community/$community/accounts/sessions_expired/$vars{'username'}$MOBILE.sid");
ONO::IO->mv(
"var/community/$community/accounts/sessions/$vars{'username'}$MOBILE.sid",
"var/community/$community/accounts/sessions_expired/$vars{'username'}$MOBILE.sid",
);

}

$vars{'cookie_sid'} = ONO::Lib::Web::Cookie->make("ono_auth_sid_v2107","","now",$vars_ref);

$vars{'mode'} = "";
$vars{'sid'} = "";
}

return \%vars;

}

sub makesid {

#: Generate an ONO SID (Session ID), combining the username with a random string,
#: and store it in the user's data directory.

my ($username,$community) = @_;

ONO::Lib::Web::Community->log("$username -> generating session ID",$community);

my $boundary = ONO::Lib::Web::Community->makesid($community,$username);

return ("$username-$boundary");
}

sub mobile_identifier {

#: Check if we're using a mobile device.

my $MOBILE;
if ($ENV{'HTTP_USER_AGENT'} =~ /(mobile|tablet|ipad|phone)/i) {
$MOBILE = ".mobile";
if ($ENV{'HTTP_USER_AGENT'} =~ /(phone)/i) {
$MOBILE = ".phone";
}
}
return $MOBILE;

}

###############################################################################
# end of script
###############################################################################

1;

__END__