Completed
Push — develop ( 098743...5f7eab )
by Raphael De
02:19
created

Server::loadAccount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 10
ccs 0
cts 9
cp 0
rs 9.4285
cc 1
eloc 6
nc 1
nop 2
crap 2
1
<?php
2
3
namespace Majora\Component\OAuth\Server;
4
5
use Majora\Component\OAuth\Entity\AccessToken;
6
use Majora\Component\OAuth\Entity\LoginAttempt;
7
use Majora\Component\OAuth\Entity\RefreshToken;
8
use Majora\Component\OAuth\Event\AccessTokenEvent;
9
use Majora\Component\OAuth\Event\AccessTokenEvents;
10
use Majora\Component\OAuth\Event\RefreshTokenEvent;
11
use Majora\Component\OAuth\Event\RefreshTokenEvents;
12
use Majora\Component\OAuth\Exception\InvalidGrantException;
13
use Majora\Component\OAuth\Generator\RandomTokenGenerator;
14
use Majora\Component\OAuth\GrantType\GrantExtensionInterface;
15
use Majora\Component\OAuth\Loader\ApplicationLoaderInterface;
16
use Majora\Component\OAuth\Model\AccessTokenInterface;
17
use Majora\Component\OAuth\Model\AccountInterface;
18
use Majora\Component\OAuth\Model\ApplicationInterface;
19
use Majora\Component\OAuth\Model\RefreshTokenInterface;
20
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
21
use Symfony\Component\OptionsResolver\OptionsResolver;
22
23
/**
24
 * OAuth server main class.
25
 */
