346 lines
12 KiB
PHP
346 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\API\v1\Voter;
|
|
|
|
use Illuminate\Http\Request;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Controllers\Util;
|
|
use App\UserOTP;
|
|
use App\Voter;
|
|
use Carbon\Carbon;
|
|
use Exception;
|
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use GuzzleHttp\Client;
|
|
|
|
class LoginController extends Controller
|
|
{
|
|
public function __invoke(Request $request)
|
|
{
|
|
/* Request OTP (or lookup phone only when send_otp is false) */
|
|
try {
|
|
// $voter = Voter::where('no_kp', $request->no_kp)->firstOrFail();
|
|
|
|
$voter = $this->findVoterForCurrentElection($request->no_kp);
|
|
|
|
if (! $this->wantsSendOtp($request)) {
|
|
activity()
|
|
->performedOn($voter)
|
|
->withProperties([
|
|
'election_id' => Util::getCurrentElection(),
|
|
'voter_id' => $voter->id,
|
|
'voter_name' => $voter->name,
|
|
'no_kp' => $voter->no_kp,
|
|
'telefon' => $voter->telefon,
|
|
'ip' => $request->ip(),
|
|
'user_agent' => $request->userAgent(),
|
|
])
|
|
->log('voter lookup phone (no tac sent)');
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Nombor telefon dijumpai.',
|
|
'notel' => $voter->telefon,
|
|
'nokp' => $voter->no_kp,
|
|
]);
|
|
}
|
|
|
|
$currentDateTime = Carbon::now();
|
|
|
|
$expired_at = Carbon::now()->addMinutes(config('onewaysms.minutes'));
|
|
$created_at = $currentDateTime->toDateTimeString();
|
|
|
|
if ($voter) {
|
|
$expiration = $this->checkExpirationTAC($request->no_kp);
|
|
|
|
if ($expiration['notexpired'] == true) {
|
|
throw new \Exception('Expired TAC : ' . $expiration['remaining']);
|
|
}
|
|
|
|
$sms_token = $this->getTokenSMS($voter->telefon, $currentDateTime);
|
|
|
|
if ($sms_token == 'error') {
|
|
throw new \Exception('error in getting the data from SMSToken');
|
|
}
|
|
|
|
$user_otp = new UserOTP();
|
|
$user_otp->nokp = $voter->no_kp;
|
|
$user_otp->telefon = $voter->telefon;
|
|
$user_otp->token = $sms_token;
|
|
$user_otp->created_at = $created_at;
|
|
$user_otp->expired_at = $expired_at;
|
|
$user_otp->save();
|
|
|
|
activity()
|
|
->performedOn($voter)
|
|
->withProperties([
|
|
'election_id' => Util::getCurrentElection(),
|
|
'voter_id' => $voter->id,
|
|
'voter_name' => $voter->name,
|
|
'no_kp' => $voter->no_kp,
|
|
'telefon' => $voter->telefon,
|
|
'otp_expires_at' => $expired_at->toDateTimeString(),
|
|
'ip' => $request->ip(),
|
|
'user_agent' => $request->userAgent(),
|
|
])
|
|
->log('voter request tac');
|
|
|
|
$response = [
|
|
'status' => 'success',
|
|
'message' => 'generated SMS Token',
|
|
'notel' => $voter->telefon,
|
|
'nokp' => $voter->no_kp,
|
|
];
|
|
|
|
if (app()->environment(['local', 'development'])) {
|
|
$response['debug_otp'] = $sms_token;
|
|
}
|
|
|
|
return response()->json($response);
|
|
}
|
|
|
|
return response()->json([
|
|
'status' => 'failed',
|
|
'message' => 'Does not found NO KP.',
|
|
]);
|
|
} catch (ModelNotFoundException $e) {
|
|
activity()
|
|
->withProperties([
|
|
'no_kp' => (string) $request->input('no_kp'),
|
|
'election_id' => Util::getCurrentElection(),
|
|
'ip' => $request->ip(),
|
|
'user_agent' => $request->userAgent(),
|
|
'reason' => 'voter not registered for current election',
|
|
])
|
|
->log('voter login rejected: not in current election');
|
|
|
|
return response()->json([
|
|
'status' => 'failed',
|
|
'message' => 'No. KP ini tidak didaftarkan untuk pilihan raya semasa. Sila pergi ke kaunter IT untuk mendaftar.',
|
|
]);
|
|
} catch (Exception $e) {
|
|
activity()
|
|
->withProperties([
|
|
'no_kp' => (string) $request->input('no_kp'),
|
|
'ip' => $request->ip(),
|
|
'user_agent' => $request->userAgent(),
|
|
'error' => $e->getMessage(),
|
|
])
|
|
->log('voter request tac failed');
|
|
|
|
return response()->json([
|
|
'status' => 'failed',
|
|
'message' => 'Error: ' . $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* One row per member per election; telefon must be the one for the current (latest) election.
|
|
*/
|
|
private function findVoterForCurrentElection($noKp)
|
|
{
|
|
$electionId = Util::getCurrentElection();
|
|
return Voter::where('no_kp', $noKp)
|
|
->where('election_id', $electionId)
|
|
->orderBy('id', 'desc')
|
|
->firstOrFail();
|
|
}
|
|
|
|
|
|
/* Check if the user wants to send OTP */
|
|
private function wantsSendOtp(Request $request, $default = true)
|
|
{
|
|
// Use array_key_exists: Request::has() treats false as "missing"
|
|
$input = $request->all();
|
|
if (! array_key_exists('send_otp', $input)) {
|
|
return $default;
|
|
}
|
|
|
|
$value = $input['send_otp'];
|
|
|
|
if (is_bool($value)) {
|
|
return $value;
|
|
}
|
|
|
|
if (is_int($value) || is_float($value)) {
|
|
return (int) $value !== 0;
|
|
}
|
|
|
|
$lower = strtolower(trim((string) $value));
|
|
|
|
if (in_array($lower, ['false', '0', 'no', 'off', ''], true)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function checkExpirationTAC($nokp): array
|
|
{
|
|
$currentDateTime = Carbon::now();
|
|
|
|
$expiretac = UserOTP::where('nokp', $nokp)
|
|
->whereDate('created_at', '=', $currentDateTime)
|
|
->whereDate('expired_at', '=', $currentDateTime)
|
|
->whereTime('expired_at', '>=', $currentDateTime->toTimeString())
|
|
->whereTime('created_at', '<=', $currentDateTime->toTimeString())
|
|
->latest()
|
|
->first();
|
|
|
|
if ($expiretac) {
|
|
$remaining = $this->getRemainingTime($expiretac->expired_at);
|
|
|
|
return ['notexpired' => true, 'remaining' => $remaining];
|
|
}
|
|
|
|
return ['notexpired' => false];
|
|
}
|
|
|
|
public function verifyTAC(Request $request)
|
|
{
|
|
$authenticateOTP = $this->getTACModel($request->no_kp, $request->token);
|
|
|
|
if (! $authenticateOTP['condition']) {
|
|
activity()
|
|
->withProperties([
|
|
'no_kp' => (string) $request->input('no_kp'),
|
|
'ip' => $request->ip(),
|
|
'user_agent' => $request->userAgent(),
|
|
'reason' => $authenticateOTP['message'] ?? 'invalid',
|
|
])
|
|
->log('voter verify tac failed');
|
|
|
|
return response()->json([
|
|
'status' => 'failed',
|
|
'message' => $authenticateOTP['message'],
|
|
]);
|
|
}
|
|
|
|
if (Auth::guard('voter')->attempt(['no_kp' => $request->no_kp, 'password' => 'admin', 'election_id' => Util::getCurrentElection()])) {
|
|
$user = Auth::guard('voter')->user();
|
|
activity()
|
|
->performedOn($user)
|
|
->withProperties([
|
|
'election_id' => Util::getCurrentElection(),
|
|
'voter_id' => $user->id,
|
|
'voter_name' => $user->name,
|
|
'no_kp' => $user->no_kp,
|
|
'ip' => $request->ip(),
|
|
'user_agent' => $request->userAgent(),
|
|
])
|
|
->log("voter login: {$user->name}");
|
|
|
|
return response()->json([
|
|
'status' => 'success',
|
|
'message' => 'Login successfully.',
|
|
'user' => Auth::guard('voter')->user(),
|
|
'election_status' => Util::getElectionStatus(),
|
|
'token' => Auth::guard('voter')->user()->createToken('My Token', ['vote'])->accessToken,
|
|
]);
|
|
}
|
|
|
|
activity()
|
|
->withProperties([
|
|
'no_kp' => (string) $request->input('no_kp'),
|
|
'ip' => $request->ip(),
|
|
'user_agent' => $request->userAgent(),
|
|
])
|
|
->log('voter login failed');
|
|
|
|
return response()->json([
|
|
'status' => 'failed',
|
|
'message' => 'No. KP tidak wujud. Atau sesi mengundi telah tamat',
|
|
]);
|
|
}
|
|
|
|
private function getTACModel($no_kp, $token)
|
|
{
|
|
$currentDateTime = Carbon::now();
|
|
|
|
$result = UserOTP::where('nokp', $no_kp)
|
|
->whereDate('created_at', '=', $currentDateTime)
|
|
->whereDate('expired_at', '=', $currentDateTime)
|
|
->whereTime('expired_at', '>=', $currentDateTime->toTimeString())
|
|
->whereTime('created_at', '<=', $currentDateTime->toTimeString())
|
|
->latest()
|
|
->first();
|
|
|
|
if (! $result) {
|
|
return ['condition' => false, 'message' => 'Token Expired'];
|
|
}
|
|
|
|
if ($result['token'] == $token) {
|
|
return ['condition' => true];
|
|
}
|
|
|
|
return ['condition' => false, 'message' => 'Invalid Token'];
|
|
}
|
|
|
|
private function getTokenSMS($tele, $date)
|
|
{
|
|
$tac_token = $this->randum_number(6, 2, false);
|
|
if (env('APP_ENV') == 'production') {
|
|
$mobileno = '6' . $tele;
|
|
$messages = sprintf(config('onewaysms.message'), $tac_token, $date);
|
|
|
|
$parameter = [
|
|
'apiusername' => env('ONEWAY_SMS_USERNAME'),
|
|
'apipassword' => env('ONEWAY_SMS_PASSWORD'),
|
|
'senderid' => env('ONEWAY_SMS_SENDERID', 'INFO'),
|
|
'mobileno' => $mobileno,
|
|
'message' => $messages,
|
|
'languagetype' => env('ONEWAY_SMS_LANG', 1),
|
|
];
|
|
|
|
$client = new Client(['verify' => false]);
|
|
$status = $client->get('http://gateway.onewaysms.com.my:10001/api.aspx', [
|
|
'query' => $parameter,
|
|
]);
|
|
|
|
if ($status) {
|
|
return $tac_token;
|
|
}
|
|
|
|
return 'error';
|
|
}
|
|
|
|
return $tac_token;
|
|
}
|
|
|
|
private function randum_number($len = 6, $dup = 1, $sort = false)
|
|
{
|
|
if ($dup < 1) {
|
|
throw new \InvalidArgumentException('Second argument is < 1');
|
|
}
|
|
|
|
$num = range(0, 9);
|
|
shuffle($num);
|
|
|
|
$num = array_slice($num, 0, ($len - $dup) + 1);
|
|
|
|
if ($dup > 0) {
|
|
$k = array_rand($num, 1);
|
|
for ($i = 0; $i < ($dup - 1); $i++) {
|
|
$num[] = $num[$k];
|
|
}
|
|
}
|
|
|
|
if ($sort) {
|
|
sort($num);
|
|
}
|
|
|
|
return implode('', $num);
|
|
}
|
|
|
|
private function getRemainingTime($expired_at)
|
|
{
|
|
$currentDateTime = Carbon::now();
|
|
$expiredAt = Carbon::parse($expired_at);
|
|
|
|
$remainingSeconds = $currentDateTime->diffInSeconds($expiredAt);
|
|
|
|
return gmdate('H:i:s', $remainingSeconds);
|
|
}
|
|
}
|