From 6130626422172cbeedb60de5f71d28dfc41b7f4f Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 Nov 2015 20:43:22 +0200 Subject: [PATCH] Working on the API --- app/Http/Controllers/AccountApiController.php | 15 +++++++- app/Http/Controllers/Auth/AuthController.php | 7 ---- app/Http/Controllers/DashboardController.php | 1 + app/Http/Controllers/PaymentController.php | 2 +- app/Http/Middleware/ApiCheck.php | 36 ++++++++++--------- app/Http/Middleware/VerifyCsrfToken.php | 7 +--- app/Http/routes.php | 3 +- app/Ninja/Repositories/AccountRepository.php | 8 ++++- readme.md | 8 ++--- 9 files changed, 50 insertions(+), 37 deletions(-) diff --git a/app/Http/Controllers/AccountApiController.php b/app/Http/Controllers/AccountApiController.php index a2154ac53fe2..65f4eff634c6 100644 --- a/app/Http/Controllers/AccountApiController.php +++ b/app/Http/Controllers/AccountApiController.php @@ -7,7 +7,7 @@ use Input; use App\Models\Client; use App\Models\Account; use App\Ninja\Repositories\AccountRepository; - +use Illuminate\Http\Request; use League\Fractal; use League\Fractal\Resource\Item; use League\Fractal\Manager; @@ -23,6 +23,19 @@ class AccountApiController extends Controller $this->accountRepo = $accountRepo; } + public function login(Request $request) + { + if ( ! env(API_SECRET) || $request->api_secret !== env(API_SECRET)) { + return 'Invalid secret'; + } + + if (Auth::attempt(['email' => $request->email, 'password' => $request->password])) { + return $this->accountRepo->createToken($request->token_name); + } else { + return 'Invalid credentials'; + } + } + public function index() { $manager = new Manager(); diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 14ef9cf8b446..a65f6153037b 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -98,13 +98,6 @@ class AuthController extends Controller { $users = $this->accountRepo->loadAccounts(Auth::user()->id); } Session::put(SESSION_USER_ACCOUNTS, $users); - - if ($request->create_token) { - if ( ! env(API_SECRET) || $request->api_secret !== env(API_SECRET)) { - return 'Invalid secret'; - } - return $this->accountRepo->createToken($request->token_name); - } } elseif ($user) { $user->failed_logins = $user->failed_logins + 1; $user->save(); diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index a8b31fdb8ab0..6dbc8ef61053 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -109,6 +109,7 @@ class DashboardController extends BaseController ->leftJoin('contacts', 'contacts.client_id', '=', 'clients.id') ->leftJoin('invoices', 'invoices.id', '=', 'payments.invoice_id') ->where('payments.account_id', '=', Auth::user()->account_id) + ->where('payments.deleted_at', '=', null) ->where('clients.deleted_at', '=', null) ->where('contacts.deleted_at', '=', null) ->where('contacts.is_primary', '=', true) diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index 54543ca4fb48..5ec8d56ddb9a 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -528,7 +528,7 @@ class PaymentController extends BaseController if (method_exists($gateway, 'completePurchase') && !$accountGateway->isGateway(GATEWAY_TWO_CHECKOUT)) { $details = $this->paymentService->getPaymentDetails($invitation, $accountGateway); $response = $gateway->completePurchase($details)->send(); - $ref = $response->getTransactionReference(); + $ref = $response->getTransactionReference() ?: $token; if ($response->isSuccessful()) { $payment = $this->paymentService->createPayment($invitation, $ref, $payerId); diff --git a/app/Http/Middleware/ApiCheck.php b/app/Http/Middleware/ApiCheck.php index 517dc905eb16..9323b4605645 100644 --- a/app/Http/Middleware/ApiCheck.php +++ b/app/Http/Middleware/ApiCheck.php @@ -21,33 +21,38 @@ class ApiCheck { */ public function handle($request, Closure $next) { + $loggingIn = $request->is('api/v1/login'); $headers = Utils::getApiHeaders(); - // check for a valid token - $token = AccountToken::where('token', '=', Request::header('X-Ninja-Token'))->first(['id', 'user_id']); - - if ($token) { - Auth::loginUsingId($token->user_id); - Session::set('token_id', $token->id); + if ($loggingIn) { + // do nothing } else { - sleep(3); - return Response::make('Invalid token', 403, $headers); + // check for a valid token + $token = AccountToken::where('token', '=', Request::header('X-Ninja-Token'))->first(['id', 'user_id']); + + if ($token) { + Auth::loginUsingId($token->user_id); + Session::set('token_id', $token->id); + } else { + sleep(3); + return Response::make('Invalid token', 403, $headers); + } } - if (!Utils::isNinja()) { + if (!Utils::isNinja() && !$loggingIn) { return $next($request); } - if (!Utils::isPro()) { + if (!Utils::isPro() && !$loggingIn) { return Response::make('API requires pro plan', 403, $headers); } else { - $accountId = Auth::user()->account->id; + $key = Auth::check() ? Auth::user()->account->id : $request->getClientIp(); // http://stackoverflow.com/questions/1375501/how-do-i-throttle-my-sites-api-users $hour = 60 * 60; $hour_limit = 100; # users are limited to 100 requests/hour - $hour_throttle = Cache::get("hour_throttle:{$accountId}", null); - $last_api_request = Cache::get("last_api_request:{$accountId}", 0); + $hour_throttle = Cache::get("hour_throttle:{$key}", null); + $last_api_request = Cache::get("last_api_request:{$key}", 0); $last_api_diff = time() - $last_api_request; if (is_null($hour_throttle)) { @@ -66,11 +71,10 @@ class ApiCheck { return Response::make("Please wait {$wait} second(s)", 403, $headers); } - Cache::put("hour_throttle:{$accountId}", $new_hour_throttle, 10); - Cache::put("last_api_request:{$accountId}", time(), 10); + Cache::put("hour_throttle:{$key}", $new_hour_throttle, 10); + Cache::put("last_api_request:{$key}", time(), 10); } - return $next($request); } diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 6af49e9b273c..5b3a74dd2108 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -7,6 +7,7 @@ class VerifyCsrfToken extends BaseVerifier { private $openRoutes = [ 'signup/register', + 'api/v1/login', 'api/v1/clients', 'api/v1/invoices', 'api/v1/quotes', @@ -34,12 +35,6 @@ class VerifyCsrfToken extends BaseVerifier { } } - if ($request->is('login')) { - if (env(API_SECRET) && $request->api_secret === env(API_SECRET)) { - return $next($request); - } - } - return parent::handle($request, $next); } diff --git a/app/Http/routes.php b/app/Http/routes.php index 859fbaa6be0a..125f858691fc 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -186,10 +186,11 @@ Route::group(['middleware' => 'auth'], function() { get('/resend_confirmation', 'AccountController@resendConfirmation'); }); -// Route group for API +// Route groups for API Route::group(['middleware' => 'api', 'prefix' => 'api/v1'], function() { Route::resource('ping', 'ClientApiController@ping'); + Route::post('login', 'AccountApiController@login'); Route::get('accounts', 'AccountApiController@index'); Route::resource('clients', 'ClientApiController'); Route::get('quotes/{client_id?}', 'QuoteApiController@index'); diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index 2441330b9a5f..c431584b888a 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -459,8 +459,14 @@ class AccountRepository public function createToken($name) { + $name = trim($name) ?: 'TOKEN'; + + if ($token = AccountToken::scope()->whereName($name)->first()) { + return $token->token; + } + $token = AccountToken::createNew(); - $token->name = trim($name) ?: 'TOKEN'; + $token->name = $name; $token->token = str_random(RANDOM_KEY_LENGTH); $token->save(); diff --git a/readme.md b/readme.md index 48c25a422d74..e43083c3dd60 100644 --- a/readme.md +++ b/readme.md @@ -39,15 +39,15 @@ If you'd like to use our code to sell your own invoicing app email us for detail * [Support Forum](https://www.invoiceninja.com/forums/forum/support/) * [Feature Roadmap](https://trello.com/b/63BbiVVe/) -### Recommended Providers -* [Stripe](https://stripe.com/) -* [Postmark](https://postmarkapp.com/) - ### Contributors * [Troels Liebe Bentsen](https://github.com/tlbdk) * [Jeramy Simpson](https://github.com/JeramyMywork) - [MyWork](https://www.mywork.com.au) * [Sigitas Limontas](https://lt.linkedin.com/in/sigitaslimontas) +### Recommended Providers +* [Stripe](https://stripe.com/) +* [Postmark](https://postmarkapp.com/) + ### Frameworks/Libraries * [laravel/laravel](https://github.com/laravel/laravel) - A PHP Framework For Web Artisans * [twbs/bootstrap](https://github.com/twbs/bootstrap) - Sleek, intuitive, and powerful front-end framework for faster and easier web development.