Merge ~roadmr/canonical-identity-provider:add-2fa-dev-creation-metrics into canonical-identity-provider:master

Proposed by Daniel Manrique
Status: Merged
Approved by: Daniel Manrique
Approved revision: c6d56679be600adde7bfb2946c58328f8cf1c4f0
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~roadmr/canonical-identity-provider:add-2fa-dev-creation-metrics
Merge into: canonical-identity-provider:master
Diff against target: 113 lines (+36/-4)
2 files modified
src/webui/tests/test_views_devices.py (+22/-0)
src/webui/views/devices.py (+14/-4)
Reviewer Review Type Date Requested Status
Maximiliano Bertacchini Approve
Review via email: mp+384389@code.launchpad.net

Commit message

Emit metrics when 2fa devices are added.

The metric includes the device type (automatically-added backup devices
have the fake "paper_auto" type) and subtype (for OATH devices which
can be TOTP or HOTP)

Description of the change

Emitted metrics look like flows.device.$DEVICE_TYPE.add.

Device types can be e.g. google_totp, google_hotp (and so on for oath devices), paper, paper_auto, for now.

To post a comment you must log in.
Revision history for this message
Maximiliano Bertacchini (maxiberta) wrote :

LGTM!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/webui/tests/test_views_devices.py b/src/webui/tests/test_views_devices.py
2index 4c1586b..aadca90 100644
3--- a/src/webui/tests/test_views_devices.py
4+++ b/src/webui/tests/test_views_devices.py
5@@ -50,6 +50,8 @@ class DeviceViewsTestCaseBase(AuthenticatedTestCase):
6 self.switch_twofactor = switches(TWOFACTOR=True)
7 self.switch_twofactor.patch()
8 self.addCleanup(self.switch_twofactor.unpatch)
9+ self.mock_increment = self.patch(
10+ 'webui.views.ui.stats.increment')
11
12 def do_2fa_login(self, device):
13 oath_token = self.factory.make_next_otp(device)
14@@ -509,6 +511,11 @@ class DeviceAdditionViewTestCase(DeviceViewsTestCaseBase):
15 )
16 self.assertRedirects(response, redirect_to, status_code=303)
17
18+ args_list = [
19+ ((u'flows.device.generic_hotp',), {u'key': u'new'}),
20+ ((u'flows.device.paper_auto',), {u'key': u'new'})]
21+ self.assertEqual(self.mock_increment.call_args_list, args_list)
22+
23 # When the auto backup deviceflag is unset...
24 @switches(TWOFACTOR_AUTO_BACKUP_DEVICE=False)
25 def test_successful_post_generic_no_paper_no_auto_add(self):
26@@ -521,6 +528,10 @@ class DeviceAdditionViewTestCase(DeviceViewsTestCaseBase):
27 # Does not add a paper backup device
28 self.assertEqual(devices.filter(device_type="paper").count(), 0)
29
30+ args_list = [
31+ ((u'flows.device.generic_hotp',), {u'key': u'new'})]
32+ self.assertEqual(self.mock_increment.call_args_list, args_list)
33+
34 # When the auto backup deviceflag is set...
35 @switches(TWOFACTOR_AUTO_BACKUP_DEVICE=True)
36 def test_successful_post_generic_paper_no_add(self):
37@@ -538,6 +549,13 @@ class DeviceAdditionViewTestCase(DeviceViewsTestCaseBase):
38 # Does not add additional paper devices.
39 self.assertEqual(devices.filter(device_type="paper").count(), 1)
40
41+ # Args list changes a bit because we did an actual login
42+ args_list = [
43+ ((u'flows.login',), {u'rpconfig': None, u'key': u'success'}),
44+ ((u'flows.2fa',), {u'rpconfig': None, u'key': u'success'}),
45+ ((u'flows.device.generic_hotp',), {u'key': u'new'})]
46+ self.assertEqual(self.mock_increment.call_args_list, args_list)
47+
48 # When the auto backup deviceflag is set...
49 @switches(TWOFACTOR_AUTO_BACKUP_DEVICE=True)
50 def test_successful_post_paper_main_no_add(self):
51@@ -553,6 +571,10 @@ class DeviceAdditionViewTestCase(DeviceViewsTestCaseBase):
52 # Does not add additional paper devices.
53 self.assertEqual(devices.filter(device_type="paper").count(), 1)
54
55+ args_list = [
56+ ((u'flows.device.paper',), {u'key': u'new'})]
57+ self.assertEqual(self.mock_increment.call_args_list, args_list)
58+
59 def test_unrecognised_type(self):
60 data = {'type': 'foobar', 'hex_key': KEY, 'name': 'Some device',
61 'otp': 'some otp key'}
62diff --git a/src/webui/views/devices.py b/src/webui/views/devices.py
63index 3f7716b..c66a4a5 100644
64--- a/src/webui/views/devices.py
65+++ b/src/webui/views/devices.py
66@@ -24,6 +24,7 @@ from identityprovider.forms import DeviceRenameForm, OATHDeviceForm
67 from identityprovider.models import AuthenticationDevice
68 from identityprovider.models import twofactor
69 from identityprovider.models.twofactor import get_otp_type
70+from identityprovider.stats import stats
71
72 from webui.decorators import require_twofactor_enabled, sso_login_required
73 from webui.views.const import (
74@@ -187,7 +188,9 @@ def _device_addition_standard(request, device_type):
75 device_types['paper'],
76 request.user)
77 backup_device = _create_device(
78- request, device_name, backup_hex_key, 0, 'paper')
79+ request, device_name, backup_hex_key, 0, 'paper',
80+ auto=True)
81+
82 # If a device was auto-added, send to a nice page.
83 # Very similar to device-print view with some tweaks
84 # for auto-generated device.
85@@ -222,20 +225,27 @@ def _device_addition_standard(request, device_type):
86
87
88 def _create_device(
89- request, device_name, hex_key, counter, device_type, oath_type=None):
90+ request, device_name, hex_key, counter, device_type, oath_type=None,
91+ auto=False):
92+
93+ full_device_type = device_type + (
94+ ':{}'.format(oath_type) if oath_type else '')
95+ stats_device_type = (
96+ "paper_auto" if device_type == 'paper' and auto else device_type) + (
97+ '_{}'.format(oath_type) if oath_type else '')
98 device = AuthenticationDevice.objects.create(
99 account=request.user,
100 name=device_name,
101 key=hex_key,
102 counter=counter,
103- device_type=device_type + (
104- ':{}'.format(oath_type) if oath_type else ''),
105+ device_type=full_device_type,
106 )
107 twofactor.login(request)
108 added_type = oath_type.upper() if oath_type else device_types[device_type]
109 messages.success(
110 request, DEVICE_ADDED.format(
111 name=device_name, type=added_type), 'temporary')
112+ stats.increment('flows.device.{}'.format(stats_device_type), key='new')
113 return device
114
115

Subscribers

People subscribed via source and target branches