Merge lp:~azzar1/software-properties/canonical-livepatch into lp:software-properties
- canonical-livepatch
- Merge into main
Proposed by
Andrea Azzarone
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 1011 | ||||
Proposed branch: | lp:~azzar1/software-properties/canonical-livepatch | ||||
Merge into: | lp:software-properties | ||||
Diff against target: |
1630 lines (+1255/-30) 10 files modified
data/gtkbuilder/dialog-livepatch-error.ui (+58/-0) data/gtkbuilder/main.ui (+90/-21) data/gtkbuilder/ubuntuone-dialog.ui (+372/-0) debian/control (+5/-1) softwareproperties/SoftwareProperties.py (+131/-0) softwareproperties/dbus/SoftwarePropertiesDBus.py (+11/-0) softwareproperties/gtk/DialogLivepatchError.py (+56/-0) softwareproperties/gtk/DialogUbuntuOne.py (+162/-0) softwareproperties/gtk/LivePatchTokenGetter.py (+192/-0) softwareproperties/gtk/SoftwarePropertiesGtk.py (+178/-8) |
||||
To merge this branch: | bzr merge lp:~azzar1/software-properties/canonical-livepatch | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Core Development Team | Pending | ||
Review via email: mp+335219@code.launchpad.net |
Commit message
Implement the GUI to enable canonical-livepatch from software-
Description of the change
To post a comment you must log in.
Revision history for this message
Andrea Azzarone (azzar1) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'data/gtkbuilder/dialog-livepatch-error.ui' |
2 | --- data/gtkbuilder/dialog-livepatch-error.ui 1970-01-01 00:00:00 +0000 |
3 | +++ data/gtkbuilder/dialog-livepatch-error.ui 2017-12-14 14:42:25 +0000 |
4 | @@ -0,0 +1,58 @@ |
5 | +<?xml version="1.0" encoding="UTF-8"?> |
6 | +<!-- Generated with glade 3.18.3 --> |
7 | +<interface> |
8 | + <requires lib="gtk+" version="3.12"/> |
9 | + <object class="GtkMessageDialog" id="messagedialog_livepatch"> |
10 | + <property name="can_focus">False</property> |
11 | + <property name="type_hint">dialog</property> |
12 | + <property name="message_type">error</property> |
13 | + <property name="text" translatable="yes">Sorry, there’s been a problem in setting up Canonical Livepatch.</property> |
14 | + <child internal-child="vbox"> |
15 | + <object class="GtkBox" id="messagedialog-vbox1"> |
16 | + <property name="can_focus">False</property> |
17 | + <property name="orientation">vertical</property> |
18 | + <property name="spacing">2</property> |
19 | + <child internal-child="action_area"> |
20 | + <object class="GtkButtonBox" id="messagedialog-action_area1"> |
21 | + <property name="can_focus">False</property> |
22 | + <property name="layout_style">end</property> |
23 | + <child> |
24 | + <object class="GtkButton" id="button_settings"> |
25 | + <property name="label" translatable="yes">Settings…</property> |
26 | + <property name="visible">True</property> |
27 | + <property name="can_focus">True</property> |
28 | + <property name="receives_default">True</property> |
29 | + <signal name="clicked" handler="on_button_settings_clicked" swapped="no"/> |
30 | + </object> |
31 | + <packing> |
32 | + <property name="expand">True</property> |
33 | + <property name="fill">True</property> |
34 | + <property name="position">0</property> |
35 | + </packing> |
36 | + </child> |
37 | + <child> |
38 | + <object class="GtkButton" id="button_ignore"> |
39 | + <property name="label" translatable="yes">Ignore</property> |
40 | + <property name="visible">True</property> |
41 | + <property name="can_focus">True</property> |
42 | + <property name="receives_default">True</property> |
43 | + <property name="yalign">0.51999998092651367</property> |
44 | + <signal name="clicked" handler="on_button_ignore_clicked" swapped="no"/> |
45 | + </object> |
46 | + <packing> |
47 | + <property name="expand">True</property> |
48 | + <property name="fill">True</property> |
49 | + <property name="position">1</property> |
50 | + </packing> |
51 | + </child> |
52 | + </object> |
53 | + <packing> |
54 | + <property name="expand">False</property> |
55 | + <property name="fill">False</property> |
56 | + <property name="position">0</property> |
57 | + </packing> |
58 | + </child> |
59 | + </object> |
60 | + </child> |
61 | + </object> |
62 | +</interface> |
63 | |
64 | === modified file 'data/gtkbuilder/main.ui' |
65 | --- data/gtkbuilder/main.ui 2016-08-17 09:34:22 +0000 |
66 | +++ data/gtkbuilder/main.ui 2017-12-14 14:42:25 +0000 |
67 | @@ -1,6 +1,7 @@ |
68 | <?xml version="1.0" encoding="UTF-8"?> |
69 | +<!-- Generated with glade 3.18.3 --> |
70 | <interface> |
71 | - <!-- interface-requires gtk+ 3.0 --> |
72 | + <requires lib="gtk+" version="3.0"/> |
73 | <object class="GtkListStore" id="model_normal_updates_display"> |
74 | <columns> |
75 | <!-- column-name text --> |
76 | @@ -160,7 +161,6 @@ |
77 | <property name="visible">True</property> |
78 | <property name="can_focus">True</property> |
79 | <property name="receives_default">False</property> |
80 | - <property name="use_action_appearance">False</property> |
81 | <property name="use_underline">True</property> |
82 | <property name="xalign">0</property> |
83 | <property name="draw_indicator">True</property> |
84 | @@ -396,7 +396,6 @@ |
85 | <property name="can_focus">True</property> |
86 | <property name="can_default">True</property> |
87 | <property name="receives_default">True</property> |
88 | - <property name="use_action_appearance">False</property> |
89 | <signal name="clicked" handler="on_add_clicked" swapped="no"/> |
90 | </object> |
91 | <packing> |
92 | @@ -413,7 +412,6 @@ |
93 | <property name="can_focus">True</property> |
94 | <property name="can_default">True</property> |
95 | <property name="receives_default">True</property> |
96 | - <property name="use_action_appearance">False</property> |
97 | <signal name="clicked" handler="on_edit_clicked" swapped="no"/> |
98 | </object> |
99 | <packing> |
100 | @@ -430,7 +428,6 @@ |
101 | <property name="can_focus">True</property> |
102 | <property name="can_default">True</property> |
103 | <property name="receives_default">True</property> |
104 | - <property name="use_action_appearance">False</property> |
105 | <property name="use_stock">True</property> |
106 | <signal name="clicked" handler="on_remove_clicked" swapped="no"/> |
107 | </object> |
108 | @@ -459,7 +456,6 @@ |
109 | <property name="visible">True</property> |
110 | <property name="can_focus">True</property> |
111 | <property name="receives_default">True</property> |
112 | - <property name="use_action_appearance">False</property> |
113 | <signal name="clicked" handler="on_button_add_cdrom_clicked" swapped="no"/> |
114 | </object> |
115 | <packing> |
116 | @@ -570,8 +566,8 @@ |
117 | <object class="GtkLabel" id="label3"> |
118 | <property name="visible">True</property> |
119 | <property name="can_focus">False</property> |
120 | - <property name="xalign">1</property> |
121 | <property name="label" translatable="yes">Automatically check for updates:</property> |
122 | + <property name="xalign">1</property> |
123 | </object> |
124 | <packing> |
125 | <property name="expand">False</property> |
126 | @@ -613,8 +609,8 @@ |
127 | <object class="GtkLabel" id="label4"> |
128 | <property name="visible">True</property> |
129 | <property name="can_focus">False</property> |
130 | - <property name="xalign">1</property> |
131 | <property name="label" translatable="yes">When there are security updates:</property> |
132 | + <property name="xalign">1</property> |
133 | </object> |
134 | <packing> |
135 | <property name="expand">False</property> |
136 | @@ -656,8 +652,8 @@ |
137 | <object class="GtkLabel" id="label5"> |
138 | <property name="visible">True</property> |
139 | <property name="can_focus">False</property> |
140 | - <property name="xalign">1</property> |
141 | <property name="label" translatable="yes">When there are other updates:</property> |
142 | + <property name="xalign">1</property> |
143 | </object> |
144 | <packing> |
145 | <property name="expand">False</property> |
146 | @@ -700,6 +696,84 @@ |
147 | </packing> |
148 | </child> |
149 | <child> |
150 | + <object class="GtkAlignment" id="alignment3"> |
151 | + <property name="visible">True</property> |
152 | + <property name="can_focus">False</property> |
153 | + <property name="left_padding">12</property> |
154 | + <child> |
155 | + <object class="GtkGrid" id="grid_livepatch"> |
156 | + <property name="visible">True</property> |
157 | + <property name="can_focus">False</property> |
158 | + <property name="halign">center</property> |
159 | + <property name="hexpand">False</property> |
160 | + <property name="vexpand">False</property> |
161 | + <property name="row_spacing">6</property> |
162 | + <property name="column_spacing">6</property> |
163 | + <child> |
164 | + <object class="GtkBox" id="hbox_livepatch"> |
165 | + <property name="visible">True</property> |
166 | + <property name="can_focus">False</property> |
167 | + <property name="halign">center</property> |
168 | + <property name="spacing">6</property> |
169 | + <child> |
170 | + <object class="GtkLabel" id="label_livepatch_login"> |
171 | + <property name="visible">True</property> |
172 | + <property name="can_focus">False</property> |
173 | + </object> |
174 | + <packing> |
175 | + <property name="expand">False</property> |
176 | + <property name="fill">True</property> |
177 | + <property name="position">0</property> |
178 | + </packing> |
179 | + </child> |
180 | + <child> |
181 | + <object class="GtkButton" id="button_ubuntuone"> |
182 | + <property name="visible">True</property> |
183 | + <property name="can_focus">True</property> |
184 | + <property name="receives_default">True</property> |
185 | + <property name="xalign">0</property> |
186 | + </object> |
187 | + <packing> |
188 | + <property name="expand">False</property> |
189 | + <property name="fill">True</property> |
190 | + <property name="position">2</property> |
191 | + </packing> |
192 | + </child> |
193 | + </object> |
194 | + <packing> |
195 | + <property name="left_attach">1</property> |
196 | + <property name="top_attach">1</property> |
197 | + </packing> |
198 | + </child> |
199 | + <child> |
200 | + <object class="GtkCheckButton" id="checkbutton_livepatch"> |
201 | + <property name="label" translatable="yes">Use Canonical Livepatch to increase security between restarts</property> |
202 | + <property name="use_action_appearance">False</property> |
203 | + <property name="visible">True</property> |
204 | + <property name="can_focus">True</property> |
205 | + <property name="receives_default">False</property> |
206 | + <property name="use_underline">True</property> |
207 | + <property name="xalign">0</property> |
208 | + <property name="draw_indicator">True</property> |
209 | + </object> |
210 | + <packing> |
211 | + <property name="left_attach">1</property> |
212 | + <property name="top_attach">0</property> |
213 | + </packing> |
214 | + </child> |
215 | + <child> |
216 | + <placeholder/> |
217 | + </child> |
218 | + </object> |
219 | + </child> |
220 | + </object> |
221 | + <packing> |
222 | + <property name="expand">True</property> |
223 | + <property name="fill">True</property> |
224 | + <property name="position">2</property> |
225 | + </packing> |
226 | + </child> |
227 | + <child> |
228 | <object class="GtkAlignment" id="alignment15"> |
229 | <property name="visible">True</property> |
230 | <property name="can_focus">False</property> |
231 | @@ -713,8 +787,8 @@ |
232 | <object class="GtkLabel" id="label29"> |
233 | <property name="visible">True</property> |
234 | <property name="can_focus">False</property> |
235 | + <property name="label" translatable="yes">Notify me of a new Ubuntu version:</property> |
236 | <property name="xalign">1</property> |
237 | - <property name="label" translatable="yes">Notify me of a new Ubuntu version:</property> |
238 | </object> |
239 | <packing> |
240 | <property name="expand">False</property> |
241 | @@ -746,7 +820,7 @@ |
242 | <packing> |
243 | <property name="expand">False</property> |
244 | <property name="fill">False</property> |
245 | - <property name="position">2</property> |
246 | + <property name="position">3</property> |
247 | </packing> |
248 | </child> |
249 | </object> |
250 | @@ -775,9 +849,9 @@ |
251 | <object class="GtkLabel" id="label27"> |
252 | <property name="visible">True</property> |
253 | <property name="can_focus">False</property> |
254 | - <property name="xalign">0</property> |
255 | <property name="label" translatable="yes"><b>Trusted software providers</b></property> |
256 | <property name="use_markup">True</property> |
257 | + <property name="xalign">0</property> |
258 | </object> |
259 | <packing> |
260 | <property name="expand">False</property> |
261 | @@ -851,7 +925,6 @@ |
262 | <property name="has_tooltip">True</property> |
263 | <property name="tooltip_markup" translatable="yes">Import the public key from a trusted software provider</property> |
264 | <property name="tooltip_text" translatable="yes">Import the public key from a trusted software provider</property> |
265 | - <property name="use_action_appearance">False</property> |
266 | <property name="use_underline">True</property> |
267 | <signal name="clicked" handler="add_key_clicked" swapped="no"/> |
268 | </object> |
269 | @@ -868,7 +941,6 @@ |
270 | <property name="visible">True</property> |
271 | <property name="can_focus">True</property> |
272 | <property name="receives_default">True</property> |
273 | - <property name="use_action_appearance">False</property> |
274 | <property name="use_stock">True</property> |
275 | <signal name="clicked" handler="remove_key_clicked" swapped="no"/> |
276 | </object> |
277 | @@ -900,7 +972,6 @@ |
278 | <property name="has_tooltip">True</property> |
279 | <property name="tooltip_markup" translatable="yes">Restore the default keys of your distribution</property> |
280 | <property name="tooltip_text" translatable="yes">Restore the default keys of your distribution</property> |
281 | - <property name="use_action_appearance">False</property> |
282 | <property name="use_underline">True</property> |
283 | <signal name="clicked" handler="on_restore_clicked" swapped="no"/> |
284 | </object> |
285 | @@ -1001,8 +1072,8 @@ |
286 | <property name="visible">True</property> |
287 | <property name="can_focus">False</property> |
288 | <property name="halign">start</property> |
289 | + <property name="label" translatable="yes">No proprietary drivers are in use.</property> |
290 | <property name="xalign">0</property> |
291 | - <property name="label" translatable="yes">No proprietary drivers are in use.</property> |
292 | </object> |
293 | <packing> |
294 | <property name="expand">True</property> |
295 | @@ -1024,11 +1095,11 @@ |
296 | <object class="GtkLabel" id="label_disc"> |
297 | <property name="visible">True</property> |
298 | <property name="can_focus">False</property> |
299 | - <property name="xalign">0</property> |
300 | <property name="label" translatable="yes"><small>A proprietary driver has private code that Ubuntu developers can't review or improve. Security and other updates are dependent on the driver vendor.</small></property> |
301 | <property name="use_markup">True</property> |
302 | <property name="wrap">True</property> |
303 | - <property name="max-width-chars">50</property> |
304 | + <property name="max_width_chars">50</property> |
305 | + <property name="xalign">0</property> |
306 | </object> |
307 | <packing> |
308 | <property name="expand">False</property> |
309 | @@ -1090,7 +1161,7 @@ |
310 | <property name="label" translatable="yes">Use proposed updates if you’re willing to report bugs on any problems that occur.</property> |
311 | <property name="use_markup">True</property> |
312 | <property name="wrap">True</property> |
313 | - <property name="max-width-chars">110</property> |
314 | + <property name="max_width_chars">110</property> |
315 | </object> |
316 | </child> |
317 | </object> |
318 | @@ -1138,7 +1209,6 @@ |
319 | <property name="can_focus">True</property> |
320 | <property name="can_default">True</property> |
321 | <property name="receives_default">True</property> |
322 | - <property name="use_action_appearance">False</property> |
323 | <property name="use_underline">True</property> |
324 | <signal name="clicked" handler="on_button_revert_clicked" swapped="no"/> |
325 | </object> |
326 | @@ -1157,7 +1227,6 @@ |
327 | <property name="can_focus">True</property> |
328 | <property name="can_default">True</property> |
329 | <property name="receives_default">True</property> |
330 | - <property name="use_action_appearance">False</property> |
331 | <property name="use_stock">True</property> |
332 | <signal name="clicked" handler="on_close_button" swapped="no"/> |
333 | </object> |
334 | |
335 | === added file 'data/gtkbuilder/ubuntu-one.png' |
336 | Binary files data/gtkbuilder/ubuntu-one.png 1970-01-01 00:00:00 +0000 and data/gtkbuilder/ubuntu-one.png 2017-12-14 14:42:25 +0000 differ |
337 | === added file 'data/gtkbuilder/ubuntuone-dialog.ui' |
338 | --- data/gtkbuilder/ubuntuone-dialog.ui 1970-01-01 00:00:00 +0000 |
339 | +++ data/gtkbuilder/ubuntuone-dialog.ui 2017-12-14 14:42:25 +0000 |
340 | @@ -0,0 +1,372 @@ |
341 | +<?xml version="1.0" encoding="UTF-8"?> |
342 | +<!-- Generated with glade 3.19.0 --> |
343 | +<interface> |
344 | + <requires lib="gtk+" version="3.0"/> |
345 | + <object class="GtkDialog" id="dialog_ubuntuone"> |
346 | + <property name="use-header-bar">1</property> |
347 | + <property name="resizable">False</property> |
348 | + <action-widgets> |
349 | + <action-widget response="cancel">cancel_button</action-widget> |
350 | + </action-widgets> |
351 | + <child internal-child="headerbar"> |
352 | + <object class="GtkHeaderBar"> |
353 | + <property name="show_close_button">False</property> |
354 | + <child> |
355 | + <object class="GtkButton" id="cancel_button"> |
356 | + <property name="label" translatable="yes">_Cancel</property> |
357 | + <property name="visible">True</property> |
358 | + <property name="can_focus">True</property> |
359 | + <property name="receives_default">True</property> |
360 | + <property name="use_underline">True</property> |
361 | + </object> |
362 | + <packing> |
363 | + <property name="pack-type">start</property> |
364 | + </packing> |
365 | + </child> |
366 | + <child> |
367 | + <object class="GtkButton" id="next_button"> |
368 | + <property name="label" translatable="yes">_Continue</property> |
369 | + <property name="visible">True</property> |
370 | + <property name="can_focus">True</property> |
371 | + <property name="can_default">True</property> |
372 | + <property name="receives_default">True</property> |
373 | + <property name="use_underline">True</property> |
374 | + <style> |
375 | + <class name="suggested-action"/> |
376 | + </style> |
377 | + </object> |
378 | + <packing> |
379 | + <property name="pack-type">end</property> |
380 | + </packing> |
381 | + </child> |
382 | + </object> |
383 | + </child> |
384 | + <child internal-child="vbox"> |
385 | + <object class="GtkBox" id="content_box"> |
386 | + <property name="visible">True</property> |
387 | + <property name="can_focus">False</property> |
388 | + <property name="margin_left">20</property> |
389 | + <property name="margin_right">20</property> |
390 | + <property name="margin_top">20</property> |
391 | + <property name="margin_bottom">20</property> |
392 | + <property name="orientation">vertical</property> |
393 | + <property name="spacing">40</property> |
394 | + <child> |
395 | + <object class="GtkBox"> |
396 | + <property name="visible">True</property> |
397 | + <property name="can_focus">False</property> |
398 | + <property name="spacing">20</property> |
399 | + <child> |
400 | + <object class="GtkImage"> |
401 | + <property name="visible">True</property> |
402 | + <property name="can_focus">False</property> |
403 | + <property name="yalign">0</property> |
404 | + <property name="file">/usr/share/software-properties/gtkbuilder/ubuntu-one.png</property> |
405 | + </object> |
406 | + <packing> |
407 | + <property name="expand">False</property> |
408 | + <property name="fill">True</property> |
409 | + <property name="position">0</property> |
410 | + </packing> |
411 | + </child> |
412 | + <child> |
413 | + <object class="GtkStack" id="page_stack"> |
414 | + <property name="visible">True</property> |
415 | + <property name="can_focus">False</property> |
416 | + <child> |
417 | + <object class="GtkGrid" id="page-login"> |
418 | + <property name="visible">True</property> |
419 | + <property name="can_focus">False</property> |
420 | + <child> |
421 | + <object class="GtkLabel" id="prompt_label"> |
422 | + <property name="visible">True</property> |
423 | + <property name="can_focus">False</property> |
424 | + <property name="margin_bottom">20</property> |
425 | + <property name="label" translatable="yes">To enable livepatch service, you need an Ubuntu Single Sign-On account.</property> |
426 | + <property name="wrap">True</property> |
427 | + <property name="xalign">0</property> |
428 | + </object> |
429 | + <packing> |
430 | + <property name="left_attach">0</property> |
431 | + <property name="top_attach">0</property> |
432 | + <property name="width">2</property> |
433 | + </packing> |
434 | + </child> |
435 | + <child> |
436 | + <object class="GtkAccelLabel"> |
437 | + <property name="visible">True</property> |
438 | + <property name="can_focus">False</property> |
439 | + <property name="halign">end</property> |
440 | + <property name="margin_right">10</property> |
441 | + <property name="margin_bottom">20</property> |
442 | + <property name="label" translatable="yes">_Email address:</property> |
443 | + <property name="use_underline">True</property> |
444 | + <property name="xalign">1</property> |
445 | + </object> |
446 | + <packing> |
447 | + <property name="left_attach">0</property> |
448 | + <property name="top_attach">1</property> |
449 | + </packing> |
450 | + </child> |
451 | + <child> |
452 | + <object class="GtkEntry" id="email_entry"> |
453 | + <property name="visible">True</property> |
454 | + <property name="can_focus">True</property> |
455 | + <property name="margin_bottom">20</property> |
456 | + <property name="hexpand">True</property> |
457 | + <property name="input_purpose">email</property> |
458 | + </object> |
459 | + <packing> |
460 | + <property name="left_attach">1</property> |
461 | + <property name="top_attach">1</property> |
462 | + </packing> |
463 | + </child> |
464 | + <child> |
465 | + <object class="GtkRadioButton" id="login_radio"> |
466 | + <property name="label" translatable="yes">I have an Ubuntu Single Sign-On account</property> |
467 | + <property name="visible">True</property> |
468 | + <property name="can_focus">True</property> |
469 | + <property name="receives_default">False</property> |
470 | + <property name="margin_bottom">5</property> |
471 | + <property name="xalign">0</property> |
472 | + <property name="active">True</property> |
473 | + <property name="draw_indicator">True</property> |
474 | + </object> |
475 | + <packing> |
476 | + <property name="left_attach">0</property> |
477 | + <property name="top_attach">2</property> |
478 | + <property name="width">2</property> |
479 | + </packing> |
480 | + </child> |
481 | + <child> |
482 | + <object class="GtkAccelLabel"> |
483 | + <property name="visible">True</property> |
484 | + <property name="can_focus">False</property> |
485 | + <property name="halign">end</property> |
486 | + <property name="margin_left">25</property> |
487 | + <property name="margin_right">10</property> |
488 | + <property name="margin_bottom">5</property> |
489 | + <property name="label" translatable="yes">_Password:</property> |
490 | + <property name="use_underline">True</property> |
491 | + <property name="xalign">1</property> |
492 | + </object> |
493 | + <packing> |
494 | + <property name="left_attach">0</property> |
495 | + <property name="top_attach">3</property> |
496 | + </packing> |
497 | + </child> |
498 | + <child> |
499 | + <object class="GtkEntry" id="password_entry"> |
500 | + <property name="visible">True</property> |
501 | + <property name="can_focus">True</property> |
502 | + <property name="margin_bottom">5</property> |
503 | + <property name="hexpand">True</property> |
504 | + <property name="visibility">False</property> |
505 | + <property name="invisible_char">•</property> |
506 | + <property name="input_purpose">password</property> |
507 | + <property name="activates_default">True</property> |
508 | + </object> |
509 | + <packing> |
510 | + <property name="left_attach">1</property> |
511 | + <property name="top_attach">3</property> |
512 | + </packing> |
513 | + </child> |
514 | + <child> |
515 | + <object class="GtkRadioButton" id="register_radio"> |
516 | + <property name="label" translatable="yes">I want to register for an account now</property> |
517 | + <property name="visible">True</property> |
518 | + <property name="can_focus">True</property> |
519 | + <property name="receives_default">False</property> |
520 | + <property name="margin_bottom">20</property> |
521 | + <property name="xalign">0</property> |
522 | + <property name="active">True</property> |
523 | + <property name="draw_indicator">True</property> |
524 | + <property name="group">login_radio</property> |
525 | + </object> |
526 | + <packing> |
527 | + <property name="left_attach">0</property> |
528 | + <property name="top_attach">5</property> |
529 | + <property name="width">2</property> |
530 | + </packing> |
531 | + </child> |
532 | + <child> |
533 | + <object class="GtkRadioButton" id="reset_radio"> |
534 | + <property name="label" translatable="yes">I've forgotten my password</property> |
535 | + <property name="visible">True</property> |
536 | + <property name="can_focus">True</property> |
537 | + <property name="receives_default">False</property> |
538 | + <property name="xalign">0</property> |
539 | + <property name="active">True</property> |
540 | + <property name="draw_indicator">True</property> |
541 | + <property name="group">login_radio</property> |
542 | + </object> |
543 | + <packing> |
544 | + <property name="left_attach">0</property> |
545 | + <property name="top_attach">6</property> |
546 | + <property name="width">2</property> |
547 | + </packing> |
548 | + </child> |
549 | + <child> |
550 | + <placeholder/> |
551 | + </child> |
552 | + </object> |
553 | + <packing> |
554 | + <property name="name">page-login</property> |
555 | + </packing> |
556 | + </child> |
557 | + <child> |
558 | + <object class="GtkGrid" id="page-otp"> |
559 | + <property name="visible">True</property> |
560 | + <property name="can_focus">False</property> |
561 | + <child> |
562 | + <object class="GtkLabel"> |
563 | + <property name="visible">True</property> |
564 | + <property name="can_focus">False</property> |
565 | + <property name="margin_bottom">20</property> |
566 | + <property name="label" translatable="yes">Enter your one-time password for two-factor authentication.</property> |
567 | + <property name="wrap">True</property> |
568 | + <property name="xalign">0</property> |
569 | + </object> |
570 | + <packing> |
571 | + <property name="left_attach">0</property> |
572 | + <property name="top_attach">0</property> |
573 | + <property name="width">2</property> |
574 | + </packing> |
575 | + </child> |
576 | + <child> |
577 | + <object class="GtkAccelLabel"> |
578 | + <property name="visible">True</property> |
579 | + <property name="can_focus">False</property> |
580 | + <property name="margin_right">10</property> |
581 | + <property name="label" translatable="yes">One-time password:</property> |
582 | + </object> |
583 | + <packing> |
584 | + <property name="left_attach">0</property> |
585 | + <property name="top_attach">1</property> |
586 | + </packing> |
587 | + </child> |
588 | + <child> |
589 | + <object class="GtkEntry" id="passcode_entry"> |
590 | + <property name="visible">True</property> |
591 | + <property name="can_focus">True</property> |
592 | + <property name="hexpand">True</property> |
593 | + <property name="input_purpose">pin</property> |
594 | + <property name="activates_default">True</property> |
595 | + </object> |
596 | + <packing> |
597 | + <property name="left_attach">1</property> |
598 | + <property name="top_attach">1</property> |
599 | + </packing> |
600 | + </child> |
601 | + </object> |
602 | + <packing> |
603 | + <property name="name">page-otp</property> |
604 | + <property name="position">1</property> |
605 | + </packing> |
606 | + </child> |
607 | + <child> |
608 | + <object class="GtkGrid" id="page-success"> |
609 | + <property name="visible">True</property> |
610 | + <property name="can_focus">False</property> |
611 | + <child> |
612 | + <object class="GtkLabel"> |
613 | + <property name="visible">True</property> |
614 | + <property name="can_focus">False</property> |
615 | + <property name="label" translatable="yes">You are now signed into Ubuntu One.</property> |
616 | + </object> |
617 | + <packing> |
618 | + <property name="left_attach">0</property> |
619 | + <property name="top_attach">0</property> |
620 | + </packing> |
621 | + </child> |
622 | + </object> |
623 | + <packing> |
624 | + <property name="name">page-success</property> |
625 | + <property name="position">2</property> |
626 | + </packing> |
627 | + </child> |
628 | + </object> |
629 | + <packing> |
630 | + <property name="expand">True</property> |
631 | + <property name="fill">True</property> |
632 | + <property name="position">1</property> |
633 | + </packing> |
634 | + </child> |
635 | + </object> |
636 | + <packing> |
637 | + <property name="expand">True</property> |
638 | + <property name="fill">True</property> |
639 | + <property name="position">0</property> |
640 | + </packing> |
641 | + </child> |
642 | + <child> |
643 | + <object class="GtkBox"> |
644 | + <property name="visible">True</property> |
645 | + <property name="can_focus">False</property> |
646 | + <property name="spacing">10</property> |
647 | + <child> |
648 | + <object class="GtkBox"> |
649 | + <property name="visible">True</property> |
650 | + <property name="can_focus">False</property> |
651 | + <child> |
652 | + <object class="GtkStack" id="status_stack"> |
653 | + <property name="visible">True</property> |
654 | + <property name="can_focus">False</property> |
655 | + <property name="margin_right">5</property> |
656 | + <child> |
657 | + <object class="GtkImage" id="status_image"> |
658 | + <property name="visible">True</property> |
659 | + <property name="can_focus">False</property> |
660 | + </object> |
661 | + <packing> |
662 | + <property name="name">status-image</property> |
663 | + </packing> |
664 | + </child> |
665 | + <child> |
666 | + <object class="GtkSpinner" id="status_spinner"> |
667 | + <property name="visible">True</property> |
668 | + <property name="can_focus">False</property> |
669 | + <property name="active">True</property> |
670 | + </object> |
671 | + <packing> |
672 | + <property name="name">status-spinner</property> |
673 | + <property name="position">1</property> |
674 | + </packing> |
675 | + </child> |
676 | + </object> |
677 | + <packing> |
678 | + <property name="expand">False</property> |
679 | + <property name="fill">True</property> |
680 | + <property name="position">0</property> |
681 | + </packing> |
682 | + </child> |
683 | + <child> |
684 | + <object class="GtkLabel" id="status_label"> |
685 | + <property name="visible">True</property> |
686 | + <property name="can_focus">False</property> |
687 | + <property name="xalign">0</property> |
688 | + </object> |
689 | + <packing> |
690 | + <property name="expand">True</property> |
691 | + <property name="fill">True</property> |
692 | + <property name="position">1</property> |
693 | + </packing> |
694 | + </child> |
695 | + </object> |
696 | + <packing> |
697 | + <property name="expand">True</property> |
698 | + <property name="fill">True</property> |
699 | + <property name="position">0</property> |
700 | + </packing> |
701 | + </child> |
702 | + </object> |
703 | + <packing> |
704 | + <property name="expand">False</property> |
705 | + <property name="fill">True</property> |
706 | + <property name="position">1</property> |
707 | + </packing> |
708 | + </child> |
709 | + </object> |
710 | + </child> |
711 | + </object> |
712 | +</interface> |
713 | |
714 | === modified file 'debian/control' |
715 | --- debian/control 2017-04-10 21:33:09 +0000 |
716 | +++ debian/control 2017-12-14 14:42:25 +0000 |
717 | @@ -41,7 +41,7 @@ |
718 | Package: software-properties-common |
719 | Architecture: all |
720 | Depends: ${python3:Depends}, ${misc:Depends}, python3, |
721 | - python3-gi, gir1.2-glib-2.0, python-apt-common (>= 0.9), python3-dbus, |
722 | + python3-gi, gir1.2-glib-2.0, gir1.2-snapd-1, python-apt-common (>= 0.9), python3-dbus, |
723 | python3-software-properties (= ${binary:Version}), ca-certificates |
724 | Breaks: python-software-properties (<< 0.85), python3-software-properties (<< 0.85) |
725 | Replaces: python-software-properties (<< 0.85), python3-software-properties (<< 0.85) |
726 | @@ -59,7 +59,11 @@ |
727 | python3-software-properties (= ${binary:Version}), |
728 | python3-gi, |
729 | gir1.2-gtk-3.0, |
730 | + gir1.2-secret-1, |
731 | python3-aptdaemon.gtk3widgets, |
732 | + python3-macaroonbakery, |
733 | + python3-pymacaroons, |
734 | + python3-requests, |
735 | software-properties-common, |
736 | ubuntu-drivers-common (>= 1:0.2.75), |
737 | python3-gi, |
738 | |
739 | === modified file 'softwareproperties/SoftwareProperties.py' |
740 | --- softwareproperties/SoftwareProperties.py 2017-03-01 17:35:37 +0000 |
741 | +++ softwareproperties/SoftwareProperties.py 2017-12-14 14:42:25 +0000 |
742 | @@ -31,6 +31,7 @@ |
743 | import os |
744 | import glob |
745 | import shutil |
746 | +import subprocess |
747 | import threading |
748 | import atexit |
749 | import tempfile |
750 | @@ -63,6 +64,10 @@ |
751 | from . import ppa |
752 | from . import cloudarchive |
753 | |
754 | +import gi |
755 | +gi.require_version('Snapd', '1') |
756 | +from gi.repository import Gio, Snapd |
757 | + |
758 | _SHORTCUT_FACTORIES = [ |
759 | ppa.shortcut_handler, |
760 | cloudarchive.shortcut_handler, |
761 | @@ -126,6 +131,8 @@ |
762 | # apt-key stuff |
763 | self.apt_key = AptAuth(rootdir=rootdir) |
764 | |
765 | + self.cancellable = Gio.Cancellable() |
766 | + |
767 | atexit.register(self.wait_for_threads) |
768 | |
769 | def wait_for_threads(self): |
770 | @@ -858,6 +865,130 @@ |
771 | except: |
772 | return False |
773 | |
774 | + # |
775 | + # Livepatch |
776 | + # |
777 | + def init_snapd(self): |
778 | + self.snapd_client = Snapd.Client() |
779 | + if not self.snapd_client.connect_sync(): |
780 | + self.snapd_client = None |
781 | + return self.snapd_client is not None |
782 | + |
783 | + def get_livepatch_snap_async(self, callback): |
784 | + assert self.snapd_client |
785 | + self.snapd_client.list_one_async('canonical-livepatch', |
786 | + self.cancellable, |
787 | + self.on_list_one_ready_cb, |
788 | + callback) |
789 | + |
790 | + def on_list_one_ready_cb(self, source_object, result, user_data): |
791 | + callback = user_data |
792 | + try: |
793 | + snap = source_object.list_one_finish(result) |
794 | + except: |
795 | + snap = None |
796 | + if snap: |
797 | + if callback: callback(snap) |
798 | + return |
799 | + else: |
800 | + assert self.snapd_client |
801 | + self.snapd_client.find_async(Snapd.FindFlags.MATCH_NAME, |
802 | + 'canonical-livepatch', |
803 | + self.cancellable, |
804 | + self.on_find_ready_cb, |
805 | + callback) |
806 | + |
807 | + def on_find_ready_cb(self, source_object, result, user_data): |
808 | + callback = user_data |
809 | + try: |
810 | + snaps = source_object.find_finish(result) |
811 | + except: |
812 | + snaps = list() |
813 | + snap = snaps[0] if len(snaps) else None |
814 | + if callback: callback(snap) |
815 | + |
816 | + def get_livepatch_snap_status(self, snap): |
817 | + if snap is None: |
818 | + return Snapd.SnapStatus.UNKNOWN |
819 | + return snap.get_status() |
820 | + |
821 | + # glib-snapd does not keep track of the status of the snap. Use this decorator |
822 | + # to make it easy to write async functions that will always have an updated |
823 | + # snap object. |
824 | + def require_livepatch_snap(func): |
825 | + def get_livepatch_snap_and_call(*args, **kwargs): |
826 | + return args[0].get_livepatch_snap_async(lambda snap: func(snap=snap, *args, **kwargs)) |
827 | + return get_livepatch_snap_and_call |
828 | + |
829 | + def is_livepatch_enabled(self): |
830 | + file = Gio.File.new_for_path(path='/var/snap/canonical-livepatch/common/machine-token') |
831 | + return file.query_exists(None) |
832 | + |
833 | + @require_livepatch_snap |
834 | + def set_livepatch_enabled_async(self, enabled, token, callback, snap=None): |
835 | + status = self.get_livepatch_snap_status(snap) |
836 | + if status == Snapd.SnapStatus.UNKNOWN: |
837 | + if callback: callback(True, _("canonical-livepatch cannot be installed")) |
838 | + elif status == Snapd.SnapStatus.ACTIVE: |
839 | + if enabled: |
840 | + error = self.enable_livepatch_service(token) |
841 | + else: |
842 | + error = self.disable_livepatch_service() |
843 | + if callback: callback(len(error) > 0, error) |
844 | + elif status == Snapd.SnapStatus.INSTALLED: |
845 | + if enabled: |
846 | + self.snapd_client.enable_async(name='canonical-livepatch', |
847 | + cancellable=self.cancellable, |
848 | + callback=self.livepatch_enable_snap_cb, |
849 | + user_data=(callback, token)) |
850 | + else: |
851 | + if callback: callback(False, "") |
852 | + elif status == Snapd.SnapStatus.AVAILABLE: |
853 | + if enabled: |
854 | + self.snapd_client.install_async(name='canonical-livepatch', |
855 | + cancellable=self.cancellable, |
856 | + callback=self.livepatch_install_snap_cb, |
857 | + user_data=(callback, token)) |
858 | + else: |
859 | + if callback: callback(False, "") |
860 | + |
861 | + def livepatch_enable_snap_cb(self, source_object, result, user_data): |
862 | + (callback, token) = user_data |
863 | + try: |
864 | + if source_object.enable_finish(result): |
865 | + error = self.enable_livepatch_service(token) |
866 | + if callback: callback(len(error) > 0, error) |
867 | + except Exception: |
868 | + if callback: callback(True, _("canonical-livepatch cannot be installed")) |
869 | + |
870 | + def livepatch_install_snap_cb(self, source_object, result, user_data): |
871 | + (callback, token) = user_data |
872 | + try: |
873 | + if source_object.install_finish(result): |
874 | + error = self.enable_livepatch_service(token) |
875 | + if callback: callback(len(error) > 0, error) |
876 | + except Exception: |
877 | + if callback: callback(True, _("canonical-livepatch cannot be installed")) |
878 | + |
879 | + def enable_livepatch_service(self, token): |
880 | + if self.is_livepatch_enabled(): |
881 | + return "" |
882 | + |
883 | + try: |
884 | + subprocess.check_output(['/snap/bin/canonical-livepatch', 'enable', token], stderr=subprocess.STDOUT) |
885 | + return "" |
886 | + except subprocess.CalledProcessError as e: |
887 | + return e.output if e.output else "" |
888 | + |
889 | + def disable_livepatch_service(self): |
890 | + if not self.is_livepatch_enabled(): |
891 | + return "" |
892 | + |
893 | + try: |
894 | + subprocess.check_output(['/snap/bin/canonical-livepatch', 'disable'], stderr=subprocess.STDOUT) |
895 | + return "" |
896 | + except subprocess.CalledProcessError as e: |
897 | + return e.output if e.output else "" |
898 | |
899 | def shortcut_handler(shortcut): |
900 | for factory in _SHORTCUT_FACTORIES: |
901 | |
902 | === modified file 'softwareproperties/dbus/SoftwarePropertiesDBus.py' |
903 | --- softwareproperties/dbus/SoftwarePropertiesDBus.py 2015-11-01 12:39:02 +0000 |
904 | +++ softwareproperties/dbus/SoftwarePropertiesDBus.py 2017-12-14 14:42:25 +0000 |
905 | @@ -61,6 +61,8 @@ |
906 | self.enforce_polkit = True |
907 | logging.debug("waiting for connections") |
908 | |
909 | + self.init_snapd() |
910 | + |
911 | # override set_modified_sourceslist to emit a signal |
912 | def save_sourceslist(self): |
913 | super(SoftwarePropertiesDBus, self).save_sourceslist() |
914 | @@ -317,6 +319,15 @@ |
915 | sender, conn, "com.ubuntu.softwareproperties.applychanges") |
916 | return self.update_keys() |
917 | |
918 | + # LivePatch |
919 | + @dbus.service.method(DBUS_INTERFACE_NAME, |
920 | + sender_keyword="sender", connection_keyword="conn", |
921 | + in_signature='bs', out_signature='bs', async_callbacks=('reply_handler', 'error_handler')) |
922 | + def SetLivepatchEnabled(self, enabled, token, reply_handler, error_handler, sender=None, conn=None): |
923 | + self._check_policykit_privilege( |
924 | + sender, conn, "com.ubuntu.softwareproperties.applychanges") |
925 | + self.set_livepatch_enabled_async(enabled, token, reply_handler) |
926 | + |
927 | # helper from jockey |
928 | def _check_policykit_privilege(self, sender, conn, privilege): |
929 | '''Verify that sender has a given PolicyKit privilege. |
930 | |
931 | === added file 'softwareproperties/gtk/DialogLivepatchError.py' |
932 | --- softwareproperties/gtk/DialogLivepatchError.py 1970-01-01 00:00:00 +0000 |
933 | +++ softwareproperties/gtk/DialogLivepatchError.py 2017-12-14 14:42:25 +0000 |
934 | @@ -0,0 +1,56 @@ |
935 | +# |
936 | +# Copyright (c) 2017 Canonical |
937 | +# |
938 | +# Authors: |
939 | +# Andrea Azzarone <andrea.azzarone@canonical.com> |
940 | +# |
941 | +# This program is free software; you can redistribute it and/or |
942 | +# modify it under the terms of the GNU General Public License as |
943 | +# published by the Free Software Foundation; either version 2 of the |
944 | +# License, or (at your option) any later version. |
945 | +# |
946 | +# This program is distributed in the hope that it will be useful, |
947 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
948 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
949 | +# GNU General Public License for more details. |
950 | +# |
951 | +# You should have received a copy of the GNU General Public License |
952 | +# along with this program; if not, write to the Free Software |
953 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
954 | +# USA |
955 | + |
956 | +import os |
957 | + |
958 | +from softwareproperties.gtk.utils import ( |
959 | + setup_ui, |
960 | +) |
961 | + |
962 | + |
963 | +class DialogLivepatchError: |
964 | + |
965 | + RESPONSE_SETTINGS = 100 |
966 | + RESPONSE_IGNORE = 101 |
967 | + |
968 | + def __init__(self, parent, datadir): |
969 | + """setup up the gtk dialog""" |
970 | + self.parent = parent |
971 | + |
972 | + setup_ui(self, os.path.join(datadir, "gtkbuilder", |
973 | + "dialog-livepatch-error.ui"), domain="software-properties") |
974 | + |
975 | + self.dialog = self.messagedialog_livepatch |
976 | + self.dialog.use_header_bar = True |
977 | + self.dialog.set_transient_for(parent) |
978 | + |
979 | + def run(self, error): |
980 | + self.dialog.format_secondary_markup( |
981 | + "The error was: \"%s\"" % error.strip()) |
982 | + res = self.dialog.run() |
983 | + self.dialog.hide() |
984 | + return res |
985 | + |
986 | + def on_button_settings_clicked(self, b, d=None): |
987 | + self.dialog.response(self.RESPONSE_SETTINGS) |
988 | + |
989 | + def on_button_ignore_clicked(self, b, d=None): |
990 | + self.dialog.response(self.RESPONSE_IGNORE) |
991 | |
992 | === added file 'softwareproperties/gtk/DialogUbuntuOne.py' |
993 | --- softwareproperties/gtk/DialogUbuntuOne.py 1970-01-01 00:00:00 +0000 |
994 | +++ softwareproperties/gtk/DialogUbuntuOne.py 2017-12-14 14:42:25 +0000 |
995 | @@ -0,0 +1,162 @@ |
996 | +# dialog_cache_outdated.py - inform the user to update the apt cache |
997 | +# |
998 | +# Copyright (c) 2017 Canonical |
999 | +# |
1000 | +# Authors: Andrea Azzarone <andrea.azzarone@canonical.com> |
1001 | +# |
1002 | +# This program is free software; you can redistribute it and/or |
1003 | +# modify it under the terms of the GNU General Public License as |
1004 | +# published by the Free Software Foundation; either version 2 of the |
1005 | +# License, or (at your option) any later version. |
1006 | +# |
1007 | +# This program is distributed in the hope that it will be useful, |
1008 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1009 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1010 | +# GNU General Public License for more details. |
1011 | +# |
1012 | +# You should have received a copy of the GNU General Public License |
1013 | +# along with this program; if not, write to the Free Software |
1014 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
1015 | +# USA |
1016 | + |
1017 | +import os |
1018 | +import gi |
1019 | +gi.require_version("Gtk", "3.0") |
1020 | +from gi.repository import Gio, Gtk |
1021 | +from gettext import gettext as _ |
1022 | +import re |
1023 | + |
1024 | +from softwareproperties.gtk.utils import ( |
1025 | + setup_ui, |
1026 | +) |
1027 | + |
1028 | +class DialogUbuntuOne: |
1029 | + |
1030 | + def __init__(self, parent, datadir): |
1031 | + """setup up the gtk dialog""" |
1032 | + self.parent = parent |
1033 | + |
1034 | + setup_ui(self, os.path.join(datadir, "gtkbuilder", "ubuntuone-dialog.ui"), domain="software-properties") |
1035 | + |
1036 | + self.dialog = self.dialog_ubuntuone |
1037 | + self.dialog.use_header_bar = True |
1038 | + self.dialog.set_transient_for(parent) |
1039 | + self.dialog.set_default(self.next_button) |
1040 | + |
1041 | + self.next_button.connect('clicked', self.__next_button_clicked_cb) |
1042 | + self.email_entry.connect('notify::text', self.__entry_edited_cb) |
1043 | + self.password_entry.connect('notify::text', self.__entry_edited_cb) |
1044 | + self.passcode_entry.connect('notify::text', self.__entry_edited_cb) |
1045 | + self.login_radio.connect('toggled', self.__radio_button_toggled_cb) |
1046 | + self.register_radio.connect('toggled', self.__radio_button_toggled_cb) |
1047 | + self.reset_radio.connect('toggled', self.__radio_button_toggled_cb) |
1048 | + |
1049 | + self.__update_widgets() |
1050 | + |
1051 | + def run(self, user_interaction_cb): |
1052 | + self.user_interaction_cb = user_interaction_cb |
1053 | + self.focus_default_entry() |
1054 | + res = self.dialog.run() |
1055 | + self.dialog.hide() |
1056 | + return res |
1057 | + |
1058 | + def get_lp_username(self): |
1059 | + return self.email_entry.get_text() |
1060 | + |
1061 | + def get_lp_password(self): |
1062 | + return self.password_entry.get_text() |
1063 | + |
1064 | + def get_lp_passcode(self): |
1065 | + passcode = self.passcode_entry.get_text() |
1066 | + return passcode if passcode is not None and len(passcode) > 0 else None |
1067 | + |
1068 | + def show_status(self, text, is_error): |
1069 | + self.status_stack.set_visible(True) |
1070 | + if is_error: |
1071 | + self.status_stack.set_visible_child_name('status-image') |
1072 | + self.status_image.set_from_icon_name('gtk-dialog-error', Gtk.IconSize.BUTTON) |
1073 | + else: |
1074 | + self.status_stack.set_visible_child_name('status-spinner') |
1075 | + self.status_label.set_text(text) |
1076 | + self.__update_widgets() |
1077 | + |
1078 | + def focus_default_entry(self): |
1079 | + if self.page_stack.get_visible_child_name() == 'page-login': |
1080 | + self.email_entry.grab_focus() |
1081 | + elif self.page_stack.get_visible_child_name() == 'page-otp': |
1082 | + self.passcode_entry.grab_focus() |
1083 | + |
1084 | + def reset_status(self): |
1085 | + self.status_stack.set_visible(False) |
1086 | + self.status_label.set_text("") |
1087 | + |
1088 | + def ask_otp(self): |
1089 | + self.page_stack.set_visible_child_name('page-otp') |
1090 | + self.reset_status() |
1091 | + self.__update_widgets() |
1092 | + |
1093 | + def show_success(self): |
1094 | + self.page_stack.set_visible_child_name('page-success') |
1095 | + self.reset_status() |
1096 | + self.__update_widgets() |
1097 | + |
1098 | + # Widget logic |
1099 | + def __is_email_address(self, text): |
1100 | + return re.match(r'[^@]+@[^@]+\.[^@]+', text) is not None |
1101 | + |
1102 | + def __update_widgets(self): |
1103 | + if self.page_stack.get_visible_child_name() == 'page-login': |
1104 | + self.email_entry.set_sensitive(True) |
1105 | + self.cancel_button.set_sensitive(True) |
1106 | + self.next_button.set_sensitive( |
1107 | + not self.login_radio.get_active() or |
1108 | + (self.__is_email_address(self.email_entry.get_text()) and |
1109 | + self.password_entry.get_text_length() > 0)) |
1110 | + self.password_entry.set_sensitive(self.login_radio.get_active()) |
1111 | + elif self.page_stack.get_visible_child_name() == 'page-otp': |
1112 | + self.passcode_entry.set_sensitive(True) |
1113 | + self.cancel_button.set_sensitive(True) |
1114 | + self.next_button.set_sensitive(self.passcode_entry.get_text_length()) |
1115 | + elif self.page_stack.get_visible_child_name() == 'page-success': |
1116 | + self.cancel_button.set_visible(False) |
1117 | + self.next_button.set_label(_('Continue')) |
1118 | + self.next_button.set_sensitive(True) |
1119 | + |
1120 | + # Widgets callbacks |
1121 | + def __send_login_request(self): |
1122 | + self.cancel_button.set_sensitive(False) |
1123 | + self.next_button.set_sensitive(False) |
1124 | + self.login_radio.set_sensitive(False) |
1125 | + self.register_radio.set_sensitive(False) |
1126 | + self.reset_radio.set_sensitive(False) |
1127 | + self.email_entry.set_sensitive(False) |
1128 | + self.passcode_entry.set_sensitive(False) |
1129 | + self.passcode_entry.set_sensitive(False) |
1130 | + |
1131 | + self.show_status(_("Signing in…"), False) |
1132 | + |
1133 | + data = { |
1134 | + 'email': self.get_lp_username(), |
1135 | + 'password': self.get_lp_password(), |
1136 | + 'otp': self.get_lp_passcode() |
1137 | + } |
1138 | + self.user_interaction_cb(self, data) |
1139 | + |
1140 | + def __radio_button_toggled_cb(self, radio_button): |
1141 | + self.__update_widgets() |
1142 | + |
1143 | + def __entry_edited_cb(self, entry, text): |
1144 | + self.__update_widgets() |
1145 | + |
1146 | + def __next_button_clicked_cb(self, button): |
1147 | + if self.page_stack.get_visible_child_name() == 'page-login': |
1148 | + if self.login_radio.get_active(): |
1149 | + self.__send_login_request() |
1150 | + elif self.register_radio.get_active(): |
1151 | + Gio.AppInfo.launch_default_for_uri('https://login.ubuntu.com/+new_account') |
1152 | + elif self.reset_radio.get_active(): |
1153 | + Gio.AppInfo.launch_default_for_uri('https://login.ubuntu.com/+forgot_password') |
1154 | + elif self.page_stack.get_visible_child_name() == 'page-otp': |
1155 | + self.__send_login_request() |
1156 | + elif self.page_stack.get_visible_child_name() == 'page-success': |
1157 | + self.dialog.response(Gtk.ResponseType.OK) |
1158 | |
1159 | === added file 'softwareproperties/gtk/LivePatchTokenGetter.py' |
1160 | --- softwareproperties/gtk/LivePatchTokenGetter.py 1970-01-01 00:00:00 +0000 |
1161 | +++ softwareproperties/gtk/LivePatchTokenGetter.py 2017-12-14 14:42:25 +0000 |
1162 | @@ -0,0 +1,192 @@ |
1163 | +# Copyright (c) 2017 Canonical Ltd. |
1164 | +# |
1165 | +# Author: Andrea Azzarone <andrea.azzarone@canonical.com> |
1166 | +# |
1167 | +# This program is free software; you can redistribute it and/or |
1168 | +# modify it under the terms of the GNU General Public License as |
1169 | +# published by the Free Software Foundation; either version 2 of the |
1170 | +# License, or (at your option) any later version. |
1171 | +# |
1172 | +# This program is distributed in the hope that it will be useful, |
1173 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1174 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1175 | +# GNU General Public License for more details. |
1176 | +# |
1177 | +# You should have received a copy of the GNU General Public License |
1178 | +# along with this program; if not, write to the Free Software |
1179 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
1180 | +# USA |
1181 | + |
1182 | +from functools import partial |
1183 | +import json |
1184 | +import logging |
1185 | +from urllib.parse import urlencode |
1186 | +import threading |
1187 | + |
1188 | +import requests |
1189 | +import macaroonbakery |
1190 | +import pymacaroons |
1191 | +from macaroonbakery import httpbakery |
1192 | + |
1193 | +from .DialogUbuntuOne import DialogUbuntuOne |
1194 | + |
1195 | +import gi |
1196 | +gi.require_version("Gtk", "3.0") |
1197 | +from gi.repository import Gtk, GLib |
1198 | +from gettext import gettext as _ |
1199 | + |
1200 | + |
1201 | +LIVEPATCH_AUTH_ROOT_URL = 'https://auth.livepatch.canonical.com' |
1202 | +UBUNTU_SSO_ROOT_URL = 'https://login.ubuntu.com' |
1203 | + |
1204 | + |
1205 | +class AuthenticationCancelled(Exception): |
1206 | + def __init__(self): |
1207 | + super(AuthenticationCancelled, self).__init__('authentication cancelled by the user') |
1208 | + |
1209 | + |
1210 | +class AuthenticationFailed(Exception): |
1211 | + def __init__(self, msg): |
1212 | + super(AuthenticationFailed, self).__init__('authentication failed: {}'.format(msg)) |
1213 | + |
1214 | + |
1215 | + |
1216 | +class LivePatchTokenGetter(object): |
1217 | + |
1218 | + def __init__(self, window_main, datadir): |
1219 | + cookies = requests.cookies.RequestsCookieJar() |
1220 | + self.session = requests.Session() |
1221 | + |
1222 | + self.usso_macaroon_interactor = USSOMacaroonInteractor(self.session, window_main, datadir) |
1223 | + client = httpbakery.Client(interaction_methods=[self.usso_macaroon_interactor], cookies=cookies) |
1224 | + |
1225 | + self.session.auth = client.auth() |
1226 | + self.session.cookies = cookies |
1227 | + |
1228 | + def get_token(self): |
1229 | + url = '{}/api/v1/tokens?{}'.format(LIVEPATCH_AUTH_ROOT_URL, urlencode({'token_type': 'user'})) |
1230 | + response = self.session.get(url, headers={'User-Agent': 'software-properties'}) |
1231 | + if response.ok: |
1232 | + data = response.json() |
1233 | + return self.usso_macaroon_interactor.lp_username, data['token'] |
1234 | + elif response.status_code == 403: |
1235 | + return self.usso_macaroon_interactor.lp_username, None |
1236 | + else: |
1237 | + raise AuthenticationFailed('received {} response from server'.format(response.status_code)) |
1238 | + |
1239 | + |
1240 | +class USSOMacaroonInteractor(httpbakery.LegacyInteractor): |
1241 | + |
1242 | + def __init__(self, session, window_main, datadir): |
1243 | + self.session = requests.Session() |
1244 | + self.window_main = window_main |
1245 | + self.datadir = datadir |
1246 | + |
1247 | + def kind(self): |
1248 | + return "interactive" |
1249 | + |
1250 | + def legacy_interact(self, client, location, visit_url): |
1251 | + usso_url = self.get_usso_macaroon_url(visit_url) |
1252 | + usso_macaroon = self.get_usso_macaroon(usso_url) |
1253 | + self.discharge_usso_macaroon(usso_macaroon, partial(self.complete_usso_macaroon_discharge, usso_url)) |
1254 | + |
1255 | + def get_usso_macaroon_url(self, url): |
1256 | + # find interaction methods for discharge |
1257 | + response = self.session.get(url, headers={'Accept': 'application/json'}) |
1258 | + if not response.ok: |
1259 | + raise AuthenticationFailed('can not find interaction methods') |
1260 | + |
1261 | + data = response.json() |
1262 | + # expect usso_macaroon interaction method |
1263 | + if 'usso_macaroon' not in data: |
1264 | + raise AuthenticationFailed('missing usso_macaroon interaction method') |
1265 | + return data['usso_macaroon'] |
1266 | + |
1267 | + def get_usso_macaroon(self, url): |
1268 | + response = self.session.get(url) |
1269 | + if not response.ok: |
1270 | + raise AuthenticationFailed('can not get usso macaroon') |
1271 | + return response.json()['macaroon'] |
1272 | + |
1273 | + def discharge_usso_macaroon(self, macaroon, macaroon_discharged_cb): |
1274 | + usso_caveats = [ |
1275 | + cav['cid'] for cav in macaroon.get('caveats', []) |
1276 | + if cav.get('cl') == UBUNTU_SSO_ROOT_URL] |
1277 | + if not usso_caveats: |
1278 | + raise AuthenticationFailed('no valid usso caveat found') |
1279 | + |
1280 | + data = {'caveat_id': usso_caveats[0]} |
1281 | + |
1282 | + def discharge_macaroon_response_cb(dialog, response): |
1283 | + show_generic_error = False |
1284 | + if not response.ok: |
1285 | + if response.status_code == 401 or response.status_code == 403: |
1286 | + code = response.json().get('code') |
1287 | + if code == 'TWOFACTOR_REQUIRED': |
1288 | + dialog.ask_otp() |
1289 | + elif code == 'INVALID_CREDENTIALS': |
1290 | + dialog.show_status(_('Incorrect email or password'), True) |
1291 | + elif code == 'ACCOUNT_SUSPENDED': |
1292 | + dialog.show_status(_('Account suspended'), True) |
1293 | + elif code == 'ACCOUNT_DEACTIVATED': |
1294 | + dialog.show_status(_('Account deactivated'), True) |
1295 | + elif code == 'EMAIL_INVALIDATED': |
1296 | + dialog.show_status(_('Email invalidated'), True) |
1297 | + elif code == 'TWOFACTOR_FAILURE': |
1298 | + dialog.show_status(_('Two-factor authentication failed'), True) |
1299 | + elif code == 'PASSWORD_POLICY_ERROR': |
1300 | + dialog.show_status(_('Password reset required'), True) |
1301 | + elif code == 'TOO-MANY_REQUESTS': |
1302 | + dialog.show_status(_('Too many requests'), True) |
1303 | + else: |
1304 | + show_generic_error = True |
1305 | + else: |
1306 | + show_generic_error = True |
1307 | + |
1308 | + if show_generic_error: |
1309 | + dialog.show_status(_("Authentication issue"), True) |
1310 | + |
1311 | + dialog.focus_default_entry() |
1312 | + else: |
1313 | + self.lp_username = dialog.get_lp_username() |
1314 | + dialog.show_success() |
1315 | + |
1316 | + root_m = macaroonbakery.Macaroon.deserialize_json(json.dumps(macaroon)).macaroon |
1317 | + discharge_m = pymacaroons.Macaroon.deserialize(response.json()['discharge_macaroon']) |
1318 | + bound_discharge = root_m.prepare_for_request(discharge_m) |
1319 | + macaroon_discharged_cb([root_m.serialize(), bound_discharge.serialize()]) |
1320 | + |
1321 | + def user_interaction_cb(dialog, new_data): |
1322 | + data.update(new_data) |
1323 | + self.discharge_macaroon_async(data, dialog, discharge_macaroon_response_cb) |
1324 | + |
1325 | + dialog = DialogUbuntuOne(self.window_main, self.datadir) |
1326 | + dialog_response = dialog.run(user_interaction_cb) |
1327 | + |
1328 | + if dialog_response != Gtk.ResponseType.OK: |
1329 | + raise AuthenticationCancelled() |
1330 | + |
1331 | + def discharge_macaroon_async(self, data, dialog, callback): |
1332 | + def target_function(): |
1333 | + try: |
1334 | + response = self.session.post( |
1335 | + '{}/api/v2/tokens/discharge'.format(UBUNTU_SSO_ROOT_URL), |
1336 | + data=json.dumps(data), |
1337 | + headers={'Content-Type': 'application/json'}) |
1338 | + except Exception as e: |
1339 | + logging.error(e) |
1340 | + response = requests.models.Response() |
1341 | + response.status_code = 500 |
1342 | + # Call callback in main thread |
1343 | + GLib.timeout_add(0, lambda data: callback(dialog, response), None) |
1344 | + |
1345 | + t = threading.Thread(target=target_function) |
1346 | + t.start() |
1347 | + |
1348 | + def complete_usso_macaroon_discharge(self, url, discharges): |
1349 | + data = {'macaroons': discharges} |
1350 | + response = self.session.post( |
1351 | + url, data=json.dumps(data), |
1352 | + headers={'Content-Type': 'application/json'}) |
1353 | + if not response.ok: |
1354 | + raise AuthenticationFailed('can not complete usso macaroon discharge') |
1355 | |
1356 | === modified file 'softwareproperties/gtk/SoftwarePropertiesGtk.py' |
1357 | --- softwareproperties/gtk/SoftwarePropertiesGtk.py 2017-04-10 21:33:20 +0000 |
1358 | +++ softwareproperties/gtk/SoftwarePropertiesGtk.py 2017-12-14 14:42:25 +0000 |
1359 | @@ -40,7 +40,8 @@ |
1360 | import gi |
1361 | gi.require_version("Gdk", "3.0") |
1362 | gi.require_version("Gtk", "3.0") |
1363 | -from gi.repository import GObject, Gdk, Gtk, Gio, GLib |
1364 | +gi.require_version('Secret', '1') |
1365 | +from gi.repository import GObject, Gdk, Gtk, Gio, GLib, Secret |
1366 | |
1367 | from .SimpleGtkbuilderApp import SimpleGtkbuilderApp |
1368 | from .DialogAdd import DialogAdd |
1369 | @@ -48,6 +49,8 @@ |
1370 | from .DialogEdit import DialogEdit |
1371 | from .DialogCacheOutdated import DialogCacheOutdated |
1372 | from .DialogAddSourcesList import DialogAddSourcesList |
1373 | +from .DialogLivepatchError import DialogLivepatchError |
1374 | +from .LivePatchTokenGetter import LivePatchTokenGetter, AuthenticationCancelled |
1375 | |
1376 | import softwareproperties |
1377 | import softwareproperties.distro |
1378 | @@ -78,6 +81,11 @@ |
1379 | STORE_VISIBLE |
1380 | ) = list(range(5)) |
1381 | |
1382 | +SECRETS_SCHEMA = Secret.Schema.new('com.ubuntu.SotwareProperties', |
1383 | + Secret.SchemaFlags.NONE, |
1384 | + {'key': Secret.SchemaAttributeType.STRING}) |
1385 | + |
1386 | +INFINIT_DBUS_TIMEOUT = 0x7fffffff/1000 |
1387 | |
1388 | def error(parent_window, summary, msg): |
1389 | """ show a error dialog """ |
1390 | @@ -182,6 +190,8 @@ |
1391 | self.show_distro() |
1392 | # Setup and show the Additonal Drivers tab |
1393 | self.init_drivers() |
1394 | + # Setup and show the LivePatch tab |
1395 | + self.init_livepatch() |
1396 | |
1397 | # Connect to switch-page before setting initial tab. Otherwise the |
1398 | # first switch goes unnoticed. |
1399 | @@ -283,7 +293,7 @@ |
1400 | |
1401 | def set_security_update_level(self): |
1402 | """Fetch the security level, Enable/Disable and set the value appropriately""" |
1403 | - |
1404 | + |
1405 | # Security Updates |
1406 | level_sec = self.get_update_automation_level() |
1407 | if level_sec == None: |
1408 | @@ -298,7 +308,7 @@ |
1409 | self.combobox_security_updates.set_active(1) # Download automatically |
1410 | elif level_sec == softwareproperties.UPDATE_INST_SEC: |
1411 | self.combobox_security_updates.set_active(2) # Download and install automatically |
1412 | - |
1413 | + |
1414 | |
1415 | def init_distro(self): |
1416 | """Setup the user interface elements to represent the distro""" |
1417 | @@ -493,7 +503,7 @@ |
1418 | except dbus.DBusException as e: |
1419 | if e._dbus_error_name == 'com.ubuntu.SoftwareProperties.PermissionDeniedByPolicy': |
1420 | logging.error("Authentication canceled, changes have not been saved") |
1421 | - |
1422 | + |
1423 | combo_handler = self.handlers[self.combobox_security_updates] |
1424 | self.combobox_security_updates.handler_block(combo_handler) |
1425 | self.set_security_update_level() |
1426 | @@ -520,13 +530,13 @@ |
1427 | except dbus.DBusException as e: |
1428 | if e._dbus_error_name == 'com.ubuntu.SoftwareProperties.PermissionDeniedByPolicy': |
1429 | logging.error("Authentication canceled, changes have not been saved") |
1430 | - |
1431 | + |
1432 | combo_handler = self.handlers[self.combobox_release_upgrades] |
1433 | self.combobox_release_upgrades.handler_block(combo_handler) |
1434 | i = self.get_release_upgrades_policy() |
1435 | self.combobox_release_upgrades.set_active(i) |
1436 | self.combobox_release_upgrades.handler_unblock(combo_handler) |
1437 | - |
1438 | + |
1439 | |
1440 | def on_combobox_server_changed(self, combobox): |
1441 | """ |
1442 | @@ -880,7 +890,7 @@ |
1443 | except dbus.DBusException as e: |
1444 | if e._dbus_error_name == 'com.ubuntu.SoftwareProperties.PermissionDeniedByPolicy': |
1445 | logging.error("Authentication canceled, changes have not been saved") |
1446 | - |
1447 | + |
1448 | update_days = self.get_update_interval() |
1449 | combo_handler = self.handlers[self.combobox_update_interval] |
1450 | for key in self.combobox_interval_mapping: |
1451 | @@ -1012,6 +1022,7 @@ |
1452 | def on_delete_event(self, widget, args): |
1453 | """Close the window if requested""" |
1454 | self.on_close_button(widget) |
1455 | + return self.quit_when_livepatch_responds |
1456 | |
1457 | def on_close_button(self, widget): |
1458 | """Show a dialog that a reload of the channel information is required |
1459 | @@ -1021,7 +1032,11 @@ |
1460 | d = DialogCacheOutdated(self.window_main, |
1461 | self.datadir) |
1462 | d.run() |
1463 | - self.quit() |
1464 | + if self.waiting_livepatch_response: |
1465 | + self.quit_when_livepatch_responds = True |
1466 | + self.hide() |
1467 | + else: |
1468 | + self.quit() |
1469 | |
1470 | def on_button_add_cdrom_clicked(self, widget): |
1471 | """ when a cdrom is requested for adding """ |
1472 | @@ -1429,3 +1444,158 @@ |
1473 | % {'count': self.nonfree_drivers}) |
1474 | else: |
1475 | self.label_driver_action.set_label(_("No proprietary drivers are in use.")) |
1476 | + |
1477 | + # |
1478 | + # Livepatch |
1479 | + # |
1480 | + def init_livepatch(self): |
1481 | + self.waiting_livepatch_response = False |
1482 | + self.quit_when_livepatch_responds = False |
1483 | + |
1484 | + if not self.is_livepatch_supported(): |
1485 | + self.grid_livepatch.set_visible(False) |
1486 | + return |
1487 | + |
1488 | + lp_username, lp_token = self.get_livepatch_credentials() |
1489 | + self.set_livepatch_credentials(lp_username, lp_token, store=False) |
1490 | + self.checkbutton_livepatch.set_active(self.is_livepatch_enabled()) |
1491 | + |
1492 | + file = Gio.File.new_for_path(path='/var/snap/canonical-livepatch/common/machine-token') |
1493 | + self.lp_monitor = file.monitor_file(Gio.FileMonitorFlags.NONE) |
1494 | + |
1495 | + self.handlers[self.checkbutton_livepatch] = \ |
1496 | + self.checkbutton_livepatch.connect("toggled", self.on_checkbutton_livepatch_toggled) |
1497 | + self.handlers[self.button_ubuntuone] = \ |
1498 | + self.button_ubuntuone.connect('clicked', self.on_button_ubuntuone_clicked) |
1499 | + self.handlers[self.lp_monitor] = \ |
1500 | + self.lp_monitor.connect('changed', self.on_livepatch_status_changed) |
1501 | + |
1502 | + def is_livepatch_supported(self): |
1503 | + supported_releases = ['trusty', 'xenial'] |
1504 | + return any([self.distro.is_codename(d) for d in supported_releases]) |
1505 | + |
1506 | + def get_livepatch_credentials(self): |
1507 | + lp_username = Secret.password_lookup_sync(SECRETS_SCHEMA, {'key': 'livepatch-username'}, None) |
1508 | + lp_token = Secret.password_lookup_sync(SECRETS_SCHEMA, {'key': 'livepatch-token'}, None) |
1509 | + return lp_username, lp_token |
1510 | + |
1511 | + def on_livepatch_status_changed(self, file_monitor, file, other_file, event_type): |
1512 | + if not self.waiting_livepatch_response: |
1513 | + self.checkbutton_livepatch.set_active(self.is_livepatch_enabled()) |
1514 | + |
1515 | + def set_livepatch_credentials(self, lp_username, lp_token, store): |
1516 | + self.lp_username = lp_username |
1517 | + self.lp_token = lp_token |
1518 | + |
1519 | + if self.lp_username: |
1520 | + self.button_ubuntuone.set_label(_('Sign Out')) |
1521 | + |
1522 | + if self.lp_token: |
1523 | + self.checkbutton_livepatch.set_sensitive(True) |
1524 | + self.label_livepatch_login.set_label(_('Signed in as %s' % self.lp_username)) |
1525 | + else: |
1526 | + self.checkbutton_livepatch.set_sensitive(False) |
1527 | + text = _('%s isn\'t authorized to use Livepatch.' % self.lp_username) |
1528 | + text = "<span color='red'>" + text + "</span>" |
1529 | + self.label_livepatch_login.set_markup(text) |
1530 | + else: |
1531 | + self.lp_token = None |
1532 | + self.checkbutton_livepatch.set_sensitive(False) |
1533 | + self.button_ubuntuone.set_label(_('Sign In…')) |
1534 | + self.label_livepatch_login.set_label(_('To use Livepatch you need to sign in.')) |
1535 | + |
1536 | + |
1537 | + if store: |
1538 | + if self.lp_username: |
1539 | + Secret.password_store(SECRETS_SCHEMA, {'key': 'livepatch-username'}, None, 'com.ubuntu.SoftwareProperties', self.lp_username) |
1540 | + else: |
1541 | + Secret.password_clear(SECRETS_SCHEMA, {'key': 'livepatch-username'}, None, None, None) |
1542 | + |
1543 | + if self.lp_token: |
1544 | + Secret.password_store(SECRETS_SCHEMA, {'key': 'livepatch-token'}, None, 'com.ubuntu.SoftwareProperties', self.lp_token) |
1545 | + else: |
1546 | + Secret.password_clear(SECRETS_SCHEMA, {'key': 'livepatch-token'}, None, None, None) |
1547 | + |
1548 | + def on_button_ubuntuone_clicked(self, button): |
1549 | + if self.is_user_logged(): |
1550 | + self.do_logout() |
1551 | + else: |
1552 | + self.do_login() |
1553 | + |
1554 | + def is_user_logged(self): |
1555 | + return self.lp_username is not None |
1556 | + |
1557 | + def do_login(self): |
1558 | + try: |
1559 | + lp_token_getter = LivePatchTokenGetter(self.window_main, self.datadir) |
1560 | + lp_username, lp_token = lp_token_getter.get_token() |
1561 | + except AuthenticationCancelled: |
1562 | + pass |
1563 | + except Exception as e: |
1564 | + logging.error(e) |
1565 | + error(self.window_main, |
1566 | + _("Error enabling Canonical Livepatch"), |
1567 | + _("Please check your Internet connection.")) |
1568 | + else: |
1569 | + self.set_livepatch_credentials(lp_username, lp_token, store=True) |
1570 | + if lp_username and lp_token: |
1571 | + self.checkbutton_livepatch.set_active(True) |
1572 | + |
1573 | + def do_logout(self): |
1574 | + self.set_livepatch_credentials(None, None, store=True) |
1575 | + self.checkbutton_livepatch.set_active(False) |
1576 | + |
1577 | + def on_checkbutton_livepatch_toggled(self, checkbutton): |
1578 | + if self.waiting_livepatch_response: |
1579 | + return |
1580 | + |
1581 | + self.waiting_livepatch_response = True |
1582 | + |
1583 | + if self.checkbutton_livepatch.get_active(): |
1584 | + self.backend.SetLivepatchEnabled(True, self.lp_token if self.lp_token else '', |
1585 | + reply_handler=self.livepatch_enabled_reply_handler, |
1586 | + error_handler=self.livepatch_enabled_error_handler, |
1587 | + timeout=INFINIT_DBUS_TIMEOUT) |
1588 | + else: |
1589 | + self.backend.SetLivepatchEnabled(False, '', |
1590 | + reply_handler=self.livepatch_enabled_reply_handler, |
1591 | + error_handler=self.livepatch_enabled_error_handler, |
1592 | + timeout=INFINIT_DBUS_TIMEOUT) |
1593 | + |
1594 | + def livepatch_enabled_reply_handler(self, is_error, prompt): |
1595 | + self.sync_checkbutton_livepatch(is_error, prompt) |
1596 | + |
1597 | + def livepatch_enabled_error_handler(self, e): |
1598 | + if e._dbus_error_name == 'com.ubuntu.SoftwareProperties.PermissionDeniedByPolicy': |
1599 | + logging.error("Authentication canceled, changes have not been saved") |
1600 | + self.sync_checkbutton_livepatch(is_error=True, prompt=None) |
1601 | + else: |
1602 | + self.sync_checkbutton_livepatch(is_error=True, prompt=str(e)) |
1603 | + |
1604 | + def sync_checkbutton_livepatch(self, is_error, prompt): |
1605 | + if is_error: |
1606 | + self.waiting_livepatch_response = False |
1607 | + self.checkbutton_livepatch.handler_block(self.handlers[self.checkbutton_livepatch]) |
1608 | + self.checkbutton_livepatch.set_active(self.is_livepatch_enabled()) |
1609 | + self.checkbutton_livepatch.handler_unblock(self.handlers[self.checkbutton_livepatch]) |
1610 | + |
1611 | + if prompt: |
1612 | + dialog = DialogLivepatchError(self.window_main, self.datadir) |
1613 | + response = dialog.run(prompt) |
1614 | + if response == DialogLivepatchError.RESPONSE_SETTINGS: |
1615 | + self.window_main.show() |
1616 | + self.quit_when_livepatch_responds = False |
1617 | + else: |
1618 | + if self.is_livepatch_enabled() and not self.checkbutton_livepatch.get_active(): |
1619 | + self.backend.SetLivepatchEnabled(False, '', |
1620 | + reply_handler=self.livepatch_enabled_reply_handler, |
1621 | + error_handler=self.livepatch_enabled_error_handler) |
1622 | + elif not self.is_livepatch_enabled() and self.checkbutton_livepatch.get_active(): |
1623 | + self.backend.SetLivepatchEnabled(True, self.lp_token if self.lp_token else '', |
1624 | + reply_handler=self.livepatch_enabled_reply_handler, |
1625 | + error_handler=self.livepatch_enabled_error_handler) |
1626 | + else: |
1627 | + self.waiting_livepatch_response = False |
1628 | + |
1629 | + if self.quit_when_livepatch_responds: |
1630 | + self.on_close_button(self.button_close) |
Marking as "Working in progress" until we have figured out how to avoid the double sign-in (Ubuntu Store and the one in software- properties)