DONE: disable auto trigger tac

This commit is contained in:
ISMAIL MASSERAN
2026-04-19 10:22:14 +08:00
parent a5aedb8e56
commit f31dcb0320
11 changed files with 454 additions and 75583 deletions
+2 -1
View File
@@ -1 +1,2 @@
[ ] checking user can vote one or two based on number of nominees
[ ] checking user can vote one or two based on number of nominees
[x] disable auto trigger tac
@@ -9,6 +9,7 @@ use App\UserOTP;
use App\Voter;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Auth;
use GuzzleHttp\Client;
@@ -16,15 +17,39 @@ class LoginController extends Controller
{
public function __invoke(Request $request)
{
/* Request OTP */
/* 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();
$voter = Voter::where('no_kp', $request->no_kp)->firstOrFail();
if ($voter) {
$expiration = $this->checkExpirationTAC($request->no_kp);
@@ -78,6 +103,21 @@ class LoginController extends Controller
'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([
@@ -95,6 +135,47 @@ class LoginController extends Controller
}
}
/**
* 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();
+1
View File
File diff suppressed because one or more lines are too long
+2
View File
File diff suppressed because one or more lines are too long
+20
View File
@@ -0,0 +1,20 @@
/*!
* html2canvas 1.4.1 <https://html2canvas.hertzen.com>
* Copyright (c) 2022 Niklas von Hertzen <https://hertzen.com>
* Released under MIT License
*/
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
+2
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
/*! @license DOMPurify 3.3.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.3.3/LICENSE */
+2 -75560
View File
File diff suppressed because one or more lines are too long
+274
View File
@@ -0,0 +1,274 @@
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <http://feross.org>
* @license MIT
*/
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/*! sheetjs (C) 2013-present SheetJS -- http://sheetjs.com */
/**
* @license
Copyright (c) 2008, Adobe Systems Incorporated
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Adobe Systems Incorporated nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @license
*
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
*/
/**
* @license
* (c) Dean McNamee <dean@gmail.com>, 2013.
*
* https://github.com/deanm/omggif
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* omggif is a JavaScript implementation of a GIF 89a encoder and decoder,
* including animation and compression. It does not rely on any specific
* underlying system, so should run in the browser, Node, or Plask.
*/
/**
* @license
* Copyright (c) 2014 Steven Spungin (TwelveTone LLC) steven@twelvetone.tv
*
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
*/
/**
* @license
* Copyright (c) 2017 Aras Abbasi
*
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
*/
/**
* @license
* Copyright (c) 2018 Aras Abbasi
*
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
*/
/**
* @license
* Copyright (c) 2019 Aras Abbasi
*
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
*/
/**
* @license
* FPDF is released under a permissive license: there is no usage restriction.
* You may embed it freely in your application (commercial or not), with or
* without modifications.
*
* Reference: http://www.fpdf.org/en/script/script37.php
*/
/**
* @license
* Joseph Myers does not specify a particular license for his work.
*
* Author: Joseph Myers
* Accessed from: http://www.myersdaily.org/joseph/javascript/md5.js
*
* Modified by: Owen Leong
*/
/**
* @license
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
* Author: Owen Leong (@owenl131)
* Date: 15 Oct 2020
* References:
* https://www.cs.cmu.edu/~dst/Adobe/Gallery/anon21jul01-pdf-encryption.txt
* https://github.com/foliojs/pdfkit/blob/master/lib/security.js
* http://www.fpdf.org/en/script/script37.php
*/
/**
* @license
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
*/
/**
* @license
* Unicode Bidi Engine based on the work of Alex Shensis (@asthensis)
* MIT License
*/
/**
* @license
* jsPDF fileloading PlugIn
* Copyright (c) 2018 Aras Abbasi (aras.abbasi@gmail.com)
*
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
*/
/**
* @license
* jsPDF filters PlugIn
* Copyright (c) 2014 Aras Abbasi
*
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
*/
/**
* @license
* jsPDF virtual FileSystem functionality
*
* Licensed under the MIT License.
* http://opensource.org/licenses/mit-license
*/
/**
* A class to parse color values
* @author Stoyan Stefanov <sstoo@gmail.com>
* {@link http://www.phpied.com/rgb-color-parser-in-javascript/}
* @license Use it if you like it
*/
/** ====================================================================
* @license
* jsPDF XMP metadata plugin
* Copyright (c) 2016 Jussi Utunen, u-jussi@suomi24.fi
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* ====================================================================
*/
/** @license
* Copyright (c) 2017 Dominik Homberger
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
https://webpjs.appspot.com
WebPRiffParser dominikhlbg@gmail.com
*/
/** @license
*
* jsPDF - PDF Document creation from JavaScript
* Version 4.2.1 Built on 2026-03-17T11:11:27.057Z
* CommitID 00000000
*
* Copyright (c) 2010-2025 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF
* 2015-2025 yWorks GmbH, http://www.yworks.com
* 2015-2025 Lukas Holländer <lukas.hollaender@yworks.com>, https://github.com/HackbrettXXX
* 2016-2018 Aras Abbasi <aras.abbasi@gmail.com>
* 2010 Aaron Spike, https://github.com/acspike
* 2012 Willow Systems Corporation, https://github.com/willowsystems
* 2012 Pablo Hess, https://github.com/pablohess
* 2012 Florian Jenett, https://github.com/fjenett
* 2013 Warren Weckesser, https://github.com/warrenweckesser
* 2013 Youssef Beddad, https://github.com/lifof
* 2013 Lee Driscoll, https://github.com/lsdriscoll
* 2013 Stefan Slonevskiy, https://github.com/stefslon
* 2013 Jeremy Morel, https://github.com/jmorel
* 2013 Christoph Hartmann, https://github.com/chris-rock
* 2014 Juan Pablo Gaviria, https://github.com/juanpgaviria
* 2014 James Makes, https://github.com/dollaruw
* 2014 Diego Casorran, https://github.com/diegocr
* 2014 Steven Spungin, https://github.com/Flamenco
* 2014 Kenneth Glassey, https://github.com/Gavvers
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Contributor(s):
* siefkenj, ahwolf, rickygu, Midnith, saintclair, eaparango,
* kim3er, mfo, alnorth, Flamenco
*/
@@ -44,8 +44,12 @@
methods: {
login() {
this.startLoading();
const noKp = document.getElementById('no_kp').value.trim();
axios
.post(config.API + 'voter/login', $('#login_form').serialize())
.post(config.API + 'voter/login', {
no_kp: noKp,
send_otp: false
})
.then(response => {
this.stopLoading();
if (this.util.showResult(response, 'success')) {
@@ -9,28 +9,42 @@
<div class="panel-body">
<form @submit.prevent="login" id="login_form">
<div class="m-4">
<span>sila masukkan kod pengesahan yang dihantar ke</span>
<br>-<b>+6 {{ $route.params.notel }}</b>
<template v-if="!otpSent">
<span>Sila sahkan nombor telefon anda. Kod akan dihantar ke nombor berakhir:</span>
<br><b>+6 {{ phoneLastFour }}</b>
</template>
<template v-else>
<span>Sila masukkan kod pengesahan yang dihantar ke nombor berakhir:</span>
<br><b>+6 {{ phoneLastFour }}</b>
</template>
<p class="text-muted small" style="margin-top:12px;margin-bottom:0;">
Jika nombor ini tidak betul atau anda tidak menerima OTP, sila pergi ke kaunter IT.
</p>
</div>
<div v-if="showDebugOtp" class="alert alert-info" style="margin-bottom:20px;">
OTP local development: <b>{{ $route.params.debug_otp }}</b>
<div v-if="otpSent && showDebugOtp" class="alert alert-info" style="margin-bottom:20px;">
OTP local development: <b>{{ debugOtp }}</b>
</div>
<div class="justify-content-center d-flex"
<div v-if="!otpSent" class="form-group" style="margin-top: 20px;">
<button type="button" class="btn btn-primary form-control" :disabled="otpSending" @click="requestOtp">
<span v-if="otpSending" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Hantar OTP ke nombor ini
</button>
</div>
<div v-show="otpSent" class="justify-content-center d-flex"
style="margin-top:20px;margin-bottom:20px;display: flex;flex-direction: row;justify-content: center;align-items: center;">
<v-otp-input ref="otpInput" input-classes="otp-input" separator="-" :num-inputs="6"
:should-auto-focus="true" :is-input-num="true" @on-change="handleOnChange"
@on-complete="handleOnComplete" />
</div>
<div style="margin-bottom:20px;">
<!-- <span>dapatkan Kod baru dalam masa : 01:23 saat</span> -->
<!-- <Countdown ref="countdown"></Countdown> -->
<div v-if="otpSent" style="margin-bottom:20px;">
<a @click="resendVerify">Hantar Kembali OTP!</a>
</div>
<div class="form-group">
<div v-if="otpSent" class="form-group">
<input ref="submitbtn" type="submit" class="btn btn-primary form-control" value="Sahkan & Teruskan"
disabled />
</div>
@@ -43,18 +57,24 @@
export default {
data: function () {
return {
loading: false
loading: false,
otpSent: false,
otpSending: false,
debugOtp: null
}
},
computed: {
showDebugOtp: function () {
return process.env.NODE_ENV !== 'production' && !!this.$route.params.debug_otp;
return process.env.NODE_ENV !== 'production' && !!this.debugOtp;
},
phoneLastFour: function () {
var digits = String(this.$route.params.notel || '').replace(/\D/g, '');
if (digits.length >= 4) {
return digits.slice(-4);
}
return digits.length ? digits : '----';
}
},
created: function () {
console.log(this.$route.params.notel);
},
methods: {
handleOnComplete(value) {
console.log("OTP completed: ", value);
@@ -67,18 +87,41 @@ export default {
handleClearInput() {
this.$refs.otpInput.clearInput();
},
requestOtp: function () {
this.sendOtp({ isResend: false });
},
resendVerify: function () {
this.sendOtp({ isResend: true });
},
sendOtp: function (opts) {
var vm = this;
var isResend = opts && opts.isResend;
this.otpSending = true;
axios.post(config.API + 'voter/login', {
no_kp: this.$route.params.nokp
no_kp: this.$route.params.nokp,
send_otp: true
})
.then(function (response) {
if (vm.util.showResult(response, 'success')) {
console.log('resend verify');
vm.otpSent = true;
if (response.data.debug_otp) {
vm.debugOtp = response.data.debug_otp;
}
if (isResend && vm.$refs.otpInput) {
vm.$refs.otpInput.clearInput();
}
vm.$nextTick(function () {
if (vm.$refs.submitbtn) {
vm.$refs.submitbtn.disabled = true;
}
});
}
})
.catch(function (error) {
vm.util.showResult(error, 'error');
})
.then(function () {
vm.otpSending = false;
});
},
startLoading: function () {
@@ -90,7 +133,7 @@ export default {
this.loading = false;
},
login: function () {
if (this.loading) return;
if (this.loading || !this.otpSent) return;
let vm = this;