1

I'm working on a voting system with Laravel, I want a guest to vote only once per day. It's working fine if they are on a Wifi network as you can imagine as they always have the same IP address. But I tried with my 4G Network, and as we change IP every time we reuse 4G it doesn't work. I'm pretty sure there is no way to do it at all, the best would be to have accounts to votes, and only accounts would be allowed to vote. But that's not really what I want.

Here is what I have for now if you want an idea :

Migration:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateGuestsVotesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('guests_votes', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->ipAddress('ip');
            $table->bigInteger('candidate_id')->unsigned();
            $table->timestamp('updated_at');

            $table
                ->foreign('candidate_id')
                ->references('id')
                ->on('candidates')
                ->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('guests_votes');
    }
}

And globally I made a Guest Singleton to manage votes :

<?php

namespace App\Library;

use App\GuestVote;

/**
 * Singleton class to manage Guest vote
 *
 * @package App\Library
 */
class Guest {

    /**
     * @var Guest
     */
    private static $instance;

    /**
     * IP of the guest
     *
     * @var string
     */
    private static $ip;

    protected function __construct()
    {
        Guest::$ip = \Request::ip();
    }

    /**
     * Singleton, retrieve only one instance
     *
     * @return Guest
     */
    public static function getInstance(): Guest
    {
        if (empty(Guest::$instance)) {
            Guest::$instance = new Guest();
        }
        return Guest::$instance;
    }

    /**
     * Guest can vote for this candidate ?
     *
     * @param \App\Candidate $candidate
     * @return bool
     */
    public function canVote($candidate)
    {
        if (!($candidate instanceof \App\Candidate)) return false;

        $guestVote = GuestVote::where([
            'ip' => Guest::$ip,
            'candidate_id' => $candidate->id
        ])
            ->select([
                'ip',
                'candidate_id',
                'updated_at'
            ])
            ->first();

        if (!$guestVote || now()->greaterThan($guestVote->updated_at->addDay(1))) return true;
        return false;
    }

    /**
     * Vote for a candidate
     *
     * @param Illuminate\Database\Eloquent\Builder|Illuminate\Database\Eloquent\Model|object|\App\Candidate|\App\Candidate[]|Illuminate\Database\Eloquent\Collection $candidateVote
     * @throws \Exception
     */
    public function vote($candidateVote)
    {
        if (!$candidateVote) return;

        $candidates = [];
        if ($candidateVote instanceof \App\Candidate) {
            $candidates[] = $candidateVote;
        } else {
            $candidates = $candidateVote;
        }

        foreach ($candidates as $candidate) {
            if (!Guest::canVote($candidate)) continue;

            $guestVote = GuestVote::firstOrNew([
                'ip' => Guest::$ip,
                'candidate_id' => $candidate->id
            ]);

            try {
                \DB::beginTransaction();

                $candidate->increment('votes');
                $guestVote->touch();

                $guestVote->save();
                $candidate->save();

                \DB::commit();
            } catch (\Exception $e) {
                \Log::error('Error while Guest was voting : ' . $e);
                \DB::rollBack();
            }
        }
    }

}

Everything is working fine until I use my 4G Network. If you have any idea how we can restrict even 4G networks? I tried to see if we couldn't get the MAC Address of the device, but sadly no :(

Thanks

Théo Benoit
  • 567
  • 1
  • 4
  • 14
  • 1
    Does this answer your question? [Ways to create a unique user fingerprint in PHP](https://stackoverflow.com/questions/4085230/ways-to-create-a-unique-user-fingerprint-in-php) – Wahyu Kristianto Mar 05 '20 at 11:20
  • It helped me to understand it's not that easy to do so. I checked on Google what developers says about "fingerprint", that's pretty interesting. Didn't found a way to do it for now – Théo Benoit Mar 05 '20 at 12:03
  • 1
    I did not answer, because this is a bassed opinion. For my some projects, I use [Fingerprint.js](https://fingerprintjs.com/). Maybe it can help you. – Wahyu Kristianto Mar 05 '20 at 12:05

0 Answers0