Skip to content

Commit 05f7f4c

Browse files
committed
Merge branch 'development' of github.com:rubentalstra/BookStack into rubentalstra-development
2 parents 454b152 + da82e70 commit 05f7f4c

File tree

4 files changed

+46
-4
lines changed

4 files changed

+46
-4
lines changed

app/Access/Oidc/OidcService.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use BookStack\Facades\Theme;
1212
use BookStack\Http\HttpRequestService;
1313
use BookStack\Theming\ThemeEvents;
14+
use BookStack\Uploads\UserAvatars;
1415
use BookStack\Users\Models\User;
1516
use Illuminate\Support\Facades\Cache;
1617
use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
@@ -26,7 +27,8 @@ public function __construct(
2627
protected RegistrationService $registrationService,
2728
protected LoginService $loginService,
2829
protected HttpRequestService $http,
29-
protected GroupSyncService $groupService
30+
protected GroupSyncService $groupService,
31+
protected UserAvatars $userAvatars
3032
) {
3133
}
3234

@@ -227,6 +229,10 @@ protected function processAccessTokenCallback(OidcAccessToken $accessToken, Oidc
227229

228230
$this->loginService->login($user, 'oidc');
229231

232+
if ($this->config()['fetch_avatars'] && $userDetails->picture) {
233+
$this->userAvatars->assignToUserFromUrl($user, $userDetails->picture, $accessToken->getToken());
234+
}
235+
230236
return $user;
231237
}
232238

app/Access/Oidc/OidcUserDetails.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public function __construct(
1111
public ?string $email = null,
1212
public ?string $name = null,
1313
public ?array $groups = null,
14+
public ?string $picture = null,
1415
) {
1516
}
1617

@@ -40,6 +41,7 @@ public function populate(
4041
$this->email = $claims->getClaim('email') ?? $this->email;
4142
$this->name = static::getUserDisplayName($displayNameClaims, $claims) ?? $this->name;
4243
$this->groups = static::getUserGroups($groupsClaim, $claims) ?? $this->groups;
44+
$this->picture = $claims->getClaim('picture') ?: $this->picture;
4345
}
4446

4547
protected static function getUserDisplayName(string $displayNameClaims, ProvidesClaims $token): string

app/Config/oidc.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,7 @@
5454
'groups_claim' => env('OIDC_GROUPS_CLAIM', 'groups'),
5555
// When syncing groups, remove any groups that no longer match. Otherwise, sync only adds new groups.
5656
'remove_from_groups' => env('OIDC_REMOVE_FROM_GROUPS', false),
57+
58+
// When enabled, BookStack will fetch the user’s avatar from the 'picture' claim (SSRF risk if URLs are untrusted).
59+
'fetch_avatars' => env('OIDC_FETCH_AVATARS', false),
5760
];

app/Uploads/UserAvatars.php

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,31 @@ public function assignToUserFromExistingData(User $user, string $imageData, stri
5353
}
5454
}
5555

56+
/**
57+
* Assign a new avatar image to the given user by fetching from a remote URL.
58+
*/
59+
public function assignToUserFromUrl(User $user, string $avatarUrl, ?string $accessToken = null): void
60+
{
61+
// Quickly skip invalid or non-HTTP URLs
62+
if (!$avatarUrl || !str_starts_with($avatarUrl, 'http')) {
63+
return;
64+
}
65+
66+
try {
67+
$this->destroyAllForUser($user);
68+
$imageData = $this->getAvatarImageData($avatarUrl, $accessToken);
69+
$avatar = $this->createAvatarImageFromData($user, $imageData, 'png');
70+
$user->avatar()->associate($avatar);
71+
$user->save();
72+
} catch (Exception $e) {
73+
Log::error('Failed to save user avatar image from URL', [
74+
'exception' => $e,
75+
'url' => $avatarUrl,
76+
'user_id' => $user->id,
77+
]);
78+
}
79+
}
80+
5681
/**
5782
* Destroy all user avatars uploaded to the given user.
5883
*/
@@ -105,15 +130,21 @@ protected function createAvatarImageFromData(User $user, string $imageData, stri
105130
}
106131

107132
/**
108-
* Gets an image from url and returns it as a string of image data.
133+
* Gets an image from a URL (public or private) and returns it as a string of image data.
109134
*
110135
* @throws HttpFetchException
111136
*/
112-
protected function getAvatarImageData(string $url): string
137+
protected function getAvatarImageData(string $url, ?string $accessToken = null): string
113138
{
114139
try {
140+
$headers = [];
141+
if (!empty($accessToken)) {
142+
$headers['Authorization'] = 'Bearer ' . $accessToken;
143+
}
144+
115145
$client = $this->http->buildClient(5);
116-
$response = $client->sendRequest(new Request('GET', $url));
146+
$response = $client->sendRequest(new Request('GET', $url, $headers));
147+
117148
if ($response->getStatusCode() !== 200) {
118149
throw new HttpFetchException(trans('errors.cannot_get_image_from_url', ['url' => $url]));
119150
}

0 commit comments

Comments
 (0)