Completed
Push — fix ( 05f8bd )
by
unknown
03:08
created

Server::grant()   B

Complexity

Conditions 3
Paths 1

Size

Total Lines 41
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 3

Importance

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