Skip to content

Commit fd6db56

Browse files
authored
Merge pull request #8 from atomx/no-auth-requests
No token for public resources, move login to a TokenStore
2 parents 110172e + 5c51973 commit fd6db56

21 files changed

+415
-68
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
.idea/
22
vendor/
33
atomx.php
4-
Atomx/MemoryAccountStore.php
54
tests/AtomxAccountStore.php
65

Atomx/ApiClient.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<?php namespace Atomx;
22

3+
use Atomx\Exceptions\ApiException;
34
use GuzzleHttp\Client;
45
use GuzzleHttp\Exception\RequestException;
56
use GuzzleHttp\Message\Response;
7+
use GuzzleHttp\Stream\StreamInterface;
68

79
class ApiClient {
810
protected $endpoint;
@@ -20,6 +22,11 @@ function __construct()
2022
$this->client = new Client(['base_url' => $this->apiBase]);
2123
}
2224

25+
public function getClient()
26+
{
27+
return $this->client;
28+
}
29+
2330
public function clearFields()
2431
{
2532
$this->fields = [];
@@ -58,6 +65,12 @@ public function post($fields = [])
5865
return $this->postUrl($this->endpoint, ['json' => $fields]);
5966
}
6067

68+
/**
69+
* @param $url
70+
* @param array $options
71+
* @return string|StreamInterface
72+
* @throws ApiException
73+
*/
6174
public function postUrl($url, $options = [])
6275
{
6376
return $this->request('post', $url, $options);
@@ -127,7 +140,6 @@ public function __get($name)
127140

128141
public function fill($fields)
129142
{
130-
// TODO: Merge with the other fields
131143
$this->fields = $fields;
132144
}
133145

Atomx/ApiException.php

Lines changed: 0 additions & 5 deletions
This file was deleted.

Atomx/AtomxClient.php

Lines changed: 35 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,39 @@
11
<?php namespace Atomx;
22

3+
use Atomx\Exceptions\ApiException;
34
use Exception;
45
use GuzzleHttp\Message\Response;
5-
use GuzzleHttp\Stream\Stream;
66

77
class AtomxClient extends ApiClient {
8+
const API_BASE = 'https://api.atomx.com/v3/';
89
protected $apiBase = null;
910
protected $id = null;
11+
protected $requiresToken = true;
1012

1113
/**
12-
* @var AccountStore Store the token for the application
14+
* @var TokenStore Store the token for the application
1315
*/
14-
private $accountStore;
15-
private $shouldSendToken = true;
16+
protected $accountStore = null;
1617

17-
function __construct(AccountStore $accountStore, $idOrFields = null)
18+
/**
19+
* AtomxClient constructor.
20+
* @param TokenStore|null $accountStore
21+
* @param int|array $idOrFields
22+
* @param string $apiBase
23+
*/
24+
function __construct($accountStore = null, $idOrFields = null)
1825
{
19-
$this->apiBase = $accountStore->getApiBase();
26+
if ($accountStore) {
27+
$this->accountStore = $accountStore;
28+
$this->apiBase = $accountStore->getApiBase();
29+
} else if ($this->requiresToken) {
30+
throw new \InvalidArgumentException("{$this->endpoint} endpoint requires an AccountStore for the token");
31+
} else {
32+
$this->apiBase = AtomxClient::API_BASE;
33+
}
2034

2135
parent::__construct();
2236

23-
$this->accountStore = $accountStore;
24-
25-
2637
if (is_array($idOrFields))
2738
$this->fields = $idOrFields;
2839
else if (is_numeric($idOrFields))
@@ -48,62 +59,33 @@ public function setId($id)
4859

4960
protected function handleResponse(Response $response)
5061
{
51-
// TODO: Handle an invalid token/not logged in message
52-
return json_decode(parent::handleResponse($response), true);
53-
}
54-
55-
protected function getDefaultOptions()
56-
{
57-
$options = parent::getDefaultOptions();
58-
59-
if ($this->shouldSendToken)
60-
$options['headers'] = ['Authorization' => $this->getToken()];
61-
62-
return $options;
63-
}
62+
$code = $response->getStatusCode();
6463

65-
public function login()
66-
{
67-
$this->shouldSendToken = false;
68-
69-
try {
70-
$response = $this->postUrl('login', [
71-
'json' => [
72-
'email' => $this->accountStore->getUsername(),
73-
'password' => $this->accountStore->getPassword()
74-
]
75-
]);
76-
} catch (ApiException $e) {
77-
throw new ApiException('Unable to login to API!');
64+
if ($code == 200) {
65+
return json_decode($response->getBody()->getContents(), true);
7866
}
7967

80-
if ($response instanceof Stream) {
81-
$response = json_decode($response->getContents(), true);
68+
if ($code == 401 && $this->requiresToken) {
69+
// Unauthorized, invalidate token
70+
$this->accountStore->storeToken(null);
8271
}
8372

84-
$this->shouldSendToken = true;
85-
86-
if ($response['success'] !== true)
87-
throw new ApiException('Unable to login to API!');
88-
73+
throw new ApiException('Request failed, received the following status: ' .
74+
$response->getStatusCode() . ' Body: ' . $response->getBody()->getContents());
75+
}
8976

90-
$token = 'Bearer ' . $response['auth_token'];
77+
protected function getDefaultOptions()
78+
{
79+
$options = parent::getDefaultOptions();
9180

92-
$this->accountStore->storeToken($token);
81+
if ($this->requiresToken)
82+
$options['headers'] = ['Authorization' => 'Bearer ' . $this->getToken()];
9383

94-
return $response['user'];
84+
return $options;
9585
}
9686

9787
private function getToken()
9888
{
99-
$token = $this->accountStore->getToken();
100-
101-
if ($token !== null) {
102-
return $token;
103-
}
104-
105-
$this->login();
106-
10789
return $this->accountStore->getToken();
10890
}
10991

Atomx/Exceptions/ApiException.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php namespace Atomx\Exceptions;
2+
3+
use Exception;
4+
5+
class ApiException extends Exception { }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php namespace Atomx\Exceptions;
2+
3+
class TotpRequiredException extends ApiException {
4+
protected $response = null;
5+
6+
public function __construct($response)
7+
{
8+
$this->response = $response;
9+
}
10+
11+
public function getResponse()
12+
{
13+
return $this->response;
14+
}
15+
}

Atomx/LoginTokenStore.php

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php namespace Atomx;
2+
3+
use Atomx\Exceptions\TotpRequiredException;
4+
use Atomx\Resources\Login;
5+
6+
class LoginTokenStore implements TokenStore {
7+
protected $token = null;
8+
protected $username, $password, $totp, $apiBase;
9+
10+
/**
11+
* @param string|null $username
12+
* @param string|null $password
13+
* @param string|null $totp
14+
* @param string|null $apiBase
15+
*/
16+
public function __construct($username = null, $password = null, $totp = null, $apiBase = null)
17+
{
18+
$this->username = $username;
19+
$this->password = $password;
20+
$this->totp = $totp;
21+
$this->apiBase = $apiBase;
22+
23+
if ($this->apiBase == null)
24+
$this->apiBase = AtomxClient::API_BASE;
25+
}
26+
27+
/**
28+
* @return null|string
29+
*/
30+
public function getToken()
31+
{
32+
if (is_null($this->token)) {
33+
$this->storeToken($this->getTokenFromLogin());
34+
}
35+
36+
return $this->token;
37+
}
38+
39+
protected function getLoginClient()
40+
{
41+
return new Login($this);
42+
}
43+
44+
protected function getTokenFromLogin()
45+
{
46+
$response = $this->getLoginClient()->login([
47+
'email' => $this->getUsername(),
48+
'password' => $this->getPassword(),
49+
'totp' => $this->getTotp()
50+
]);
51+
52+
if ($response['totp_required'])
53+
throw new TotpRequiredException($response);
54+
55+
return $response['auth_token'];
56+
}
57+
58+
/**
59+
* @param string|null $token
60+
*/
61+
public function storeToken($token)
62+
{
63+
$this->token = $token;
64+
}
65+
66+
/**
67+
* @return string
68+
*/
69+
public function getUsername()
70+
{
71+
return $this->username;
72+
}
73+
74+
/**
75+
* @return string
76+
*/
77+
public function getPassword()
78+
{
79+
return $this->password;
80+
}
81+
82+
public function getTotp()
83+
{
84+
return $this->totp;
85+
}
86+
87+
/**
88+
* @return string
89+
*/
90+
public function getApiBase()
91+
{
92+
return $this->apiBase;
93+
}
94+
}

Atomx/ReportStreamer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php namespace Atomx;
22

3-
use Atomx\AccountStore;
3+
use Atomx\TokenStore;
44
use GuzzleHttp\Stream\Stream;
55

66

Atomx/Resources/Browsers.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55

66
class Browsers extends AtomxClient {
77
protected $endpoint = 'browsers';
8+
protected $requiresToken = false;
89
}

Atomx/Resources/CategoryList.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44

55
class CategoryList extends AtomxClient {
66
protected $endpoint = 'categories';
7+
protected $requiresToken = false;
78
}

0 commit comments

Comments
 (0)