I currently have two tables namely storing users and clients. A client is related to a User in a one-to-one relationship.
I am currently storing and updating the models like so but it feels rather clunky...
public function store(Request $request)
{
$requestData = $request->all();
$user = new User();
$user->fill($requestData);
$user->save();
$client = new Client;
$client->fill($requestData);
$client->user()->associate($user);
$client->save();
return response()->json($client->toArray(), 201, ['id' => $client->id]);
}
public function update(Request $request, $id)
{
try {
$client = Client::findOrFail($id);
$user = User::findOrFail($client->fk_user);
} catch (ModelNotFoundException $e) {
return response()->json([
'error' => [
'message' => 'Client not found',
]
], 404);
}
$requestData = $request->all();
$user->fill($requestData);
$user->save();
$client->fill($requestData);
$client->user()->associate($user);
$client->save();
return response()->json($client->toArray(), 200);
}
Is there a way to refactor this to avoid having to work with both users and clients as separate models. Is there a way to perhaps fill a client and in turn fill the parent user model?
Just wondering, thanks!
I´ve made some refactoring inspiration for you. This is not necessarily the "right" way, but maybe you can pick up something you like.
Note! I haven't actually tested the code, its probably full of syntax errors. I just hacked it down to show some ideas. Some of the logic, like associating the user, I've imaginary placed in the client model. The error handling happens in app/Exceptions/Handler.php.
Hope you can have some use of it :)
private function saveUser($args, $user = null) {
$user = $user ?: new User();
$user->fill($args);
$user->save();
return $user;
}
private function saveClient($args, $client= null) {
$client = $client ?: new Client;
$client->fill($args);
$client->save();
return $client;
}
private function respondCreated($data = []) {
return $this->respond($data, 201, "created");
}
private function respond($data = [], $statusCode = 200, $message = "ok") {
$response = [
"message" => $message,
"data" => $data
];
return response()->json($response, $statusCode);
}
public function store(Request $request)
{
$user = $this->saveUser($request->all());
$client = $this->saveClient($request->all());
$client->saveUser($user);
return $this->respondCreated($client);
}
public function update(Request $request, $id)
{
$client = $this->saveClient($request->all(), $client::findOrFail($id));
$this->saveUser($request->all(), $client->fk_user);
return $this->respond($client);
}
Related
How to pass a variable($test) from store to index? because I would like to display a variable in my index.blade
public function index()
{
return view('users.index', [
'users' => User::all()
]);
}
public function store(Request $request)
{
$user = new User($request->all());
$user->save();
$test = "test";
return redirect('users');
}
To resolve your problem you may edit your code like below:
index function:
public function index($test=null)
{
return view('users.index', [
'users' => User::all(),
'test' => $test
]);
}
store function:
public function store(Request $request)
{
$user = new User($request->all());
$user->save();
$test = "test";
return redirect(route('users.index', compact('test')));
}
N.B: for storing your user I don't recommend to you mass assignment (new User($request->all())) when you create a new user especially if you have a password or token to store there.
I've set an OIDC authentication with a custom authenticator as follow:
class SsoAuthenticator implements AuthenticatorInterface, AuthenticationEntryPointInterface
{
private UserProviderInterface $user_provider;
private ?LoggerInterface $logger;
public function __construct(LdapUserRepository $ldap_user_repository, UserProviderInterface $user_provider, LoggerInterface $logger = null)
{
$this->user_provider = $user_provider;
$this->logger = $logger;
$this->ldap_user_repository = $ldap_user_repository;
}
public function start(Request $request, AuthenticationException $authException = null): Response
{
$response = new Response();
$response->headers->set('WWW-Authenticate', sprintf('Basic realm="%s"', 'emmaus.example.com'));
$response->setStatusCode(401);
return $response;
}
public function supports(Request $request): ?bool
{
return $request->headers->has('Authorization') || $request->get('token');
}
public function authenticate(Request $request): Passport
{
$oidc = new Oidc($this->ldap_user_repository);
$token = $request->get('token');
$decoded_token = $oidc->decodeToken($token);
$user_name = $oidc = $oidc->getUserName($decoded_token);
if(!(is_a($this->user_provider, UserProviderInterface::class)))
{
throw new AuthenticationException('error forbidden buddy');
}
$user_badge = new UserBadge($user_name);
$credentials = new BadgeCredentials();
return new Passport($user_badge, $credentials);
}
public function createToken(Passport $passport, string $firewallName): TokenInterface
{
return new UsernamePasswordToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles());
}
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
{
return new OidcToken(['ROLE_USER'], null);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$oidc = new Oidc($this->ldap_user_repository);
$token = $request->get('token') ? $request->get('token') : $request->get('Authorization');
$decoded_token = $oidc->decodeToken($token);
$user_identifier = $oidc->getUserName($decoded_token);
$user = $this->ldap_user_repository->findOneBy(['username' => $user_identifier]);
$last_name = $user->getLastName();
$first_name = $user->getFirstName();
$roles = $user->getRoles();
$email = $user->getEmail();
$group_results = array();
$groups = $user->getGroupBase();
foreach($groups as $group)
{
array_push($group_results, $group->getName());
}
$token = $request->get('token') ? $request->get('token') : $request->headers->has('Authorization');
$_SESSION['token_lemon_ldap'] = $token;
$data = array(
'roles' => $roles,
'userName' => $user_identifier,
'firstName' => $first_name,
'lastName' => $last_name,
'email' => $email,
'token' => $token,
'groups' => $group_results
);
return new Response(
json_encode($data)
);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$oidc = new Oidc($this->ldap_user_repository);
$decoded_token = $oidc->decodeToken($request->get('token'));
try
{
return $this->start($request, $exception);
}catch(UnexpectedValueException $exception)
{
throw new $exception('wrong number of segment');
}
}
}
?>
I've the authorization to access resources from API after successful authentication, but when I'm fetching response from controller, it return onAutenticationSucess() response's data at each request, and can't access data from controllers, do you have an idea what i'm missing? I'm looking at session or kernel.response, but can't make my head around a proper solution.
Thanks
I upgraded:
"tymon/jwt-auth": "0.5.*",
from a very old version, and it seems like the API has changed. I managed to fix the login, using:
public function login(Request $request)
{
$credentials = $request->only(['username', 'password']);
$validator = Validator::make($credentials, [
'username' => 'required',
'password' => 'required',
]);
if($validator->fails()) {
throw new ValidationHttpException($validator->errors()->all());
}
if (!$token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
$user = auth()->user();
$user->ip_address = $request->ip();
if ($request->device_token)
$user->device_token = $request->device_token;
$user->save();
$data['token'] = $token;
$data['email'] = $user->email;
return response()->json($data);
}
So my login work, but all API's that required the token - fail now.
Example of API that fail:
class UserController extends Controller
{
public function __construct()
{
// $this->middleware('auth:api', ['except' => ['login']]);
}
public function enterWorld(Request $request)
{
$token = $request->input('token');
$user = JWTAuth::toUser($token);
return $user;
}
Any idea how to convert the token from the request to the user with the new API?
I couldn't find any docs about it.
I tried:
return response()->json(auth()->user());
but in this API it return empty array. only in login it works.
Try the following:
$user = JWTAuth::setRequest($request)->user();
You may also explicitly set the guard when using the following syntax:
// pass the guard in to the auth() helper.
return response()->json(auth('jwt')->user());
I have a front-end SPA built on Angular 6 and back-end on Laravel 5.6. I'm trying to make a facebook auth using ngx-social-login on the front-end and a Socialite on the back-end.
That is code in my component
signInWithFacebook(): void {
this.sas.signIn(FacebookLoginProvider.PROVIDER_ID).then(
userData => this.as.fb(userData.authToken).subscribe(x => {
console.log(x);
})
);
}
And this is a service
fb(data): Observable<any> {
return this.http.get(this.API_URL, data);
}
And here is my Laravel routes
$api->version('v1', function ($api) {
$api->get('auth/facebook', 'SocialAuthFacebookController#redirectToProvider');
$api->get('auth/facebook/callback', 'SocialAuthFacebookController#callback');
});
That is a controller
public function redirectToProvider()
{
return Socialite::driver('facebook')->stateless()->redirect();
}
public function callback()
{
$user = Socialite::driver('facebook')->stateless()->user();
$authUser = $this->findOrCreateUser($user, 'facebook');
$token = JWTAuth::fromUser($authUser);
return Response::json(compact('token'));
}
public function findOrCreateUser($user, $provider)
{
$authUser = User::where('provider_id', $user->id)->first();
if ($authUser) {
return $authUser;
}
return User::create([
'name' => $user->name,
'email' => $user->email,
'provider' => $provider,
'provider_id' => $user->id
]);
}
Since I'm using Laravel as an API-only so I suppose that I cannot access redirectToProvider so that I tried to call auth/facebook/callback and pass it an authToken that I get after a login on my SPA. However, it doesn't seem to work.
I'm experiencing the next error
Thanks to Facebook there is so much information so that I don't know what's wrong and what to do with it.
Here's an example that might help:
/**
* Redirect the user to the Facebook authentication page.
*
* #return Response
*/
public function redirectToProvider()
{
return Socialite::driver('facebook')->stateless()->redirect();
}
/**
* Obtain the user information from Facebook.
*
* #return JsonResponse
*/
public function handleProviderCallback()
{
$providerUser = Socialite::driver('facebook')->stateless()->user();
$user = User::query()->firstOrNew(['email' => $providerUser->getEmail()]);
if (!$user->exists) {
$user->name = $providerUser->getName();
$user->save();
}
$token = JWTAuth::fromUser($user);
return new JsonResponse([
'token' => $token
]);
}
Hi can someone help me to prevent bjyauthorize to catch my api event error raised?
bjyauthorize redirect non logged user to login form as added to config. But since my api are allowed for all roles even for guest i just want it to return Json error message catched by ApiProblemListener
ApplicationRest\Module.php
class Module implements
ConfigProviderInterface,
AutoloaderProviderInterface
{
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$sm = $app->getServiceManager();
$events = $app->getEventManager();
$listener = $sm->get('ApplicationRest\ApiAuthenticationListener');
$events->getSharedManager()->attach('ApplicationRest\Controller', 'dispatch', $listener, 500);
$events->attach('render', array($this, 'onRender'), 100);
$events->attach($sm->get('ApplicationRest\ApiProblemListener'));
}
/**
* Listener for the render event
* Attaches a rendering/response strategy to the View.
*
* #param \Zend\Mvc\MvcEvent $e
*/
public function onRender($e)
{
$result = $e->getResult();
if (!$result instanceof RestfulJsonModel) {
return;
}
//var_dump(123);exit();
$app = $e->getTarget();
$services = $app->getServiceManager();
$view = $services->get('View');
$restfulJsonStrategy = $services->get('ApplicationRest\RestfulJsonStrategy');
$events = $view->getEventManager();
// register at high priority, to "beat" normal json strategy registered
// via view manager
$events->attach($restfulJsonStrategy, 500);
}
}
Have many modules and i am really thinking to move away my apiModule "ApplicationRest" to another project but don't really want to update model and service each time i make some updates on main project.
Any suggestions would welcome!
Thanks for your time!
EDIT: Provided more HeaderAuthentication class
class HeaderAuthentication implements AdapterInterface
{
const AUTHORIZATION_HEADER = 'Authorization';
const CRYPTO = 'sha256';
protected $request;
protected $repository;
public function __construct(RequestInterface $request, UserRepository $repository)
{
$this->request = $request;
$this->repository = $repository;
}
/**
* Authorization: Key={key} Timestamp={timestamp} Signature={signature}
* #return Result
*/
public function authenticate()
{
$request = $this->getRequest();
if (!$request instanceof Request) {
return;
}
$headers = $request->getHeaders();
// Check Authorization header presence
if (!$headers->has(static::AUTHORIZATION_HEADER)) {
return new Result(Result::FAILURE, null, array(
'Authorization header missing'
));
}
$authorization = $headers->get(static::AUTHORIZATION_HEADER)->getFieldValue();
// Validate public key
$publicKey = $this->extractPublicKey($authorization);
$user = $this->getUserRepository()
->findOneByApiSecret($publicKey);
if (null === $user) {
$code = Result::FAILURE_IDENTITY_NOT_FOUND;
return new Result($code, null, array(
'User not found based on public key'
));
}
// Validate signature
$signature = $this->extractSignature($authorization);
/*$hmac = $this->getHmac($request, $user);
if ($signature !== $hmac) {
$code = Result::FAILURE_CREDENTIAL_INVALID;
return new Result($code, null, array(
'Signature does not match'
));
}*/
return new Result(Result::SUCCESS, $user);
}
}
ApiAuthenticationListener
class ApiAuthenticationListener
{
protected $adapter;
public function __construct(HeaderAuthentication $adapter)
{
$this->adapter = $adapter;
}
public function __invoke(MvcEvent $event)
{
$result = $this->adapter->authenticate();
if (!$result->isValid()) {
$response = $event->getResponse();
// Set some response content
$response->setStatusCode(401);
return $response;
}
// All is OK
$event->setParam('user', $result->getIdentity());
}
}
I'm guessing you configured guards on your route. You need to tell BJYAuthorize, through your module config, that this controller or route shouldn't be protected.
'bjyauthorize' => [
'default_role' => 'guest',
...
'guards' => [
'BjyAuthorize\Guard\Controller' => [
// system tools
['controller' => 'Application\Controller\Api', 'roles' => [] ],
['controller' => 'error', 'roles' => []],
],
],
],
I cut out the nitty gritty that's app specific, but this type of thing is quickly solved. I had a similar need for CLI routes to be unprotected by what is otherwise, http auth.