Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stateful 2fa providers #9632

Merged
merged 2 commits into from
Jun 25, 2018
Merged

Conversation

ChristophWurst
Copy link
Member

@ChristophWurst ChristophWurst commented May 28, 2018

This change makes two-factor providers and their association with users (whether the provider is enabled or not) stateful as in that we don't actually require the provider to be loaded when we want to check if a user uses 2FA. This greatly improves performance as we don't have to read from the file system and parse the info.xml file.

To do:

  • Basic implementation
  • Adapted/added tests
  • Squashed commits
  • Adapt 2fa apps to use the IRegistry to propagate their enabled/disabled state
    • twofactor_u2f
    • twofactor_totp
    • twofactor_sms
    • others?
  • Proper handling of the backup codes app (auto-enable when other providers get enabled? auto-disable when all other providers get disabled?) won't change that for now and keep the existing logic (app has to be configured by user).

cc @rullzer

@codecov
Copy link

codecov bot commented Jun 4, 2018

Codecov Report

Merging #9632 into master will decrease coverage by <.01%.
The diff coverage is 56.92%.

@@             Coverage Diff              @@
##             master    #9632      +/-   ##
============================================
- Coverage      52.1%   52.09%   -0.01%     
- Complexity    25910    25949      +39     
============================================
  Files          1642     1648       +6     
  Lines         95721    95862     +141     
  Branches       1289     1289              
============================================
+ Hits          49871    49943      +72     
- Misses        45850    45919      +69
Impacted Files Coverage Δ Complexity Δ
lib/private/Server.php 82.24% <ø> (-0.18%) 278 <0> (-1)
core/templates/twofactorselectchallenge.php 0% <0%> (ø) 0 <0> (ø) ⬇️
core/Command/TwoFactorAuth/State.php 0% <0%> (ø) 11 <11> (?)
core/Migrations/Version14000Date20180522074438.php 0% <0%> (ø) 4 <4> (?)
core/register_command.php 0% <0%> (ø) 0 <0> (ø) ⬇️
version.php 0% <0%> (ø) 0 <0> (ø) ⬇️
core/Controller/LoginController.php 79.02% <100%> (ø) 38 <0> (ø) ⬇️
.../private/Authentication/TwoFactorAuth/Registry.php 100% <100%> (ø) 4 <4> (?)
...ivate/Authentication/TwoFactorAuth/ProviderSet.php 100% <100%> (ø) 5 <5> (?)
...b/private/Authentication/TwoFactorAuth/Manager.php 80% <74%> (+1.95%) 36 <13> (+1) ⬆️
... and 11 more

@ChristophWurst ChristophWurst force-pushed the enhancement/stateful-2fa-providers branch from a3bd376 to 7c27db9 Compare June 18, 2018 08:42
@ChristophWurst ChristophWurst added 3. to review Waiting for reviews and removed 2. developing Work in progress labels Jun 18, 2018
@ChristophWurst ChristophWurst changed the title [WIP] Stateful 2fa providers Stateful 2fa providers Jun 18, 2018
@ChristophWurst ChristophWurst force-pushed the enhancement/stateful-2fa-providers branch from 7c27db9 to 35b8532 Compare June 18, 2018 09:15
@ChristophWurst
Copy link
Member Author

This is ready for review now.

Steps to test:

  • Enable a 2fa provider app
  • Configure the 2fa provider
  • Log in once
  • Log out
  • Disable the app again
  • Try to log in

Before:
You could still log in.

Now:
You see a warning that at least one provider failed to load and you cannot complete the login.

@ChristophWurst
Copy link
Member Author

ChristophWurst commented Jun 18, 2018

  • failure 1

There were 2 warnings:

  1. Tests\Core\Controller\LoginControllerTest::testLoginWithOneTwoFactorProvider
    Trying to configure method "getProviders" which cannot be configured because it does not exist, has not been specified, is final, or is static

  2. Tests\Core\Controller\LoginControllerTest::testLoginWithMultpleTwoFactorProviders
    Trying to configure method "getProviders" which cannot be configured because it does not exist, has not been specified, is final, or is static

  • failure 2
  1. Test\Authentication\TwoFactorAuth\Db\ProviderUserAssignmentDaoTest::testPersist
    Failed asserting that 0 is identical to 1.

/drone/src/github.com/nextcloud/server/tests/lib/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDaoTest.php:93

->andWhere($qb->expr()->eq('enabled', $qb->createNamedParameter(0)));
$res = $q->execute();
$cnt = $res->rowCount();
$this->assertSame(1, $cnt);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some weird reason the query returns 0 rows here. I cannot find out why that is the case. If I attach a debugger an step through the code I see that the ->persist call inserts one row.

