Signed-off-by: Julius Härtl <jus@bitgrid.net>tags/v24.0.0beta1
@@ -27,6 +27,7 @@ declare(strict_types=1); | |||
*/ | |||
namespace OC\Core\Controller; | |||
use OC\Authentication\Exceptions\InvalidTokenException; | |||
use OC\Core\Db\LoginFlowV2; | |||
use OC\Core\Exception\LoginFlowV2NotFoundException; | |||
use OC\Core\Service\LoginFlowV2Service; | |||
@@ -173,6 +174,48 @@ class ClientFlowLoginV2Controller extends Controller { | |||
); | |||
} | |||
/** | |||
* @PublicPage | |||
*/ | |||
public function apptokenRedirect(string $stateToken, string $user, string $password) { | |||
if (!$this->isValidStateToken($stateToken)) { | |||
return $this->stateTokenForbiddenResponse(); | |||
} | |||
try { | |||
$this->getFlowByLoginToken(); | |||
} catch (LoginFlowV2NotFoundException $e) { | |||
return $this->loginTokenForbiddenResponse(); | |||
} | |||
$loginToken = $this->session->get(self::TOKEN_NAME); | |||
// Clear session variables | |||
$this->session->remove(self::TOKEN_NAME); | |||
$this->session->remove(self::STATE_NAME); | |||
try { | |||
$token = \OC::$server->get(\OC\Authentication\Token\IProvider::class)->getToken($password); | |||
if ($token->getLoginName() !== $user) { | |||
throw new InvalidTokenException('login name does not match'); | |||
} | |||
} catch (InvalidTokenException $e) { | |||
$response = new StandaloneTemplateResponse( | |||
$this->appName, | |||
'403', | |||
[ | |||
'message' => $this->l10n->t('Invalid app password'), | |||
], | |||
'guest' | |||
); | |||
$response->setStatus(Http::STATUS_FORBIDDEN); | |||
return $response; | |||
} | |||
$result = $this->loginFlowV2Service->flowDoneWithAppPassword($loginToken, $this->getServerPath(), $this->userId, $password); | |||
return $this->handleFlowDone($result); | |||
} | |||
/** | |||
* @NoAdminRequired | |||
* @UseSession | |||
@@ -196,7 +239,10 @@ class ClientFlowLoginV2Controller extends Controller { | |||
$sessionId = $this->session->getId(); | |||
$result = $this->loginFlowV2Service->flowDone($loginToken, $sessionId, $this->getServerPath(), $this->userId); | |||
return $this->handleFlowDone($result); | |||
} | |||
private function handleFlowDone(bool $result): StandaloneTemplateResponse { | |||
if ($result) { | |||
return new StandaloneTemplateResponse( | |||
$this->appName, |
@@ -186,6 +186,23 @@ class LoginFlowV2Service { | |||
return true; | |||
} | |||
public function flowDoneWithAppPassword(string $loginToken, string $server, string $loginName, string $appPassword): bool { | |||
try { | |||
$data = $this->mapper->getByLoginToken($loginToken); | |||
} catch (DoesNotExistException $e) { | |||
return false; | |||
} | |||
$data->setLoginName($loginName); | |||
$data->setServer($server); | |||
// Properly encrypt | |||
$data->setAppPassword($this->encryptPassword($appPassword, $data->getPublicKey())); | |||
$this->mapper->update($data); | |||
return true; | |||
} | |||
public function createTokens(string $userAgent): LoginFlowV2Tokens { | |||
$flow = new LoginFlowV2(); | |||
$pollToken = $this->random->generate(128, ISecureRandom::CHAR_DIGITS.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER); |
@@ -68,6 +68,7 @@ $application->registerRoutes($this, [ | |||
['name' => 'ClientFlowLoginV2#grantPage', 'url' => '/login/v2/grant', 'verb' => 'GET'], | |||
['name' => 'ClientFlowLoginV2#generateAppPassword', 'url' => '/login/v2/grant', 'verb' => 'POST'], | |||
['name' => 'ClientFlowLoginV2#init', 'url' => '/login/v2', 'verb' => 'POST'], | |||
['name' => 'ClientFlowLoginV2#apptokenRedirect', 'url' => '/login/v2/apptoken', 'verb' => 'POST'], | |||
['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'], | |||
['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'], | |||
['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'], |
@@ -20,6 +20,7 @@ | |||
*/ | |||
style('core', 'login/authpicker'); | |||
script('core', 'login/authpicker'); | |||
/** @var array $_ */ | |||
/** @var \OCP\IURLGenerator $urlGenerator */ | |||
@@ -50,4 +51,21 @@ $urlGenerator = $_['urlGenerator']; | |||
</a> | |||
</p> | |||
<form action="<?php p($urlGenerator->linkToRouteAbsolute('core.ClientFlowLoginV2.apptokenRedirect')); ?>" method="post" id="app-token-login-field" class="hidden"> | |||
<p class="grouptop"> | |||
<input type="text" name="user" id="user" placeholder="<?php p($l->t('Username')) ?>"> | |||
<label for="user" class="infield"><?php p($l->t('Username')) ?></label> | |||
</p> | |||
<p class="groupbottom"> | |||
<input type="password" name="password" id="password" placeholder="<?php p($l->t('App token')) ?>"> | |||
<label for="password" class="infield"><?php p($l->t('Password')) ?></label> | |||
</p> | |||
<input type="hidden" name="stateToken" value="<?php p($_['stateToken']) ?>" /> | |||
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>"> | |||
<input id="submit-app-token-login" type="submit" class="login primary icon-confirm-white" value="<?php p($l->t('Grant access')) ?>"> | |||
</form> | |||
</div> | |||
<?php if (empty($_['oauthState'])): ?> | |||
<a id="app-token-login" class="warning" href="#"><?php p($l->t('Alternative log in using app token')) ?></a> | |||
<?php endif; ?> |