26
class Server
27
{
28
    /**
29
     * @var GrantExtensionInterface[]
30
     */
31
    protected $grantExtensions;
32
33
    /**
34
     * @var ApplicationLoaderInterface
35
     */
36
    protected $applicationLoader;
37
38
    /**
39
     * @var EventDispatcherInterface
40
     */
41
    protected $eventDispatcher;
42
43
    /**
44
     * @var RandomTokenGenerator
45
     */
46
    protected $randomTokenGenerator;
47
48
    /**
49
     * @var int
50
     */
51
    protected $accessTokenTtl;
52
53
    /**
54
     * @var string
55
     */
56
    protected $accessTokenClassName;
57
58
    /**
59
     * @var int
60
     */
61
    protected $refreshTokenTtl;
62
63
    /**
64
     * @var string
65
     */
66
    protected $refreshTokenClassName;
67
68
    /**
69
     * Construct.
70
     *
71
     * @param EventDispatcherInterface   $eventDispatcher
72
     * @param ApplicationLoaderInterface $applicationLoader
73
     * @param int                        $accessTokenTtl
74
     * @param string                     $accessTokenClassName
75
     * @param int                        $refreshTokenTtl
76
     * @param string                     $refreshTokenClassName
77
     * @param RandomTokenGenerator       $randomTokenGenerator
78
     * @param array                      $grantExtensions
79
     */
80
    public function __construct(
81
        EventDispatcherInterface $eventDispatcher,
82
        ApplicationLoaderInterface $applicationLoader,
83
        $accessTokenTtl,
84
        $accessTokenClassName,
85
        $refreshTokenTtl,
86
        $refreshTokenClassName,
87
        RandomTokenGenerator $randomTokenGenerator,
88
        array $grantExtensions = array()
89
    ) {
90
        $this->applicationLoader = $applicationLoader;
91
        $this->eventDispatcher = $eventDispatcher;
92
        $this->randomTokenGenerator = $randomTokenGenerator;
93
94
        $this->accessTokenTtl = $accessTokenTtl ?: AccessTokenInterface::DEFAULT_TTL;
95
        $this->accessTokenClassName = $accessTokenClassName ?: AccessToken::class;
96
97
        $this->refreshTokenTtl = $refreshTokenTtl ?: RefreshTokenInterface::DEFAULT_TTL;
98
        $this->refreshTokenClassName = $refreshTokenClassName ?: RefreshToken::class;
99
100
        $this->grantExtensions = array();
101
        foreach ($grantExtensions as $grantType => $extension) {
102
            $this->registerGrantExtension($grantType, $extension);
103
        }
104
    }
105
106
    /**
107
     * Register an extension under given grant type.
108
     *
109
     * @param string                  $grantType
110
     * @param GrantExtensionInterface $extension
111
     */
112
    public function registerGrantExtension($grantType, GrantExtensionInterface $extension)
113
    {
114
        $this->grantExtensions[$grantType] = $extension;
115
    }
116
117
    /**
118
     * Validate given request parameters and build a
119
     * LoginAttempt object with it.
120
     *
121
     * @param array $data
122
     * @param array $headers
123
     * @param array $query
124
     *
125
     * @return LoginAttempt
126
     */
127
    protected function createLoginAttempt(array $data, array $headers, array $query)
128
    {
129
        // validate grant_type manually (needed to guess specialized option resolver)
130
        if (empty($data['grant_type'])) {
131
            throw new \InvalidArgumentException('Any grant_type given.');
132
        }
133
        $grantType = $data['grant_type'];
134
        if (!isset($this->grantExtensions[$grantType])) {
135
            throw new \InvalidArgumentException('Given grant_type is invalid.');
136
        }
137
138
        // create option resolver
139
        $requestResolver = new OptionsResolver();
140
        $requestResolver->setRequired(array(
141
            'client_secret',
142
            'client_api_key',
143
            'grant_type',
144
        ));
145
        $this->grantExtensions[$grantType]->configureRequestParameters(
146
            $requestResolver
147
        );
148
149
        return new LoginAttempt(
150
            $query,
151
            $requestResolver->resolve($data),
152
            $headers
153
        );
154
    }
155
156
    /**
157
     * Loads application for given login attempt.
158
     *
159
     * @param LoginAttempt $loginAttempt
160
     *
161
     * @return ApplicationInterface
162
     *
163
     * @throws InvalidGrantException
164
     */
165
    protected function loadApplication(LoginAttempt $loginAttempt)
166
    {
167
        // retrieve Application
168
        if (!$application = $this->applicationLoader->retrieveByApiKeyAndSecret(
169
            $loginAttempt->getData('client_api_key'),
170
            $loginAttempt->getData('client_secret')
171
        )) {
172
            throw new InvalidGrantException(
173
                $loginAttempt,
174
                'Any application found for given api_key / secret.'
175
            );
176
        }
177
178
        return $application;
179
    }
180
181
    /**
182
     * Runs grant extension to load accounts.
183
     *
184
     * @param ApplicationInterface $application
185
     * @param LoginAttempt         $loginAttempt
186
     *
187
     * @return AccountInterface
188
     *
189
     * @throws \InvalidArgumentException
190
     * @throws UnknownGrantTypeException
191
     */
192
    protected function loadAccount(
193
        ApplicationInterface $application,
194
        LoginAttempt $loginAttempt
195
    ) {
196
        // run grant extension
197
        return $this->grantExtensions[$loginAttempt->getData('grant_type')]->grant(
198
            $application,
199
            $loginAttempt
200
        );
201
    }
202
203
    /**
204
     * Grant given credentials, or throws an exception if invalid
205
     * credentials for application or account.
206
     *
207
     * @param array $data    login request data
208
     * @param array $headers optionnal login request headers
209
     * @param array $query   optionnal login request query
210
     *
211
     * @return AccessTokenInterface
212
     */
213
    public function grant(array $data, array $headers = array(), array $query = array())
214
    {
215
        // create and validate login attempt from given data
216
        $loginAttempt = $this->createLoginAttempt(
217
            $data, $headers, $query
218
        );
219
220
        // load application / account
221
        $account = $this->loadAccount(
222
            $application = $this->loadApplication($loginAttempt),
223
            $loginAttempt
224
        );
225
226
        // event call
227
        $this->eventDispatcher->dispatch(
228
            AccessTokenEvents::MAJORA_ACCESS_TOKEN_CREATED,
229
            new AccessTokenEvent(
230
                $accessToken = new $this->accessTokenClassName(
231
                    $application,
232
                    $account,
233
                    $this->accessTokenTtl,
234
                    $this->randomTokenGenerator->generate('access_token')
235
                )
236
            )
237
        );
238
239
        $this->eventDispatcher->dispatch(
240
            RefreshTokenEvents::MAJORA_REFRESH_TOKEN_CREATED,
241
            new RefreshTokenEvent(
242
                $refreshToken = new $this->refreshTokenClassName(
243
                    $application,
244
                    $account,
245
                    $this->refreshTokenTtl,
246
                    $this->randomTokenGenerator->generate('refresh_token')
247
                )
248
            )
249
        );
250
251
        return $accessToken;
252
    }
253
}
254