lonnieezell / myth-auth

One-stop Auth package for CodeIgniter 4
MIT License
633 stars 207 forks source link

Exception in json post! #427

Open rafinhaa opened 3 years ago

rafinhaa commented 3 years ago

Hi, i implemented myth auth in a test api and when i try to create a new user via "application/json" i get an exception

{
  "title": "ErrorException",
  "type": "ErrorException",
  "code": 500,
  "message": "Undefined offset: 1",
  "file": "/var/www/html/vendor/myth/auth/src/Authentication/Passwords/NothingPersonalValidator.php",
  "line": 90,
  "trace": [
    {
      "file": "/var/www/html/vendor/myth/auth/src/Authentication/Passwords/NothingPersonalValidator.php",
      "line": 90,
      "function": "errorHandler",
      "class": "CodeIgniter\\Debug\\Exceptions",
      "type": "->",
      "args": [
        8,
        "Undefined offset: 1",
        "/var/www/html/vendor/myth/auth/src/Authentication/Passwords/NothingPersonalValidator.php",
        90,
        {
          "password": "gilmar@ar.com",
          "user": [],
          "userName": "",
          "email": "",
          "valid": true,
          "needles": [
            ""
          ],
          "localPart": ""
        }
      ]
    },
    {
      "file": "/var/www/html/vendor/myth/auth/src/Authentication/Passwords/NothingPersonalValidator.php",
      "line": 39,
      "function": "isNotPersonal",
      "class": "Myth\\Auth\\Authentication\\Passwords\\NothingPersonalValidator",
      "type": "->",
      "args": [
        "gilmar@ar.com",
        []
      ]
    },
    {
      "file": "/var/www/html/vendor/myth/auth/src/Authentication/Passwords/PasswordValidator.php",
      "line": 55,
      "function": "check",
      "class": "Myth\\Auth\\Authentication\\Passwords\\NothingPersonalValidator",
      "type": "->",
      "args": [
        "gilmar@ar.com",
        []
      ]
    },
    {
      "file": "/var/www/html/vendor/myth/auth/src/Authentication/Passwords/ValidationRules.php",
      "line": 45,
      "function": "check",
      "class": "Myth\\Auth\\Authentication\\Passwords\\PasswordValidator",
      "type": "->",
      "args": [
        "gilmar@ar.com",
        []
      ]
    },
    {
      "file": "/var/www/html/vendor/codeigniter4/framework/system/Validation/Validation.php",
      "line": 312,
      "function": "strong_password",
      "class": "Myth\\Auth\\Authentication\\Passwords\\ValidationRules",
      "type": "->",
      "args": [
        "gilmar@ar.com",
        null
      ]
    },
    {
      "file": "/var/www/html/vendor/codeigniter4/framework/system/Validation/Validation.php",
      "line": 159,
      "function": "processRules",
      "class": "CodeIgniter\\Validation\\Validation",
      "type": "->",
      "args": [
        "password",
        "password",
        "gilmar@ar.com",
        [
          "required",
          "strong_password"
        ],
        {
          "name": "rafael",
          "lastname": "sonicne",
          "email": "gilmar@ar.com",
          "password": "gilmar@ar.com",
          "cpassword": "gilmar@ar.com",
          "DBGroup": null
        }
      ]
    },
    {
      "file": "/var/www/html/vendor/codeigniter4/framework/system/Controller.php",
      "line": 183,
      "function": "run",
      "class": "CodeIgniter\\Validation\\Validation",
      "type": "->",
      "args": []
    },
    {
      "file": "/var/www/html/app/Controllers/Api/V1/Users.php",
      "line": 99,
      "function": "validate",
      "class": "CodeIgniter\\Controller",
      "type": "->",
      "args": [
        {
          "email": {
            "rules": "required|valid_email|is_unique[users.email]",
            "errors": {
              "required": "O e-mail é necessário",
              "valid_email": "Você deve inserir um email válido",
              "is_unique": "Esse e-mail já está cadastrado"
            }
          },
          "name": {
            "rules": "required|min_length[2]",
            "errors": {
              "required": "Seu nome é necessário",
              "min_length": "Seu nome deve ter pelo menos 2 caracteres"
            }
          },
          "lastname": {
            "rules": "required|min_length[2]",
            "errors": {
              "required": "Seu sobrenome é necessário",
              "min_length": "Seu sobrenome deve ter pelo menos 2 caracteres"
            }
          },
          "password": {
            "rules": "required|strong_password",
            "errors": {
              "required": "A senha é necessário",
              "strong_password": "Essa senha está facil demais"
            }
          },
          "cpassword": {
            "rules": "required|matches[password]",
            "errors": {
              "required": "Você não digitou a confirmação da senha",
              "matches": "As senhas não são iguais"
            }
          }
        }
      ]
    },
    {
      "file": "/var/www/html/vendor/codeigniter4/framework/system/CodeIgniter.php",
      "line": 928,
      "function": "create",
      "class": "App\\Controllers\\Api\\V1\\Users",
      "type": "->",
      "args": []
    },
    {
      "file": "/var/www/html/vendor/codeigniter4/framework/system/CodeIgniter.php",
      "line": 436,
      "function": "runController",
      "class": "CodeIgniter\\CodeIgniter",
      "type": "->",
      "args": [
        {}
      ]
    },
    {
      "file": "/var/www/html/vendor/codeigniter4/framework/system/CodeIgniter.php",
      "line": 336,
      "function": "handleRequest",
      "class": "CodeIgniter\\CodeIgniter",
      "type": "->",
      "args": [
        null,
        {
          "handler": "file",
          "backupHandler": "dummy",
          "storePath": "/var/www/html/writable/cache/",
          "cacheQueryString": false,
          "prefix": "",
          "ttl": 60,
          "file": {
            "storePath": "/var/www/html/writable/cache/",
            "mode": 416
          },
          "memcached": {
            "host": "127.0.0.1",
            "port": 11211,
            "weight": 1,
            "raw": false
          },
          "redis": {
            "host": "127.0.0.1",
            "password": null,
            "port": 6379,
            "timeout": 0,
            "database": 0
          },
          "validHandlers": {
            "dummy": "CodeIgniter\\Cache\\Handlers\\DummyHandler",
            "file": "CodeIgniter\\Cache\\Handlers\\FileHandler",
            "memcached": "CodeIgniter\\Cache\\Handlers\\MemcachedHandler",
            "predis": "CodeIgniter\\Cache\\Handlers\\PredisHandler",
            "redis": "CodeIgniter\\Cache\\Handlers\\RedisHandler",
            "wincache": "CodeIgniter\\Cache\\Handlers\\WincacheHandler"
          }
        },
        false
      ]
    },
    {
      "file": "/var/www/html/public/index.php",
      "line": 37,
      "function": "run",
      "class": "CodeIgniter\\CodeIgniter",
      "type": "->",
      "args": []
    }
  ]
}

When I send the request through "multipart/form-data" I get the registration successfully!

I investigated the myth files to find a solution to this problem, but I couldn't.

Below are the classes I used as a test:

<?php

namespace App\Controllers\Api\V1;

use CodeIgniter\RESTful\ResourceController;

class Users extends ResourceController
{
    protected $modelName = 'App\Models\UserModel';
    protected $format    = 'json';

    public function create(){
        $rules = [
            'email' => [
                'rules' => 'required|valid_email|is_unique[users.email]',               
                'errors' => [
                    'required' => 'O e-mail é necessário',
                    'valid_email' => 'Você deve inserir um email válido',
                    'is_unique' => 'Esse e-mail já está cadastrado',
                ],
            ],
            'name' => [
                'rules' => 'required|min_length[2]',
                'errors' => [
                    'required' => 'Seu nome é necessário',
                    'min_length' => 'Seu nome deve ter pelo menos 2 caracteres',
                ],
            ],
            'lastname' => [
                'rules' => 'required|min_length[2]',
                'errors' => [
                    'required' => 'Seu sobrenome é necessário',
                    'min_length' => 'Seu sobrenome deve ter pelo menos 2 caracteres',
                ],
            ],
            'password' => [
                'rules' => 'required|strong_password',
                'errors' => [
                    'required' => 'A senha é necessário',
                    'strong_password' => 'Essa senha está facil demais',
                ],                
            ],          
            'cpassword' => [
                'rules' => 'required|matches[password]',
                'errors' => [
                    'required' => 'Você não digitou a confirmação da senha',
                    'matches' => 'As senhas não são iguais',
                ],                
            ],
        ];

        if (! $this->validate($rules)){
            return $this->respond([
                'status' => 500,
                'error' => true,
                'message' => $this->validator->getErrors(),
                'data' => []
            ],500);
        }
        die('registration ok');
    }

}

Routes: $routes->resource('api/v1/users');

Prints: fail ok

Codeigniter 4.1.3 PHP 7.3

rafinhaa commented 3 years ago

I found the cause of the problem, I had to modify the buildUserFromRequest method in the /src/Authentication/Passwords/ValidationRules.php file

protected function buildUserFromRequest()
{
    $fields = $this->prepareValidFields();

    if (strpos(service('request')->getHeaderLine('Content-Type'), 'application/json') !== false)
    {
        $data = (array) service('request')->getJSON();
    $data = array_intersect_key($data, array_fill_keys($fields, null));
    }else{
        $data = array_filter(service('request')->getPost($fields));
    }
    return new User($data);
}

Is this modification acceptable?

rafinhaa commented 3 years ago
protected function buildUserFromRequest()
{
    $fields = $this->prepareValidFields();

    $data = array_filter(service('request')->getPost($fields));
    if (empty($data)) {
        //convert request body to associative array
        $data = array_intersect_key(
            json_decode(service('request')->getBody(), true), 
            array_fill_keys($fields, null)
        );
    }
    return new User($data);
}

so it should be better!

lonnieezell commented 3 years ago

I think that is likely fine. If you can submit a PR for it, that would be awesome.

Sparking2 commented 2 years ago

Hi, no news about adding this issue to the release?

MGatner commented 2 years ago

If someone wants to take this on as a PR please do.

manageruz commented 2 years ago

I'll do

manageruz commented 2 years ago

Maybe just like this?

protected function buildUserFromRequest()
 {
        $fields = $this->prepareValidFields();

        // $data = array_filter(service('request')->getPost($fields));

        // pulling data from $_REQUEST, get rid of NULL values
        // works fine with both 'form-multipart' and 'application/json' content-types
        // return type is array
        $data = array_filter(service('request')->getVar($fields));

        return new User($data);
 }
manageruz commented 2 years ago

And what about Shield? Shield's code is same as Myth.