Changeset View
Changeset View
Standalone View
Standalone View
src/app/Http/Controllers/API/V4/UsersController.php
- This file was moved from src/app/Http/Controllers/API/UsersController.php.
<?php | <?php | ||||
namespace App\Http\Controllers\API; | namespace App\Http\Controllers\API\V4; | ||||
use App\Http\Controllers\Controller; | use App\Http\Controllers\Controller; | ||||
use App\Domain; | use App\Domain; | ||||
use App\Rules\UserEmailDomain; | use App\Rules\UserEmailDomain; | ||||
use App\Rules\UserEmailLocal; | use App\Rules\UserEmailLocal; | ||||
use App\Sku; | use App\Sku; | ||||
use App\User; | use App\User; | ||||
use Illuminate\Http\Request; | use Illuminate\Http\Request; | ||||
use Illuminate\Support\Facades\Auth; | use Illuminate\Support\Facades\Auth; | ||||
use Illuminate\Support\Facades\DB; | use Illuminate\Support\Facades\DB; | ||||
use Illuminate\Support\Facades\Validator; | use Illuminate\Support\Facades\Validator; | ||||
use Illuminate\Support\Str; | use Illuminate\Support\Str; | ||||
class UsersController extends Controller | class UsersController extends Controller | ||||
{ | { | ||||
/** | /** | ||||
* Create a new API\UsersController instance. | |||||
* | |||||
* Ensures that the correct authentication middleware is applied except for /login | |||||
* | |||||
* @return void | |||||
*/ | |||||
public function __construct() | |||||
{ | |||||
$this->middleware('auth:api', ['except' => ['login']]); | |||||
} | |||||
/** | |||||
* Helper method for other controllers with user auto-logon | |||||
* functionality | |||||
* | |||||
* @param \App\User $user User model object | |||||
*/ | |||||
public static function logonResponse(User $user) | |||||
{ | |||||
$token = auth()->login($user); | |||||
return response()->json([ | |||||
'status' => 'success', | |||||
'access_token' => $token, | |||||
'token_type' => 'bearer', | |||||
'expires_in' => Auth::guard()->factory()->getTTL() * 60, | |||||
]); | |||||
} | |||||
/** | |||||
* Delete a user. | * Delete a user. | ||||
* | * | ||||
* @param int $id User identifier | * @param int $id User identifier | ||||
* | * | ||||
* @return \Illuminate\Http\JsonResponse The response | * @return \Illuminate\Http\JsonResponse The response | ||||
*/ | */ | ||||
public function destroy($id) | public function destroy($id) | ||||
{ | { | ||||
Show All 20 Lines | class UsersController extends Controller | ||||
* Listing of users. | * Listing of users. | ||||
* | * | ||||
* The user-entitlements billed to the current user wallet(s) | * The user-entitlements billed to the current user wallet(s) | ||||
* | * | ||||
* @return \Illuminate\Http\JsonResponse | * @return \Illuminate\Http\JsonResponse | ||||
*/ | */ | ||||
public function index() | public function index() | ||||
{ | { | ||||
\Log::debug("Regular API"); | |||||
$user = $this->guard()->user(); | $user = $this->guard()->user(); | ||||
$result = $user->users()->orderBy('email')->get()->map(function ($user) { | $result = $user->users()->orderBy('email')->get()->map(function ($user) { | ||||
$data = $user->toArray(); | $data = $user->toArray(); | ||||
$data = array_merge($data, self::userStatuses($user)); | $data = array_merge($data, self::userStatuses($user)); | ||||
return $data; | return $data; | ||||
}); | }); | ||||
return response()->json($result); | return response()->json($result); | ||||
} | } | ||||
/** | /** | ||||
* Get the authenticated User | |||||
* | |||||
* @return \Illuminate\Http\JsonResponse | |||||
*/ | |||||
public function info() | |||||
{ | |||||
$user = $this->guard()->user(); | |||||
$response = $this->userResponse($user); | |||||
return response()->json($response); | |||||
} | |||||
/** | |||||
* Get a JWT token via given credentials. | |||||
* | |||||
* @param \Illuminate\Http\Request $request The API request. | |||||
* | |||||
* @return \Illuminate\Http\JsonResponse | |||||
*/ | |||||
public function login(Request $request) | |||||
{ | |||||
$v = Validator::make( | |||||
$request->all(), | |||||
[ | |||||
'email' => 'required|min:2', | |||||
'password' => 'required|min:4', | |||||
] | |||||
); | |||||
if ($v->fails()) { | |||||
return response()->json(['status' => 'error', 'errors' => $v->errors()], 422); | |||||
} | |||||
$credentials = $request->only('email', 'password'); | |||||
if ($token = $this->guard()->attempt($credentials)) { | |||||
return $this->respondWithToken($token); | |||||
} | |||||
return response()->json(['status' => 'error', 'message' => __('auth.failed')], 401); | |||||
} | |||||
/** | |||||
* Log the user out (Invalidate the token) | |||||
* | |||||
* @return \Illuminate\Http\JsonResponse | |||||
*/ | |||||
public function logout() | |||||
{ | |||||
$this->guard()->logout(); | |||||
return response()->json([ | |||||
'status' => 'success', | |||||
'message' => __('auth.logoutsuccess') | |||||
]); | |||||
} | |||||
/** | |||||
* Refresh a token. | |||||
* | |||||
* @return \Illuminate\Http\JsonResponse | |||||
*/ | |||||
public function refresh() | |||||
{ | |||||
return $this->respondWithToken($this->guard()->refresh()); | |||||
} | |||||
/** | |||||
* Get the token array structure. | |||||
* | |||||
* @param string $token Respond with this token. | |||||
* | |||||
* @return \Illuminate\Http\JsonResponse | |||||
*/ | |||||
protected function respondWithToken($token) | |||||
{ | |||||
return response()->json( | |||||
[ | |||||
'access_token' => $token, | |||||
'token_type' => 'bearer', | |||||
'expires_in' => $this->guard()->factory()->getTTL() * 60 | |||||
] | |||||
); | |||||
} | |||||
/** | |||||
* Display information on the user account specified by $id. | * Display information on the user account specified by $id. | ||||
* | * | ||||
* @param int $id The account to show information for. | * @param int $id The account to show information for. | ||||
* | * | ||||
* @return \Illuminate\Http\JsonResponse | * @return \Illuminate\Http\JsonResponse | ||||
*/ | */ | ||||
public function show($id) | public function show($id) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 255 Lines • ▼ Show 20 Lines | // 'cost' => $ent->cost, | ||||
/** | /** | ||||
* Create a response data array for specified user. | * Create a response data array for specified user. | ||||
* | * | ||||
* @param \App\User $user User object | * @param \App\User $user User object | ||||
* | * | ||||
* @return array Response data | * @return array Response data | ||||
*/ | */ | ||||
protected function userResponse(User $user): array | public static function userResponse(User $user): array | ||||
{ | { | ||||
$response = $user->toArray(); | $response = $user->toArray(); | ||||
// Settings | // Settings | ||||
// TODO: It might be reasonable to limit the list of settings here to these | // TODO: It might be reasonable to limit the list of settings here to these | ||||
// that are safe and are used in the UI | // that are safe and are used in the UI | ||||
$response['settings'] = []; | $response['settings'] = []; | ||||
foreach ($user->settings as $item) { | foreach ($user->settings as $item) { | ||||
$response['settings'][$item->key] = $item->value; | $response['settings'][$item->key] = $item->value; | ||||
} | } | ||||
// Aliases | // Aliases | ||||
$response['aliases'] = []; | $response['aliases'] = []; | ||||
foreach ($user->aliases as $item) { | foreach ($user->aliases as $item) { | ||||
$response['aliases'][] = $item->alias; | $response['aliases'][] = $item->alias; | ||||
} | } | ||||
// Status info | // Status info | ||||
$response['statusInfo'] = self::statusInfo($user); | $response['statusInfo'] = self::statusInfo($user); | ||||
$response = array_merge($response, self::userStatuses($user)); | $response = array_merge($response, self::userStatuses($user)); | ||||
// Add discount info to wallet object output | |||||
$map_func = function ($wallet) { | |||||
$result = $wallet->toArray(); | |||||
if ($wallet->discount) { | |||||
$result['discount'] = $wallet->discount->discount; | |||||
$result['discount_description'] = $wallet->discount->description; | |||||
} | |||||
return $result; | |||||
}; | |||||
// Information about wallets and accounts for access checks | // Information about wallets and accounts for access checks | ||||
$response['wallets'] = $user->wallets->toArray(); | $response['wallets'] = $user->wallets->map($map_func)->toArray(); | ||||
$response['accounts'] = $user->accounts->toArray(); | $response['accounts'] = $user->accounts->map($map_func)->toArray(); | ||||
$response['wallet'] = $user->wallet()->toArray(); | $response['wallet'] = $map_func($user->wallet()); | ||||
return $response; | return $response; | ||||
} | } | ||||
/** | /** | ||||
* Prepare user statuses for the UI | * Prepare user statuses for the UI | ||||
* | * | ||||
* @param \App\User $user User object | * @param \App\User $user User object | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | protected function validateUserRequest(Request $request, $user, &$settings = []) | ||||
$controller = $user ? $user->wallet()->owner : $this->guard()->user(); | $controller = $user ? $user->wallet()->owner : $this->guard()->user(); | ||||
// For new user validate email address | // For new user validate email address | ||||
if (empty($user)) { | if (empty($user)) { | ||||
$email = $request->email; | $email = $request->email; | ||||
if (empty($email)) { | if (empty($email)) { | ||||
$errors['email'] = \trans('validation.required', ['attribute' => 'email']); | $errors['email'] = \trans('validation.required', ['attribute' => 'email']); | ||||
} elseif ($error = self::validateEmail($email, $controller, false)) { | } elseif ($error = \App\Utils::validateEmail($email, $controller, false)) { | ||||
$errors['email'] = $error; | $errors['email'] = $error; | ||||
} | } | ||||
} | } | ||||
// Validate aliases input | // Validate aliases input | ||||
if (isset($request->aliases)) { | if (isset($request->aliases)) { | ||||
$aliases = []; | $aliases = []; | ||||
$existing_aliases = $user ? $user->aliases()->get()->pluck('alias')->toArray() : []; | $existing_aliases = $user ? $user->aliases()->get()->pluck('alias')->toArray() : []; | ||||
foreach ($request->aliases as $idx => $alias) { | foreach ($request->aliases as $idx => $alias) { | ||||
if (is_string($alias) && !empty($alias)) { | if (is_string($alias) && !empty($alias)) { | ||||
// Alias cannot be the same as the email address (new user) | // Alias cannot be the same as the email address (new user) | ||||
if (!empty($email) && Str::lower($alias) == Str::lower($email)) { | if (!empty($email) && Str::lower($alias) == Str::lower($email)) { | ||||
continue; | continue; | ||||
} | } | ||||
// validate new aliases | // validate new aliases | ||||
if ( | if ( | ||||
!in_array($alias, $existing_aliases) | !in_array($alias, $existing_aliases) | ||||
&& ($error = self::validateEmail($alias, $controller, true)) | && ($error = \App\Utils::validateEmail($alias, $controller, true)) | ||||
) { | ) { | ||||
if (!isset($errors['aliases'])) { | if (!isset($errors['aliases'])) { | ||||
$errors['aliases'] = []; | $errors['aliases'] = []; | ||||
} | } | ||||
$errors['aliases'][$idx] = $error; | $errors['aliases'][$idx] = $error; | ||||
continue; | continue; | ||||
} | } | ||||
$aliases[] = $alias; | $aliases[] = $alias; | ||||
} | } | ||||
} | } | ||||
$request->aliases = $aliases; | $request->aliases = $aliases; | ||||
} | } | ||||
if (!empty($errors)) { | if (!empty($errors)) { | ||||
return response()->json(['status' => 'error', 'errors' => $errors], 422); | return response()->json(['status' => 'error', 'errors' => $errors], 422); | ||||
} | } | ||||
// Update user settings | // Update user settings | ||||
$settings = $request->only(array_keys($rules)); | $settings = $request->only(array_keys($rules)); | ||||
unset($settings['password'], $settings['aliases'], $settings['email']); | unset($settings['password'], $settings['aliases'], $settings['email']); | ||||
} | } | ||||
/** | |||||
* Email address (login or alias) validation | |||||
* | |||||
* @param string $email Email address | |||||
* @param \App\User $user The account owner | |||||
* @param bool $is_alias The email is an alias | |||||
* | |||||
* @return string Error message on validation error | |||||
*/ | |||||
protected static function validateEmail(string $email, User $user, bool $is_alias = false): ?string | |||||
{ | |||||
$attribute = $is_alias ? 'alias' : 'email'; | |||||
if (strpos($email, '@') === false) { | |||||
return \trans('validation.entryinvalid', ['attribute' => $attribute]); | |||||
} | |||||
list($login, $domain) = explode('@', $email); | |||||
// Check if domain exists | |||||
$domain = Domain::where('namespace', Str::lower($domain))->first(); | |||||
if (empty($domain)) { | |||||
return \trans('validation.domaininvalid'); | |||||
} | |||||
// Validate login part alone | |||||
$v = Validator::make( | |||||
[$attribute => $login], | |||||
[$attribute => ['required', new UserEmailLocal(!$domain->isPublic())]] | |||||
); | |||||
if ($v->fails()) { | |||||
return $v->errors()->toArray()[$attribute][0]; | |||||
} | |||||
// Check if it is one of domains available to the user | |||||
// TODO: We should have a helper that returns "flat" array with domain names | |||||
// I guess we could use pluck() somehow | |||||
$domains = array_map( | |||||
function ($domain) { | |||||
return $domain->namespace; | |||||
}, | |||||
$user->domains() | |||||
); | |||||
if (!in_array($domain->namespace, $domains)) { | |||||
return \trans('validation.entryexists', ['attribute' => 'domain']); | |||||
} | |||||
// Check if user with specified address already exists | |||||
if (User::findByEmail($email)) { | |||||
return \trans('validation.entryexists', ['attribute' => $attribute]); | |||||
} | |||||
return null; | |||||
} | |||||
} | } |