Skip to content

Commit 2a48ce0

Browse files
josephsnyderzackgalbreathwilliamjallen
authored
Add PingIdentity provider (Kitware#2038)
Add a local package for the Socialite Package for the PingIdentity provider. --------- Co-authored-by: Zack Galbreath <zack.galbreath@kitware.com> Co-authored-by: William Allen <16820599+williamjallen@users.noreply.github.com>
1 parent 6e8d60a commit 2a48ce0

File tree

12 files changed

+270
-3
lines changed

12 files changed

+270
-3
lines changed

app/Providers/EventServiceProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class EventServiceProvider extends ServiceProvider
2323
\SocialiteProviders\GitLab\GitLabExtendSocialite::class.'@handle',
2424
\SocialiteProviders\GitHub\GitHubExtendSocialite::class.'@handle',
2525
\SocialiteProviders\Google\GoogleExtendSocialite::class.'@handle',
26+
\SocialiteProviders\PingIdentity\PingIdentityExtendSocialite::class.'@handle',
2627
],
2728
];
2829

composer.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
"shiftonelabs/laravel-sqs-fifo-queue": "3.0.1",
4444
"socialiteproviders/github": "4.1.0",
4545
"socialiteproviders/gitlab": "4.1.0",
46-
"socialiteproviders/google": "4.1.0"
46+
"socialiteproviders/google": "4.1.0",
47+
"socialiteproviders/pingidentity": "1.0.0"
4748
},
4849
"require-dev": {
4950
"ext-dom": "*",
@@ -109,5 +110,11 @@
109110
"post-create-project-cmd": [
110111
"@php artisan key:generate --ansi"
111112
]
112-
}
113+
},
114+
"repositories": [
115+
{
116+
"type": "path",
117+
"url": "./resources/providers/PingIdentity"
118+
}
119+
]
113120
}

composer.lock

Lines changed: 38 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/services.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@
4444
'display_name' => "Google",
4545
],
4646