@rullzer did I use the query builder incorrectly?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rowcount is the number of rows affected by insert/update/delete

Either do 'fetchAll' and count on that. Or use the count function in the query. Both should work just fine here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should have read the method docs. Makes sense, updated my code and tests seem to pass locally. Thanks a lot!

version.php Outdated
@@ -29,7 +29,7 @@
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
// when updating major/minor version number.

$OC_Version = array(14, 0, 0, 4);
$OC_Version = array(14, 0, 0, 5);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be changed again, because another PR with the same change got merged.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*/
public function preSchemaChange(IOutput $output, Closure $schemaClosure,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

->andWhere($qb->expr()->eq('enabled', $qb->createNamedParameter(0)));
$res = $q->execute();
$cnt = $res->rowCount();
$this->assertSame(1, $cnt);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rowcount is the number of rows affected by insert/update/delete

Either do 'fetchAll' and count on that. Or use the count function in the query. Both should work just fine here.

@rullzer
Copy link
Member

rullzer commented Jun 19, 2018

Very cool stuff. I need to test it properly but code makes a lot of sense

@ChristophWurst
Copy link
Member Author

Let's see if CI likes the current state and if so, I'll squash my commits one more time.

This adds persistence to the Nextcloud server 2FA logic so that the server
knows which 2FA providers are enabled for a specific user at any time, even
when the provider is not available.

The `IStatefulProvider` interface was added as tagging interface for providers
that are compatible with this new API.

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
@ChristophWurst ChristophWurst force-pushed the enhancement/stateful-2fa-providers branch from f341134 to 13d93f5 Compare June 20, 2018 06:30
@MorrisJobke
Copy link
Member

Let's see if CI likes the current state and if so, I'll squash my commits one more time.

And now? xD

@ChristophWurst
Copy link
Member Author

And now? xD

Squashed, rebased and CI is happy. Time to review :)

Will fix the 2fa providers soonish so that they propagate their state automatically.

Copy link
Member

@rullzer rullzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool stuff!!!

@@ -0,0 +1,88 @@
<?php

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strict

* @param string $appId
*/
protected function loadTwoFactorApp(string $appId) {
if (!OC_App::isAppLoaded($appId)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use the appmanager here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither the interface, nor the implementation provide methods to load an app.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #8505

@@ -0,0 +1,71 @@
<?php

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strict

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
@@ -1,6 +1,11 @@
<div class="warning">
<h2 class="two-factor-header"><?php p($l->t('Two-factor authentication')) ?></h2>
<p><?php p($l->t('Enhanced security is enabled for your account. Please authenticate using a second factor.')) ?></p>
<?php if ($_['providerMissing']): ?>
<p>
<strong><?php p($l->t('Could not load at least one of your enabled two-factor auth methods. Please contact your admin.')) ?></strong>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to add an setup check for this as well? Just iterate over the table and list them for the admin?

Copy link
Member Author

@ChristophWurst ChristophWurst Jun 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like querying a set of generally enabled providers and checking if they can be loaded? Sounds reasonable.

Edit: I'll create a ticket for that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> #9985

Copy link
Member

@MorrisJobke MorrisJobke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@MorrisJobke
Copy link
Member

@MorrisJobke MorrisJobke added 4. to release Ready to be released and/or waiting for tests to finish and removed 3. to review Waiting for reviews labels Jun 25, 2018
@ChristophWurst
Copy link
Member Author

The one failing acceptance tests looks unrelated to this change.

@MorrisJobke MorrisJobke merged commit 9444a3f into master Jun 25, 2018
@MorrisJobke MorrisJobke deleted the enhancement/stateful-2fa-providers branch June 25, 2018 13:50

namespace OC\Core\Command\TwoFactorAuth;

use OC\Core\Command\Base;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PHP Fatal error: Cannot use OC\Core\Command\Base as Base because the name is already in use in /drone/src/github.com/nextcloud/server/core/Command/TwoFactorAuth/State.php on line 29

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #10023

rcdevs pushed a commit to rcdevs/nextcloud_openotp_auth that referenced this pull request Sep 18, 2018
-  Admins can enable or disable 2FA for all users, this change give the possibility to be "statefull" in other word
we have to register enable/disable state for all users in IRegistry during plugin configuration (all user IRegistry will be populated at first config)
- Add type to IProvider implemented classes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4. to release Ready to be released and/or waiting for tests to finish enhancement feature: authentication
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants