Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

php 7 without user and password #35

Closed
manelj opened this issue Apr 13, 2017 · 15 comments
Closed

php 7 without user and password #35

manelj opened this issue Apr 13, 2017 · 15 comments
Labels

Comments

@manelj
Copy link

manelj commented Apr 13, 2017

When i test a token request without sending user nor password it return a good token.
I i send user with wrong password it returns unauthorized

@tuupola
Copy link
Owner

tuupola commented Apr 13, 2017

Can you show me the code you are using?

@manelj
Copy link
Author

manelj commented Apr 13, 2017

middleware.php

`<?php

/*

*/
use App\Token;

use Slim\Middleware\JwtAuthentication;
use Slim\Middleware\HttpBasicAuthentication;
use Tuupola\Middleware\Cors;
use Gofabian\Negotiation\NegotiationMiddleware;
use Micheh\Cache\CacheUtil;

$container = $app->getContainer();

//var_dump($container);

$container["HttpBasicAuthentication"] = function ($container) {
return new HttpBasicAuthentication([
"secure" => false,
"path" => "/token",
//"realm" => "Protected",
//"relaxed" => ["192.168.50.52"],
"environment" => "REDIRECT_HTTP_AUTHORIZATION",
"users" => [
"test" => "1234"
],
"callback" => function ($request, $response, $arguments) {
// print_r($arguments);
// die;
},
"error" => function ($request, $response, $arguments) {
$data = [];
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response->write(json_encode($data, JSON_UNESCAPED_SLASHES));
}
]);
};

$container["token"] = function ($container) {
return new Token;
};

$container["JwtAuthentication"] = function ($container) {
return new JwtAuthentication([
//"cookie" => "taskiu",
"secure" => false,
"path" => "/",
"passthrough" => ["/token", "/info"],
"secret" => getenv("JWT_SECRET"),
"logger" => $container["logger"],
//"relaxed" => ["192.168.50.52"],
"environment" => ["HTTP_AUTHORIZATION", "REDIRECT_HTTP_AUTHORIZATION", "REDIRECT_REDIRECT_HTTP_AUTHORIZATION"],
"error" => function ($request, $response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
},
"callback" => function ($request, $response, $arguments) use ($container) {
$container["token"]->hydrate($arguments["decoded"]);
}
]);
};

$container["Cors"] = function ($container) {
return new Cors([
"logger" => $container["logger"],
"origin" => ["*"],
"methods" => ["GET", "POST", "PUT", "PATCH", "DELETE"],
"headers.allow" => ["Authorization", "If-Match", "If-Unmodified-Since"],
"headers.expose" => ["Authorization", "Etag"],
"credentials" => true,
"cache" => 60,
"error" => function ($request, $response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}
]);
};

$container["Negotiation"] = function ($container) {
return new NegotiationMiddleware([
"accept" => ["application/json"]
]);
};

$app->add("HttpBasicAuthentication");
$app->add("JwtAuthentication");
$app->add("Cors");
$app->add("Negotiation");

$container["cache"] = function ($container) {
return new CacheUtil;
};
`

@manelj
Copy link
Author

manelj commented Apr 13, 2017

HttpBasicAuthentication.php