47+
'pingidentity' => [
48+
'client_id' => env('PINGIDENTITY_CLIENT_ID'),
49+
'client_secret' => env('PINGIDENTITY_CLIENT_SECRET'),
50+
'redirect' => env('PINGIDENTITY_REDIRECT_URI'),
51+
'instance_uri' => env('PINGIDENTITY_DOMAIN'),
52+
'enable' => env('PINGIDENTITY_ENABLE', false),
53+
'autoregister' => env('PINGIDENTITY_AUTO_REGISTER_NEW_USERS', false),
54+
'oauth' => true,
55+
'display_name' => "PingIdentity",
56+
],
57+
4758
'mailgun' => [
4859
'domain' => env('MAILGUN_DOMAIN'),
4960
'secret' => env('MAILGUN_SECRET'),

docs/authentication.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,19 @@ Begin by [creating OAuth2 credentials for your Google project](https://developer
9393
| GOOGLE_CLIENT_SECRET | The client secret from your Google OAuth2 credentials. | '' |
9494
| GOOGLE_AUTO_REGISTER_NEW_USERS | Whether to automatically register a new user or provide them the Registration form | false
9595

96+
###### PingIdentity
97+
98+
Begin by [creating OAuth2 client in your PingIdentity console](https://docs.pingidentity.com/r/en-us/solution-guides/mzt1663945300370). Then fill out the following `.env` variables:
99+
100+
| Variable | Description | Default |
101+
| -------- |------------ | ------- |
102+
| PINGIDENTITY_ENABLE | Whether or not to use Google as an OAuth2 provider. | false |
103+
| PINGIDENTITY_CLIENT_ID | The client ID from your Google OAuth2 credentials. | '' |
104+
| PINGIDENTITY_CLIENT_SECRET | The client secret from your Google OAuth2 credentials. | '' |
105+
| PINGIDENTITY_DOMAIN | The GitLab server to authenticate against. | https://auth.pingone.com/ |
106+
| PINGIDENTITY_REDIRECT_URI | The callback URL for the CDash instance: <CDashURL>/auth/pingidentity/callback. | '' |
107+
| PINGIDENTITY_AUTO_REGISTER_NEW_USERS | Whether to automatically register a new user or provide them the Registration form | false
108+
96109
## SAML2
97110

98111
To configure CDash to authenticate against a SAML2 identity provider, you need to call `php artisan saml2:create-tenant` from the root of your CDash clone. For more details about the arguments that this Artisan command accepts, please run `php artisan saml2:create-tenant --help` or view the [upstream documentation](https://github.com/24Slides/laravel-saml2/#step-2-create-a-tenant).

phpstan-baseline.neon

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29442,6 +29442,16 @@ parameters:
2944229442
count: 1
2944329443
path: database/migrations/2023_04_16_204249_project_name_regex.php
2944429444

29445+
-
29446+
message: "#^Method SocialiteProviders\\\\PingIdentity\\\\Provider\\:\\:mapUserToObject\\(\\) has parameter \\$user with no value type specified in iterable type array\\.$#"
29447+
count: 1
29448+
path: resources/providers/PingIdentity/Provider.php
29449+
29450+
-
29451+
message: "#^Property SocialiteProviders\\\\PingIdentity\\\\Provider\\:\\:\\$scopes type has no value type specified in iterable type array\\.$#"
29452+
count: 1
29453+
path: resources/providers/PingIdentity/Provider.php
29454+
2944529455
-
2944629456
message: "#^Undefined variable\\: \\$this$#"
2944729457
count: 1
@@ -29558,6 +29568,16 @@ parameters:
2955829568
count: 1
2955929569
path: tests/Feature/LdapAuthWithRulesTest.php
2956029570

29571+
-
29572+
message: "#^Call to an undefined method Mockery\\\\Expectation\\:\\:shouldReceive\\(\\)\\.$#"
29573+
count: 1
29574+
path: tests/Feature/LoginAndRegistration.php
29575+
29576+
-
29577+
message: "#^Call to method shouldReceive\\(\\) on an unknown class Laravel\\\\Socialite\\\\PingIdentity\\\\Provider\\.$#"
29578+
count: 1
29579+
path: tests/Feature/LoginAndRegistration.php
29580+
2956129581
-
2956229582
message: "#^Cannot call method delete\\(\\) on App\\\\Models\\\\User\\|null\\.$#"
2956329583
count: 1

public/img/pingidentity_signin.png

548 Bytes
Loading
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace SocialiteProviders\PingIdentity;
4+
5+
use SocialiteProviders\Manager\SocialiteWasCalled;
6+
7+
class PingIdentityExtendSocialite
8+
{
9+
public function handle(SocialiteWasCalled $socialiteWasCalled): void
10+
{
11+
$socialiteWasCalled->extendSocialite('pingidentity', Provider::class);
12+
}
13+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
namespace SocialiteProviders\PingIdentity;
4+
5+
use GuzzleHttp\RequestOptions;
6+
use SocialiteProviders\Manager\OAuth2\AbstractProvider;
7+
use SocialiteProviders\Manager\OAuth2\User;
8+
9+
class Provider extends AbstractProvider
10+
{
11+
public const IDENTIFIER = 'PINGIDENTITY';
12+
13+
/**
14+
* The scopes being requested.
15+
*
16+
* @var array
17+
*/
18+
protected $scopes = [
19+
'openid',
20+
'profile',
21+
'email',
22+
];
23+
24+
protected $scopeSeparator = ' ';
25+
26+
/**
27+
* Get the authentication URL for the provider.
28+
*
29+
* @param string $state
30+
*/
31+
protected function getAuthUrl($state): string
32+
{
33+
$auth_url = $this->buildAuthUrlFromBase($this->getInstanceUri().'/as/authorization.oauth2', $state);
34+
$auth_url .= "&acr_values=Single_Factor&prompt=login";
35+
return $auth_url;
36+
}
37+
38+
/**
39+
* Get the token URL for the provider.
40+
*/
41+
protected function getTokenUrl(): string
42+
{
43+
return $this->getInstanceUri() . '/as/token.oauth2?acr_values=Single_Factor&prompt=login';
44+
}
45+
46+
/**
47+
* Map the raw user array to a Socialite User instance.
48+
*/
49+
protected function mapUserToObject(array $user): \Laravel\Socialite\Two\User
50+
{
51+
return (new User())->setRaw($user)->map([
52+
'nickname' => $user['name'],
53+
'name' => $user['given_name'] . " " . $user['family_name'],
54+
'email' => $user['email'],
55+
]);
56+
}
57+
58+
/**
59+
* Get the Instance URL for the provider.
60+
*/
61+
protected function getInstanceUri(): string
62+
{
63+
return $this->getConfig('instance_uri', 'https://auth.pingone.com/');
64+
}
65+
66+
/**
67+
* Get the raw user for the given access token.
68+
*
69+
* @param string $token
70+
* @return array<string>
71+
*/
72+
protected function getUserByToken($token): array
73+
{
74+
$response = $this->getHttpClient()->get($this->getInstanceUri() . '/idp/userinfo.openid', [
75+
RequestOptions::HEADERS => [
76+
'Authorization' => "Bearer $token",
77+
],
78+
]);
79+
80+
return json_decode((string) $response->getBody(), true);
81+
}
82+
83+
/**
84+
* Additional configuration key values that may be set
85+
* @return array<string>
86+
*/
87+
public static function additionalConfigKeys(): array
88+
{
89+
return ['instance_uri'];
90+
}
91+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "socialiteproviders/pingidentity",
3+
"description": "PingIdentity OAuth2 Provider for Laravel Socialite",
4+
"version": "1.0.0",
5+
"license": "MIT",
6+
"keywords": [
7+
"pingidentity",
8+
"laravel",
9+
"oauth",
10+
"provider",
11+
"socialite"
12+
],
13+
"authors": [
14+
{
15+
}
16+
],
17+
"require": {
18+
"php": "^8.0",
19+
"ext-json": "*",
20+
"socialiteproviders/manager": "^4.4"
21+
},
22+
"autoload": {
23+
"psr-4": {
24+
"SocialiteProviders\\PingIdentity\\": ""
25+
}
26+
}
27+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"packages": {
3+
"socialiteproviders/pingidentity": {
4+
"1.0.0": { @composer.json }
5+
}
6+
}
7+
}

tests/Feature/LoginAndRegistration.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Slides\Saml2\Events\SignedIn as Saml2SignedInEvent;
1414
use Symfony\Component\HttpKernel\Exception\HttpException;
1515
use Tests\TestCase;
16+
use Laravel\Socialite\Facades\Socialite;
1617

1718
class LoginAndRegistration extends TestCase
1819
{
@@ -250,6 +251,45 @@ public function testSaml2LoginListener() : void
250251
Mockery::close();
251252
}
252253

254+
public function testPingIdentity() : void
255+
{
256+
// Verify that the PingIdentity button doesn't appear by default.
257+
$response = $this->get('/login');
258+
$response->assertDontSeeText('PingIdentity');
259+
260+
// Enable PingIdentity, verify the button appears.
261+
config(['services.pingidentity.enable' => true]);
262+
$response = $this->get('/login');
263+
$response->assertSeeText('PingIdentity');
264+
}
265+
266+
/**
267+
* Test PingIdentity authentication
268+
*/
269+
public function testPingIdentityProvider() : void
270+
{
271+
// Stolen from: https://laracasts.com/discuss/channels/testing/testing-laravel-socialite-callback
272+
$abstractUser = Mockery::mock('Laravel\Socialite\Two\User');
273+
$abstractUser->shouldReceive('getId')
274+
->andReturn(1234567890)
275+
->shouldReceive('getEmail')
276+
->andReturn('cdash@test.com')
277+
->shouldReceive('getNickname')
278+
->andReturn('Pseudo')
279+
->shouldReceive('getName')
280+
->andReturn('Arlette Laguiller')
281+
->shouldReceive('getAvatar')
282+
->andReturn('https://en.gravatar.com/userimage');
283+
284+
$provider = Mockery::mock('Laravel\Socialite\PingIdentity\Provider');
285+
$provider->shouldReceive('user')->andReturn($abstractUser);
286+
287+
Socialite::shouldReceive('driver')->with('pingidentity')->andReturn($provider);
288+
289+
$response = $this->get("auth/pingidentity/callback");
290+
$response->assertRedirect("/register?fname=Arlette&lname=Laguiller&email=cdash%40test.com");
291+
}
292+
253293
public function testRegisterUserWhenDisabled() : void
254294
{
255295
// Create a user by sending proper data

0 commit comments

Comments
 (0)