`<?php

/*

*/

namespace Slim\Middleware;

use Slim\Middleware\HttpBasicAuthentication\AuthenticatorInterface;
use Slim\Middleware\HttpBasicAuthentication\ArrayAuthenticator;
use Slim\Middleware\HttpBasicAuthentication\RequestMethodRule;
use Slim\Middleware\HttpBasicAuthentication\RequestPathRule;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

class HttpBasicAuthentication
{
private $rules;
private $options = [
"secure" => true,
"relaxed" => ["localhost", "127.0.0.1"],
"users" => null,
"path" => null,
"passthrough" => null,
"realm" => "Protected",
"environment" => "HTTP_AUTHORIZATION",
"authenticator" => null,
"callback" => null,
"error" => null
];

public function __construct($options = [])
{
    /* Setup stack for rules */
    $this->rules = new \SplStack;

    /* Store passed in options overwriting any defaults */
    $this->hydrate($options);

    /* If array of users was passed in options create an authenticator */
    if (is_array($this->options["users"])) {
        $this->options["authenticator"] = new ArrayAuthenticator([
            "users" => $this->options["users"]
        ]);
    }

    /* If nothing was passed in options add default rules. */
    if (!isset($options["rules"])) {
        $this->addRule(new RequestMethodRule([
            "passthrough" => ["OPTIONS"]
        ]));
    }

    /* If path was given in easy mode add rule for it. */
    if (null !== $this->options["path"]) {
        $this->addRule(new RequestPathRule([
            "path" => $this->options["path"],
            "passthrough" => $this->options["passthrough"]
        ]));
    }

    /* There must be an authenticator either passed via options */
    /* or added because $this->options["users"] was an array. */
    if (null === $this->options["authenticator"]) {
        throw new \RuntimeException("Authenticator or users array must be given");
    }
}

public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
{
    $host = $request->getUri()->getHost();
    $scheme = $request->getUri()->getScheme();
    $server_params = $request->getServerParams();

    /* If rules say we should not authenticate call next and return. */
    if (false === $this->shouldAuthenticate($request)) {
        return $next($request, $response);
    }

    /* HTTP allowed only if secure is false or server is in relaxed array. */
    if ("https" !== $scheme && true === $this->options["secure"]) {
        if (!in_array($host, $this->options["relaxed"])) {
            $message = sprintf(
                "Insecure use of middleware over %s denied by configuration.",
                strtoupper($scheme)
            );
            throw new \RuntimeException($message);
        }
    }

    /* Just in case. */
    $user = false;
    $password = false;

    /* If using PHP in CGI mode. */
    if (isset($server_params[$this->options["environment"]])) {
        if (preg_match("/Basic\s+(.*)$/i", $server_params[$this->options["environment"]], $matches)) {
            list($user, $password) = explode(":", base64_decode($matches[1]), 2);
        }
    } else {
        if (isset($server_params["PHP_AUTH_USER"])) {
            $user = $server_params["PHP_AUTH_USER"];
        }
        if (isset($server_params["PHP_AUTH_PW"])) {
            $password = $server_params["PHP_AUTH_PW"];
        }
    }

    $params = ["user" => $user, "password" => $password];

    /* Check if user authenticates. */
    if (false === $this->options["authenticator"]($params)) {
        /* Set response headers before giving it to error callback */
        $response = $response
            ->withStatus(401)
            ->withHeader("WWW-Authenticate", sprintf('Basic realm="%s"', $this->options["realm"]));

        return $this->error($request, $response, [
            "message" => "Authentication failed"
        ]);
    }

    /* If callback returns false return with 401 Unauthorized. */
    if (is_callable($this->options["callback"])) {
        if (false === $this->options["callback"]($request, $response, $params)) {
            /* Set response headers before giving it to error callback */
            $response = $response
                ->withStatus(401)
                ->withHeader("WWW-Authenticate", sprintf('Basic realm="%s"', $this->options["realm"]));

            return $this->error($request, $response, [
                "message" => "Callback returned false"
            ]);
        }
    }


    /* Everything ok, call next middleware. */
    return $next($request, $response);
}

private function hydrate($data = [])
{
    foreach ($data as $key => $value) {
        $method = "set" . ucfirst($key);
        if (method_exists($this, $method)) {
            call_user_func([$this, $method], $value);
        }
    }
}

private function shouldAuthenticate(RequestInterface $request)
{
    /* If any of the rules in stack return false will not authenticate */
    foreach ($this->rules as $callable) {
        if (false === $callable($request)) {
            return false;
        }
    }
    return true;
}

/**
 * Call the error handler if it exists
 *
 * @return void
 */
public function error(RequestInterface $request, ResponseInterface $response, $arguments)
{
    if (is_callable($this->options["error"])) {
        $handler_response = $this->options["error"]($request, $response, $arguments);
        if (is_a($handler_response, "\Psr\Http\Message\ResponseInterface")) {
            return $handler_response;
        }
    }
    return $response;
}

public function getAuthenticator()
{
    return $this->options["authenticator"];
}

public function setAuthenticator($authenticator)
{
    $this->options["authenticator"] = $authenticator;
    return $this;
}

public function getUsers()
{
    return $this->options["users"];
}

/* Do not mess with users right now */
private function setUsers($users)
{
    $this->options["users"] = $users;
    return $this;
}

public function getPath()
{
    return $this->options["path"];
}

/* Do not mess with path right now */
private function setPath($path)
{
    $this->options["path"] = $path;
    return $this;
}

public function getPassthrough()
{
    return $this->options["passthrough"];
}

private function setPassthrough($passthrough)
{
    $this->options["passthrough"] = $passthrough;
    return $this;
}

public function getRealm()
{
    return $this->options["realm"];
}

public function setRealm($realm)
{
    $this->options["realm"] = $realm;
    return $this;
}

public function getEnvironment()
{
    return $this->options["environment"];
}

public function setEnvironment($environment)
{
    $this->options["environment"] = $environment;
    return $this;
}

/**
 * Get the secure flag
 *
 * @return boolean
 */
public function getSecure()
{
    return $this->options["secure"];
}

/**
 * Set the secure flag
 *
 * @return self
 */
public function setSecure($secure)
{
    $this->options["secure"] = !!$secure;
    return $this;
}

/**
 * Get hosts where secure rule is relaxed
 *
 * @return string
 */
public function getRelaxed()
{
    return $this->options["relaxed"];
}

/**
 * Set hosts where secure rule is relaxed
 *
 * @return self
 */
public function setRelaxed(array $relaxed)
{
    $this->options["relaxed"] = $relaxed;
    return $this;
}

/**
 * Get the callback
 *
 * @return string
 */
public function getCallback()
{
    return $this->options["callback"];
}

/**
 * Set the callback
 *
 * @return self
 */
public function setCallback($callback)
{
    $this->options["callback"] = $callback;
    return $this;
}

/**
 * Get the error handler
 *
 * @return string
 */
public function getError()
{
    return $this->options["error"];
}

/**
 * Set the error handler
 *
 * @return self
 */
public function setError($error)
{
    $this->options["error"] = $error;
    return $this;
}

public function getRules()
{
    return $this->rules;
}

public function setRules(array $rules)
{
    /* Clear the stack */
    unset($this->rules);
    $this->rules = new \SplStack;

    /* Add the rules */
    foreach ($rules as $callable) {
        $this->addRule($callable);
    }
    return $this;
}

public function addRule($callable)
{
    $this->rules->push($callable);
    return $this;
}

}
`

@manelj
Copy link
Author

manelj commented Apr 13, 2017

I use this REST client.

chrome://restclient/content/restclient.html

@tuupola
Copy link
Owner

tuupola commented Apr 13, 2017

HttpBasicAuthentication.php

Have you done some changes to the middleware or why did you post this code?

@manelj
Copy link
Author

manelj commented Apr 13, 2017

No i don't change anything in the HttpBasicAuthentication.php

@tuupola
Copy link
Owner

tuupola commented Apr 13, 2017

This is too big and confusing code dump. Can you show minimal code example which reproduces the problem together with example curl request showing the request and response headers. Something like:

$ curl --include --user user:password https//example.com/token

@manelj
Copy link
Author

manelj commented Apr 13, 2017

With curl testing it works fine. I think the REST client for chrome will "remember" de user basic auth, because i have removed it the credentials to test and it send equal.

One question: when i try like this i got a warning:
curl "http://taskiu.cesigrup.es/api/v1/public/token" --request POST --include --insecure --header "Content-Type: application/json" --data '["tareas.all"]' --user test:1234

@manelj
Copy link
Author

manelj commented Apr 13, 2017

This is the response

HTTP/1.1 201 Created
Date: Thu, 13 Apr 2017 14:27:39 GMT
Server: Apache
Content-Length: 432
Content-Type: application/json

{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0OTIwOTM2NTksImV4cCI6MTQ5MjEwMDg1OSwianRpIjoiNG1YOFEzYVlvWU9kdWRtakp4Wk96dyIsInN1YiI6bnVsbCwic2NvcGUiOm51bGx9.2TdEejv6NMvUoFFqXR2T5lXilS1LLH2b2hL-rN6OFno", "expires": 1492100859
}
Warning: array_filter() expects parameter 1 to be array, null given in <
b>/var/www/vhost/cesigrup.es/home/html/taskiu/api/v1/routes/token.php on line 34

@tuupola
Copy link
Owner

tuupola commented Apr 13, 2017

What is in token.php around line 34? It is not part of Basic Auth middleware.

@manelj
Copy link
Author

manelj commented Apr 13, 2017

$scopes = array_filter($requested_scopes, function ($needle) use ($valid_scopes) {
    return in_array($needle, $valid_scopes);
});

@manelj
Copy link
Author

manelj commented Apr 13, 2017

I'm testing your example https://github.com/tuupola/slim-api-skeleton
With php 7

@tuupola
Copy link
Owner

tuupola commented Apr 13, 2017

Fresh install with slim-api-skeleton works ok for me. You might have changed some code which broke the token generation.

$ curl http://192.168.50.52/token --request POST --include --insecure --header "Content-Type: application/json" --data '["tareas.all"]' --user test:test

HTTP/1.1 201 Created
Date: Thu, 13 Apr 2017 14:40:02 GMT
Server: Apache/2.2.15 (CentOS)
X-Powered-By: PHP/5.6.30
Content-Length: 247
Connection: close
Content-Type: application/json

{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0OTIwOTQ0MDMsImV4cCI6MTQ5MjEwMTYwMywianRpIjoiMTRKTXF4SjNTNU1OU0NWZ3NubVplWiIsInN1YiI6InRlc3QiLCJzY29wZSI6W119.tXlQa3RI27-vj34y2khhItkA2rVz5ooDzC3CUDol0SI",
    "expires": 1492101603
}

@manelj
Copy link
Author

manelj commented Apr 13, 2017

Ok. Now it works. I am sending body like this { "data" : ["tareas.all"] } and it's wrong, it has to be only the array ["tareas.all"]

Can i ask you what does exactly this piece from middleware.php ?

$container["Negotiation"] = function ($container) { return new NegotiationMiddleware([ "accept" => ["application/json"] ]); };

@tuupola
Copy link
Owner

tuupola commented Apr 14, 2017

It adds the content negotiation middleware. The README contains quite good explanation what the middleware does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants